package sim.lib.outputs;

import java.awt.*;
import java.io.*;

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

public class Pin extends RotatableFlippableWrapperPainted implements EngineModule
{
/* ==================================================================
	Creation Part
	================================================================= */
	private static Image ICON = GuiFileLink.getImage("sim/lib/outputs/pin.gif");
	
	public Image getIcon()
	{
		return Pin.ICON;
	}
	
	public Wrapper createWrapper()
	{
		return this.getCopy();
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		Pin result = this.getCopy();
		result.setGridLocation(gridPosition);
		return result;
	}
	
	public String getBubbleHelp()
	{
		return "Pin";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	private String value = null;
	private int busSize = 8;
	private int valueLenght;
	private int pinNumber;
	private String label;
	public static int numPins = 1;
	public static int nextNum = 1;
	public static boolean deleted = false;
	
	public Pin()
	{
		super();
		this.setBusSize(8);
		//this.setLabel("PIN"+nextNum);
		//this.setLabel("");
		this.pinNumber = nextNum;

	}
	
	public Pin getCopy()
	{
		Pin result = new Pin();
		result.setBusSize(this.busSize);
		result.setLabel("PIN"+nextNum);
		result.setNumber(nextNum);
		
		if (deleted){
			deleted = false;
		}else{
			nextNum = numPins;
		}
		
		return result;
	}
	
	public void setLabel(String lbl){
		this.label = lbl;
	}
	
	public void initializeGridSize()
	{
		this.setGridSize(4, 4);
	}
	
	public String getLabel(){
		return this.label;
	}
	
	public int getNumber(){
		return this.pinNumber;
	}
	
	public void setNumber(int n){
		this.pinNumber = n;
	}
	
	public void setBusSize(int size)
	{
		this.busSize = size;
		
		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));
		
