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);
	}

	public void setInput(Junction in) {
		this.input = in;
	}

	/* ==================================================================
	 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;
		}
	}

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

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