package sim.lib.outputs;

import java.awt.*;

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

public class BusLed extends RotatableFlippableWrapperPainted implements EngineModule
{
/* ==================================================================
	Creation Part
	================================================================= */
	private static Image ICON = GuiFileLink.getImage("sim/lib/outputs/LcdIcon.gif");
	
	public Image getIcon()
	{
		return BusLed.ICON;
	}
	
	public Wrapper createWrapper()
	{
		return this.getCopy();
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		BusLed result = this.getCopy();
		result.setGridLocation(gridPosition);
		return result;
	}
	
	public String getBubbleHelp()
	{
		return "LCD";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	private String value = null;
	private int busSize;
	private int valueLenght;
	
	public BusLed()
	{
		super();
		
		this.setBusSize(8);
	}
	
	public BusLed getCopy()
	{
		BusLed result = new BusLed();
		result.setBusSize(this.busSize);
		
		return result;
	}
	
	public void initializeGridSize()
	{
		this.setGridSize(4, 4);
	}
	
	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();
		
		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 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);
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.setBusSize(Integer.valueOf(specificParameters[0]).intValue());
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 1;
	}
	
/* ==================================================================
	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;
		
		g.setColor(WrapperPainted.BACKGROUND);
		
		offsetY = 3 * gridGap / 2;
		offsetX = 2 * gridGap;
		
		g.fillRect(gridGap + 2, offsetY + 2, width - offsetX - 3, gridGap - 3);
		
		g.setColor(this.brush);
		g.drawRect(gridGap, offsetY, width - offsetX, gridGap);
		g.fillRect(width - gridGap, offsetX - 1, gridGap, 3);
		
		g.setColor(WrapperPainted.BACKGROUND.brighter());
		g.drawLine(gridGap + 1, offsetY + 1, width - gridGap - 1, offsetY + 1);
		g.drawLine(gridGap + 1, offsetY + 1, gridGap + 1, offsetY + gridGap - 1);
		
		g.setColor(WrapperPainted.BACKGROUND.darker());
		g.drawLine(gridGap + 1, offsetY + gridGap - 1, width - gridGap - 1, offsetY + gridGap - 1);
		g.drawLine(width - gridGap - 1, offsetY + 2, width - gridGap - 1, offsetY + gridGap - 1);
		
		if(this.value != null)
		{
			g.setColor(Color.blue);
			
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * gridGap / 4));
			
			offsetX = gridGap + (width - offsetX - g.getFontMetrics().stringWidth(this.value)) / 2;
			offsetY = offsetY + 3 * gridGap / 4 + 1;
			
			g.drawString(this.value, offsetX, offsetY);
		}
	}
	
	protected void paintNormal_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int width = this.gridSize.width * gridGap;
		int offsetX, offsetY;
		
		g.setColor(WrapperPainted.BACKGROUND);
		
		offsetY = 3 * gridGap / 2;
		offsetX = 2 * gridGap;
		
		g.fillRect(gridGap + 2, offsetY + 2, width - offsetX - 3, gridGap - 3);
		
		g.setColor(WrapperPainted.BACKGROUND.brighter());
		g.drawLine(gridGap + 1, offsetY + 1, width - gridGap - 1, offsetY + 1);
		g.drawLine(gridGap + 1, offsetY + 1, gridGap + 1, offsetY + gridGap - 1);
		
		g.setColor(WrapperPainted.BACKGROUND.darker());
		g.drawLine(gridGap + 1, offsetY + gridGap - 1, width - gridGap - 1, offsetY + gridGap - 1);
		g.drawLine(width - gridGap - 1, offsetY + 2, width - gridGap - 1, offsetY + gridGap - 1);
		
		g.setColor(this.brush);
		g.drawRect(gridGap, offsetY, width - offsetX, gridGap);
		g.fillRect((this.getGridSize().width / 2) * gridGap - 1, 0, 3, offsetY);
		
		if(this.value != null)
		{
			g.setColor(Color.blue);
			
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * gridGap / 4));
			
			offsetX = gridGap + (width - offsetX - g.getFontMetrics().stringWidth(this.value)) / 2;
			offsetY = offsetY + 3 * gridGap / 4 + 1;
			
			g.drawString(this.value, offsetX, offsetY);
		}
	}
	
	protected void paintNormal_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int width = this.gridSize.width * gridGap;
		int offsetX, offsetY;
		
		g.setColor(WrapperPainted.BACKGROUND);
		
		offsetY = 3 * gridGap / 2;
		offsetX = 2 * gridGap;
		
		g.fillRect(gridGap + 2, offsetY + 2, width - offsetX - 3, gridGap - 3);
		
		g.setColor(this.brush);
		g.drawRect(gridGap, offsetY, width - offsetX, gridGap);
		g.fillRect(0, offsetX - 1, gridGap, 3);
		
		g.setColor(WrapperPainted.BACKGROUND.brighter());
		g.drawLine(gridGap + 1, offsetY + 1, width - gridGap - 1, offsetY + 1);
		g.drawLine(gridGap + 1, offsetY + 1, gridGap + 1, offsetY + gridGap - 1);
		
		g.setColor(WrapperPainted.BACKGROUND.darker());
		g.drawLine(gridGap + 1, offsetY + gridGap - 1, width - gridGap - 1, offsetY + gridGap - 1);
		g.drawLine(width - gridGap - 1, offsetY + 2, width - gridGap - 1, offsetY + gridGap - 1);
		
		if(this.value != null)
		{
			g.setColor(Color.blue);
			
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * gridGap / 4));
			
			offsetX = gridGap + (width - offsetX - g.getFontMetrics().stringWidth(this.value)) / 2;
			offsetY = offsetY + 3 * gridGap / 4 + 1;
			
			g.drawString(this.value, offsetX, offsetY);
		}
	}
	
	protected void paintNormal_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int width = this.gridSize.width * gridGap;
		int offsetX, offsetY;
		
		g.setColor(WrapperPainted.BACKGROUND);
		
		offsetY = 3 * gridGap / 2;
		offsetX = 2 * gridGap;
		
		g.fillRect(gridGap + 2, offsetY + 2, width - offsetX - 3, gridGap - 3);
		
		g.setColor(WrapperPainted.BACKGROUND.brighter());
		g.drawLine(gridGap + 1, offsetY + 1, width - gridGap - 1, offsetY + 1);
		g.drawLine(gridGap + 1, offsetY + 1, gridGap + 1, offsetY + gridGap - 1);
		
		g.setColor(WrapperPainted.BACKGROUND.darker());
		g.drawLine(gridGap + 1, offsetY + gridGap - 1, width - gridGap - 1, offsetY + gridGap - 1);
		g.drawLine(width - gridGap - 1, offsetY + 2, width - gridGap - 1, offsetY + gridGap - 1);
		
		g.setColor(this.brush);
		g.drawRect(gridGap, offsetY, width - offsetX, gridGap);
		g.fillRect((this.getGridSize().width / 2) * gridGap - 1, offsetY + gridGap, 3 , offsetY);
		
		if(this.value != null)
		{
			g.setColor(Color.blue);
			
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * gridGap / 4));
			
			offsetX = gridGap + (width - offsetX - g.getFontMetrics().stringWidth(this.value)) / 2;
			offsetY = offsetY + 3 * gridGap / 4 + 1;
			
			g.drawString(this.value, offsetX, offsetY);
		}
	}
	
	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 + this.gridSize.width, this.gridLocation.y + 2, this.busSize);
	}
	
	protected boolean canDropNormal_90()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y, this.busSize);
	}
	
	protected boolean canDropNormal_180()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2, this.busSize);
	}
	
	protected boolean canDropNormal_270()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y + 4, 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 + this.gridSize.width, this.gridLocation.y + 2, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_90()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_180()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_270()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y + 4, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedFlipped_0()
	{
		this.dropedNormal_0();
	}
	
	protected void dropedFlipped_90()
	{
		this.dropedNormal_270();
	}
	
	protected void dropedFlipped_180()
	{
		this.dropedNormal_180();
	}
	
	protected void dropedFlipped_270()
	{
		this.dropedNormal_90();
	}
	
	
	
	
	protected void adjustToChanges()
	{
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	private int oldBusSize = 0;
	
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new EditBusSize(this.busSize));
	}
		
	public void respondToChanges(Component property)
	{
		if(this.oldBusSize == 0)
			this.oldBusSize = this.busSize;
		
		CentralPanel.ACTIVE_GRID.eraseComponent(this);
		this.setBusSize(((EditBusSize)property).getBusSize());
		CentralPanel.ACTIVE_GRID.paintComponent(this);
	}
	
	public void restoreOriginalProperties()
	{
		if(this.oldBusSize != 0)
		{
			this.setBusSize(this.oldBusSize);
			this.oldBusSize = 0;
		}
	}
}
