package sim.lib.memory;

import java.awt.*;

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

import sim.lib.wires.Junction;

public class Register extends RegisterStructure
{
/* ==================================================================
	Creation Part
	================================================================= */
	private static Image ICON = GuiFileLink.getImage("sim/lib/memory/RegisterIcon.gif");
	
	public Image getIcon()
	{
		return Register.ICON;
	}
	
	public String getBubbleHelp()
	{
		return "Register";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	public Register()
	{
		super();
		this.name = "Register";
	}
	
	protected Wrapper getCopy()
	{
		Register result = new Register();
		
		result.changeDelay(this.delay);
		result.setBusSize(this.busSize);
		
		return result;
	}
	
	public void paint(Graphics g)
	{
		// draw if visible
		if(this.isVisible())
		{
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;
			
			int width = gridGap * this.gridSize.width;
			int temp = 3 * gridGap;
			
			g.setColor(this.brush);
			
			g.drawRect(gridGap, gridGap + increment, width - 2 * gridGap, 17 * increment);
			
			g.drawLine(width - gridGap, temp, width, temp);
			
			temp = gridGap * (this.gridSize.width / 2) - 1;
			
			g.fillRect(temp, 0, 3, gridGap + increment);
			g.fillRect(temp, 22 * increment, 3, 6 * increment);
			
			g.drawRect(10 * increment, 4 * gridGap, width - 5 * gridGap, 6 * increment);
			
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));
			
			FontMetrics fm = g.getFontMetrics(g.getFont());
			
			g.drawString("ld", width - 5 * increment - fm.stringWidth("ld"), 3 * gridGap + increment);
			
			g.clearRect(10 * increment + 1, 4 * gridGap + 1, width - 5 * gridGap - 2, 6 * increment - 2);
			
			g.drawString(this.value, (width - fm.stringWidth(this.value)) / 2, 5 * gridGap);
			
			Shape old = g.getClip();
			g.setClip(2 * gridGap, gridGap, width - 4 * gridGap, 2 * gridGap);
			g.setFont(new Font(Wrapper.FONT_NAME, Font.BOLD, 3 * increment));
			fm = g.getFontMetrics(g.getFont());
			g.drawString(this.name, (width - fm.stringWidth(this.name)) / 2, 10 * increment);
			g.setClip(old);
		}
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	public boolean canDrop()
	{
		boolean result = Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width, this.gridLocation.y + 3, 1);
		result = result && Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y, this.busSize);
		result = result && Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y + 7, this.busSize);
		
		return result;
	}
		
	public void droped()
	{
		this.clock = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width, this.gridLocation.y + 3, 1);
		this.input = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y, this.busSize);
		this.output = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y + 7, this.busSize);
		
		this.changeColor(Color.black);
		this.oldValue = null;
		this.oldStore = null;
	}
	
	public void selected()
	{
		this.input.removePin();
		this.output.removePin();
		this.clock.removePin();
		
		this.changeColor(Color.green);
	}
	
	public void checkAfterSelected()
	{
		Wrapper.checkPin(this.input);
		Wrapper.checkPin(this.output);
		Wrapper.checkPin(this.clock);
	}
	
/* ==================================================================
	Simulation part
	================================================================= */
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer)
	{
		double time = this.delay + currentTime;
		boolean foundUndefined = false;
		int loop, index, hex = 0, base = 1;
		
		// check clock
		if(!currentInputs[this.busSize].isUndefined())
		{
			if((!this.pastClock) && currentInputs[this.busSize].getValue())
				this.clkGoesHigh = currentTime;
			
			this.pastClock = currentInputs[this.busSize].getValue();
		}
		else
			this.pastClock = true;
		
		// drive content and display hex value
		if(this.clkGoesHigh == currentTime)
		{	
			for(loop = 0; loop < (this.busSize / 4 + 1); loop++)
			{
				for(index = loop * 4; (index < 4 * (loop + 1)) && (index < this.busSize); index++)
				{
					if(currentInputs[index].isUndefined())
					{
						foundUndefined = true;
						peer.setOutputPinUndefined(index, time);
						this.store[index] = 2;
					}
					else if(currentInputs[index].getValue())
					{
						hex = hex + base;
						peer.setOutputPinValue(index, true, time);
						this.store[index] = 1;
					}
					else
					{
						peer.setOutputPinValue(index, false, time);
						this.store[index] = 0;
					}
					
					base = 2 * base;
				}
			
				if(loop == 0)
				{
					if(foundUndefined)
						this.value = "-";
					else
						this.value = Integer.toHexString(hex);
				}
				else if(foundUndefined)
					this.value = "-" + this.value;
				else if(this.value.length() < this.valueLenght)
					this.value = Integer.toHexString(hex) + this.value;
				
				hex = 0;
				base = 1;
				foundUndefined = false;
			}
			
			this.value = this.value.toUpperCase();
			CentralPanel.ACTIVE_GRID.paintComponent(this);
		}
	}
	
	public void createEnginePeer(EnginePeerList epl)
	{
		this.pastClock = false;
		this.clkGoesHigh = -1;
		EnginePeer ep = new EnginePeer(this.busSize + 1, this.busSize, this);
		
		for(int loop = 0; loop < this.busSize; loop++)
		{
			ep.setInputPin(loop, this.input.getNodes().getItemAt(loop));
			ep.setOutputPin(loop, this.output.getNodes().getItemAt(loop));
		}
		
		ep.setInputPin(this.busSize, this.clock.getNodes().getItemAt(0));
		
		for(int loop = 0; loop < this.busSize; loop++)
		{
			if(this.store[loop] == 2)
				ep.setOutputPinUndefined(loop, 0);
			else
				ep.setOutputPinValue(loop, this.store[loop] == 1, 0);
		}
		
		epl.insertItem(ep);
	}
}