		//this.setGridSize(fm.stringWidth(max) / Grid.SIZE + 4, 4);
		this.valueLenght = max.length();
	}
	
	public int getBusSize()
	{
		return this.busSize;
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	private Junction input = null;
	
	public void selected()
	{
		this.input.removePin();
		nextNum = this.pinNumber;
		deleted = true;
		numPins--;
		this.changeColor(Color.green);
	}
	
	public void checkAfterSelected()
	{
		Wrapper.checkPin(this.input);
	}
	
/* ==================================================================
	Simulation part
	================================================================= */
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer)
	{
		boolean foundUndefined = false;
		int hex = 0;
		int base = 1;
		int loop, index;
		
		for(loop = 0; loop < (this.busSize / 4 + 1); loop++)
		{
			for(index = loop * 4; (index < 4 * (loop + 1)) && (index < this.busSize) &&(!foundUndefined); index++)
			{
				if(currentInputs[index].isUndefined())
					foundUndefined = true;
				else if(currentInputs[index].getValue())
					hex = hex + base;
				
				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)
	{
		EnginePeer ep = new EnginePeer(this.busSize, this);
		
		for(int index = 0; index < this.busSize; index++)
			ep.setInputPin(index, this.input.getNodes().getItemAt(index));
		
		epl.insertItem(ep);
	}
	
	public NodeList getInputPins(){
		return this.input.getNodes();
	}
	
	public void reset()
	{
		this.value = null;
		CentralPanel.ACTIVE_GRID.paintComponent(this);
	}
	
	public Wrapper getParentWrapper()
	{
		return this;
	}
	
/* ==================================================================
	Storage Part
	================================================================= */
	public String getSpecificParameters()
	{
		return (Integer.toString(this.busSize) + Wrapper.SEPARATOR + this.getNumber() + Wrapper.SEPARATOR + this.getLabel() + Wrapper.SEPARATOR);
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.setBusSize(Integer.valueOf(specificParameters[0]).intValue());
				this.setNumber(Integer.valueOf(specificParameters[1]).intValue());
				this.setLabel(specificParameters[2]);
				//this.flow = Boolean.valueOf(specificParameters[0]).booleanValue();
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 3;
	}
	
/* ==================================================================
	Rotation abd Flipping Part
	================================================================= */
	
	protected void paintNormal_0(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int width = this.gridSize.width * gridGap;
		int offsetX, offsetY;
		
		int buswidth = 3;
		if (this.busSize < 2){
			buswidth = 1;
		}
		
		g.setColor(WrapperPainted.BACKGROUND);
		
		offsetY = 3 * gridGap / 2;
		offsetX = 2 * gridGap;
		
		//g.setColor(Color.white);
		//g.fillArc(gridGap, offsetY, 5*gridGap/4, 5*gridGap/4, 0, 360);
		g.setColor(this.brush);
		g.drawArc(gridGap, offsetY, gridGap, gridGap, 0, 360);
		g.fillRect(gridGap*2, offsetX - 1, gridGap, buswidth);
		
		g.setFont(new Font(Wrapper.FONT_NAME,Font.PLAIN,3 * gridGap / 5));
		g.drawString(this.label,gridGap,gridGap);
		
	}
	
	protected void paintNormal_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		//this.gridSize.width = this.gridSize.width - gridGap;
		int width = this.gridSize.width * gridGap;
		int offsetX, offsetY;
		
		int buswidth = 3;
		if (this.busSize < 2){
			buswidth = 1;
		}
		
		g.setColor(WrapperPainted.BACKGROUND);
		
		offsetY = 3 * gridGap / 2;
		offsetX = 2 * gridGap;
		
		g.setColor(this.brush);
		g.drawArc(3*gridGap/2, gridGap*2, gridGap, gridGap, 0, 360);
		g.fillRect(gridGap*2-1, gridGap, buswidth, gridGap);
		
		g.setFont(new Font(Wrapper.FONT_NAME,Font.PLAIN,3 * gridGap / 5));
		g.drawString(this.label,6*gridGap/4,15*gridGap/4);
		

	}
	
	protected void paintNormal_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int width = this.gridSize.width * gridGap;
		int offsetX, offsetY;
		
		int buswidth = 3;
		if (this.busSize < 2){
			buswidth = 1;
		}
		
		g.setColor(WrapperPainted.BACKGROUND);
		
		offsetY = 3 * gridGap / 2;
		offsetX = 2 * gridGap;
		
		g.setColor(this.brush);
		g.drawArc(gridGap*2, offsetY, gridGap, gridGap, 0, 360);
		g.fillRect(gridGap, offsetX - 1, gridGap, buswidth);
		
		g.setFont(new Font(Wrapper.FONT_NAME,Font.PLAIN,3 * gridGap / 5));
		g.drawString(this.label,2*gridGap,gridGap);
	}
	
	protected void paintNormal_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int width = this.gridSize.width * gridGap;
		int offsetX, offsetY;
		
		int buswidth = 3;
		if (this.busSize < 2){
			buswidth = 1;
		}
		
		g.setColor(WrapperPainted.BACKGROUND);
		
		offsetY = 3 * gridGap / 2;
		offsetX = 2 * gridGap;
		
		g.setColor(this.brush);
		g.drawArc(3*gridGap/2, gridGap, gridGap, gridGap, 0, 360);
		g.fillRect(gridGap*2-1, gridGap*2, buswidth, gridGap);
		
		g.setFont(new Font(Wrapper.FONT_NAME,Font.PLAIN,3 * gridGap / 5));
		/*String tmp;
		if (this.pinNumber < 10){
			tmp = "0" + this.pinNumber;
		}else{
			tmp = Integer.toString(this.pinNumber);
		}*/
		g.drawString(this.label,6*gridGap/4,3*gridGap/4);
	}
	
	protected void paintFlipped_0(Graphics g)
	{
		this.paintNormal_0(g);
	}
	
	protected void paintFlipped_90(Graphics g)
	{
		this.paintNormal_270(g);
	}
	
	protected void paintFlipped_180(Graphics g)
	{
		this.paintNormal_180(g);
	}
	
	protected void paintFlipped_270(Graphics g)
	{
		this.paintNormal_90(g);
	}
	
	
	
	
	protected boolean canDropNormal_0()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + 3, this.gridLocation.y + 2, this.busSize);
	}
	
	protected boolean canDropNormal_90()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + 2, this.gridLocation.y + 1, this.busSize);
	}
	
	protected boolean canDropNormal_180()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + 1, this.gridLocation.y + 2, this.busSize);
	}
	
	protected boolean canDropNormal_270()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + 2, this.gridLocation.y + 3, this.busSize);
	}
	
	protected boolean canDropFlipped_0()
	{
		return this.canDropNormal_0();
	}
	
	protected boolean canDropFlipped_90()
	{
		return this.canDropNormal_270();
	}
	
	protected boolean canDropFlipped_180()
	{
		return this.canDropNormal_180();
	}
	
	protected boolean canDropFlipped_270()
	{
		return this.canDropNormal_90();
	}
	
	
	
	
	protected void dropedNormal_0()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x + 3, this.gridLocation.y + 2, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
		numPins++;
		nextNum = numPins;
	}
	
	protected void dropedNormal_90()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x + 2, this.gridLocation.y + 1, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
		numPins++;
		nextNum = numPins;
	}
	
	protected void dropedNormal_180()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x+1, this.gridLocation.y + 2, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
		numPins++;
		nextNum = numPins;
	}
	
	protected void dropedNormal_270()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x + 2, this.gridLocation.y + 3, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
		numPins++;
		nextNum = numPins;
	}
	
	protected void dropedFlipped_0()
	{
		this.dropedNormal_0();
		numPins++;
		nextNum = numPins;
	}
	
	protected void dropedFlipped_90()
	{
		this.dropedNormal_270();
		numPins++;
		nextNum = numPins;
	}
	
	protected void dropedFlipped_180()
	{
		this.dropedNormal_180();
		numPins++;
		nextNum = numPins;
	}
	
	protected void dropedFlipped_270()
	{
		this.dropedNormal_90();
		numPins++;
		nextNum = numPins;
	}
	
	
	
	
	protected void adjustToChanges()
	{
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	private int oldBusSize = 0;
	
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new PinProperties(this.busSize,this.label));
	}
		
	public void respondToChanges(Component property)
	{
		this.brush = Color.white;
		CentralPanel.ACTIVE_GRID.paintComponent(this); //ERASE OLD DRAWING
		this.brush = Color.black;
		this.setBusSize(((PinProperties)property).getPinBusSize());
		this.setLabel(((PinProperties)property).getLabel());
		CentralPanel.ACTIVE_GRID.paintComponent(this);
	}
	
	
	
	public void restoreOriginalProperties()
	{
		if(this.oldBusSize != 0)
		{
			this.setBusSize(this.oldBusSize);
			this.oldBusSize = 0;
		}
	}

	@Override
	public void writeVHDLEntity(String path) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public String getVHDLName() {
		// TODO Auto-generated method stub
		return null;
	}
}
