package sim.lib.memory;

import java.awt.*;

import sim.*;
import sim.engine.*;

import sim.lib.wires.Junction;

public abstract class RegisterStructure extends WrapperPainted implements EngineModule
{
/* ==================================================================
	Creation Part
	================================================================= */
	public Wrapper createWrapper()
	{
		return this.getCopy();
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		Wrapper result = this.getCopy();
		result.setGridLocation(gridPosition);
		return result;
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	protected String value;
	protected int[] store;
	protected String name;
	protected int busSize;
	protected int valueLenght;
	
	public RegisterStructure()
	{
		super();
		this.setBusSize(8);
	}
	
	protected abstract Wrapper getCopy();
	
	public void initializeGridSize()
	{
		this.setGridSize(10, 7);
	}
	
	public void setBusSize(int size)
	{
		this.busSize = size;
		int requiredSize, loop;
		
		String max = Integer.toHexString((int)Math.pow(2, this.busSize) - 1).toUpperCase();
		FontMetrics fm = this.getFontMetrics(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * Grid.SIZE / 4));
		
		requiredSize = fm.stringWidth(max) / Grid.SIZE + 2;
		
		if(requiredSize > 5)
			this.setGridSize(requiredSize + 5, 7);
		else
			this.setGridSize(10, 7);
		
		this.valueLenght = max.length();
		
		this.value = "0";
		for(loop = 0; this.value.length() < this.valueLenght; loop++)
			this.value = "0" + this.value;
		
		this.store = new int[this.busSize];
		for(loop = 0; loop < this.busSize; loop++)
			this.store[loop] = 0;
	}
	
	public int getBusSize()
	{
		return this.busSize;
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	protected Junction input = null;
	protected Junction output = null;
	protected Junction clock = null;
	
/* ==================================================================
	Simulation part
	================================================================= */
	protected double delay = 1;
	protected boolean pastClock;
	protected double clkGoesHigh;
	
	public void reset()
	{
	}
	
	public Wrapper getParentWrapper()
	{
		return this;
	}

	public double getDelay()
	{
		return this.delay;
	}
	
	public void changeDelay(double newValue)
	{
		this.delay = newValue;
	}
	
/* ==================================================================
	Storage Part
	================================================================= */
	public String getSpecificParameters()
	{
		int loop;
		String result = Double.toString(this.delay) + Wrapper.SEPARATOR + this.busSize + Wrapper.SEPARATOR + this.name + Wrapper.SEPARATOR;
		
		for(loop = 0; loop < this.busSize; loop++)
			result = result + this.store[this.busSize - 1 - loop];
		
		result = result + Wrapper.SEPARATOR + this.value + Wrapper.SEPARATOR;
		
		return result;
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		int loop;
		
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.delay = Double.valueOf(specificParameters[0]).doubleValue();
				this.setBusSize(Integer.valueOf(specificParameters[1]).intValue());
				this.name = specificParameters[2];
				
				for(loop = 0; loop < this.busSize; loop++)
					this.store[this.busSize - 1 - loop] = Integer.valueOf(specificParameters[3].substring(loop, loop + 1)).intValue();
				
				this.value = specificParameters[4];
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 5;
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	private int oldBusSize;
	protected String oldValue = null;
	protected int[] oldStore;
	
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new RegisterProperties(this.busSize, this.delay, this.value, this.name));
	}
		
	public void respondToChanges(Component property)
	{
		RegisterProperties rp = (RegisterProperties)property;
		
		this.delay = rp.getDelay();
		
		if(this.oldValue == null)
		{
			this.oldBusSize = this.busSize;
			this.oldValue = this.value;
			this.oldStore = this.store;
		}
		
		CentralPanel.ACTIVE_GRID.eraseComponent(this);
		this.setBusSize(rp.getBusSize());
		this.value = rp.getContent().toUpperCase();
		this.name = rp.getRegisterName();
		CentralPanel.ACTIVE_GRID.paintComponent(this);
		
		int loop, index, base;
		String binary;
		
		try
		{
			for(loop = 0; loop < this.valueLenght; loop++)
			{
				base = 4 * loop;
				
				if(this.value.charAt(this.valueLenght - 1 - loop) == '-')
				{
					for(index = base; (index - base < 4) && (index < this.busSize); index++)
						this.store[index] = 2;
				}
				else
				{	
					binary = Integer.toBinaryString(Integer.valueOf(this.value.substring(this.valueLenght - 1 - loop, this.valueLenght - loop), 16).intValue());
					
					for(index = binary.length(); index < 4; index++)
						binary = "0" + binary;
					
					for(index = 3; (index >= 0) && (base + 3 - index < this.busSize); index--)
					{
						if(binary.charAt(index) == '1')
							this.store[base + 3 - index] = 1;
						else
							this.store[base + 3 - index] = 0;
					}
				}
			}
		}
		catch(NumberFormatException e)
		{
		}
	}
	
	public void restoreOriginalProperties()
	{
		if(this.oldValue != null)
		{
			this.setBusSize(this.oldBusSize);
			this.value = this.oldValue;
			this.store = this.oldStore;
			this.oldValue = null;
			this.oldStore = null;
		}
	}
}