package sim.lib.others;

import java.awt.*;

import sim.*;
import sim.engine.*;
import sim.lib.EditDelay;
import sim.lib.wires.Junction;

public class FullAdder extends WrapperPainted implements EngineModule
{
/* ==================================================================
	Creation Part
	================================================================= */
	private static Image ICON = GuiFileLink.getImage("sim/lib/others/FullAdderIcon.gif");
	
	public Image getIcon()
	{
		return FullAdder.ICON;
	}
	
	public Wrapper createWrapper()
	{
		return this.getCopy();
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		FullAdder result = this.getCopy();
		result.setGridLocation(gridPosition);
		return result;
	}
	
	public String getBubbleHelp()
	{
		return "Full Adder";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	public FullAdder()
	{
		super();
	}
	
	private FullAdder getCopy()
	{
		FullAdder result = new FullAdder();
		
		result.changeDelay(this.delay);
		return result;
	}
	
	public void initializeGridSize()
	{
		this.setGridSize(5, 4);
	}
	
	public void paint(Graphics g)
	{
		// draw if visible
		if(this.isVisible())
		{
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;
			
			g.setColor(this.brush);
			
			
			int offset =  3 * gridGap;
			g.drawRect(gridGap, 2 * increment, offset, offset);
			
			int start = 4 * gridGap;
			int end = 5 * gridGap;
			offset =  2 * gridGap;
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));
			
			g.drawLine(start, gridGap, end, gridGap);
			g.drawString("x", 3 * gridGap + increment, gridGap + increment);
			
			g.drawLine(start, offset, end, offset);
			g.drawString("y", 3 * gridGap + increment, offset + increment);
			offset = offset + gridGap;
			g.drawLine(start, offset, end, offset);
			g.drawString("c", 3 * gridGap + increment, offset + increment);
			
			g.drawLine(0, offset, gridGap, offset);
			g.drawString("C", gridGap + increment, offset + increment);
			g.drawLine(0, gridGap, gridGap, gridGap);
			g.drawString("S", gridGap + increment, gridGap + increment);
		}
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	private Junction inputC = null;
	private Junction inputX = null;
	private Junction inputY = null;
	private Junction outputS = null;
	private Junction outputC = null;
	
	public boolean canDrop()
	{
		boolean result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 1, 1);
		result = result && Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 3, 1);
		result = result && Wrapper.canDropJuncion(this.gridLocation.x + 5, this.gridLocation.y + 1, 1);
		result = result && Wrapper.canDropJuncion(this.gridLocation.x + 5, this.gridLocation.y + 2, 1);
		result = result && Wrapper.canDropJuncion(this.gridLocation.x + 5, this.gridLocation.y + 3, 1);
		
		return result;
	}
		
	public void droped()
	{
		this.outputS = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1, 1);
		this.outputC = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 3, 1);
		this.inputX = Wrapper.setPinAt(this.gridLocation.x + 5, this.gridLocation.y + 1, 1);
		this.inputY = Wrapper.setPinAt(this.gridLocation.x + 5, this.gridLocation.y + 2, 1);
		this.inputC = Wrapper.setPinAt(this.gridLocation.x + 5, this.gridLocation.y + 3, 1);
		
		this.changeColor(Color.black);
	}
	
	public void selected()
	{
		this.inputC.removePin();
		this.inputX.removePin();
		this.inputY.removePin();
		this.outputC.removePin();
		this.outputS.removePin();
		
		this.changeColor(Color.green);
	}
	
	public void checkAfterSelected()
	{
		Wrapper.checkPin(this.inputC);
		Wrapper.checkPin(this.inputX);
		Wrapper.checkPin(this.inputY);
		Wrapper.checkPin(this.outputC);
		Wrapper.checkPin(this.outputS);
	}
	
/* ==================================================================
	Simulation part
	================================================================= */
	protected double delay = 1;
	
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer)
	{
		double time = this.delay + currentTime;
		
		if(currentInputs[0].isUndefined() || currentInputs[1].isUndefined() || currentInputs[2].isUndefined())
		{
			peer.setOutputPinUndefined(0, time);
			peer.setOutputPinUndefined(1, time);
		}
		else
		{
			int sum = 0;
			
			if(currentInputs[0].getValue())
				sum++;
			
			if(currentInputs[1].getValue())
				sum++;
			
			if(currentInputs[2].getValue())
				sum++;
			
			if(sum == 0)
			{
				peer.setOutputPinValue(0, false, time);
				peer.setOutputPinValue(1, false, time);
			}
			else if(sum == 1)
			{
				peer.setOutputPinValue(0, true, time);
				peer.setOutputPinValue(1, false, time);
			}
			else if(sum == 2)
			{
				peer.setOutputPinValue(0, false, time);
				peer.setOutputPinValue(1, true, time);
			}
			else if(sum == 3)
			{
				peer.setOutputPinValue(0, true, time);
				peer.setOutputPinValue(1, true, time);
			}
		}
	}
	
	public void createEnginePeer(EnginePeerList epl)
	{
		EnginePeer ep = new EnginePeer(3, 2, this);
		
		ep.setInputPin(0, this.inputX.getNodes().getItemAt(0));
		ep.setInputPin(1, this.inputY.getNodes().getItemAt(0));
		ep.setInputPin(2, this.inputC.getNodes().getItemAt(0));
		
		ep.setOutputPin(0, this.outputS.getNodes().getItemAt(0));
		ep.setOutputPin(1, this.outputC.getNodes().getItemAt(0));
		
		epl.insertItem(ep);
	}
	
	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()
	{
		return (Double.toString(this.delay) + Wrapper.SEPARATOR);
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.delay = Double.valueOf(specificParameters[0]).doubleValue();
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 1;
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new EditDelay(this.delay));
	}
		
	public void respondToChanges(Component property)
	{
		this.delay = ((EditDelay)property).getDelay();
	}
}