package sim.lib.others;

import java.awt.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.Writer;
import java.util.Scanner;

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

import sim.lib.wires.Junction;
import sim.lib.wires.JunctionList;

public class Encoder extends RotatableFlippableWrapperPainted implements EngineModule {
	/* ==================================================================
	 Creation Part
	 ================================================================= */

	public Image getIcon() {
		return GuiFileLink.getImage("sim/lib/others/Encoder.gif");
	}

	public Wrapper createWrapper() {
		return this.getCopy();
	}

	public Wrapper createWrapper(Point gridPosition) {
		Encoder result = this.getCopy();
		result.setGridLocation(gridPosition);
		return result;
	}

	public String getBubbleHelp() {
		return "Encoder";
	}

	/* ==================================================================
	 GUI part
	 ================================================================= */
	public Encoder() {
		super();

		this.setBusSize(3);
	}

	private Encoder getCopy() {
		Encoder result = new Encoder();

		result.setBusSize(this.outputBusSize);
		result.changeDelay(this.delay);

		return result;
	}

	public void initializeGridSize() {
		this.setGridSize(4, 6);
	}
	/* ==================================================================
	 Maintanance Part
	 ================================================================= */
	private Junction output = new Junction(1);
	private JunctionList input = new JunctionList();
	private int numOfInputs;
	private int outputBusSize;
	private static int INPUT_BUS_SIZE = 1;

	public void setBusSize(int size) {
		this.outputBusSize = size;
		this.numOfInputs = (int) Math.pow(2, this.outputBusSize);
		this.adjustToChanges();
	}

	public void selected() {
		this.output.removePin();
		for (int loop = 0; loop < this.numOfInputs; loop++) {
			this.input.getItemAt(loop).removePin();
		}

		this.changeColor(Color.green);
	}

	public void checkAfterSelected() {
		Wrapper.checkPin(this.output);
		for (int loop = 0; loop < this.numOfInputs; loop++) {
			Wrapper.checkPin(this.input.getItemAt(loop));
		}
	}
	/* ==================================================================
	 Simulation part
	 ================================================================= */
	protected double delay = 1;

	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer) {
		boolean foundUndefined = false;
		double time = currentTime + this.delay;
		int position = -1;

		int loop;

		for (loop = 0; (loop < this.numOfInputs) && (!foundUndefined); loop++) {
			if (currentInputs[loop].isUndefined()) {
				foundUndefined = true;
			} else {
				if (currentInputs[loop].getValue()) {
					if (position != -1) {
						foundUndefined = true;
					} else {
						position = loop;
					}
				}
			}
		}

		if (position == -1) {
			foundUndefined = true;
		}

		if (foundUndefined) {
			for (loop = 0; loop < this.outputBusSize; loop++) {
				peer.setOutputPinUndefined(loop, time);
			}
		} else {
			for (loop = 0; (loop < this.outputBusSize) && (position != 0); loop++) {
				if (position % 2 == 1) {
					peer.setOutputPinValue(loop, true, time);
				} else {
					peer.setOutputPinValue(loop, false, time);
				}
				position = position / 2;
			}

			while (loop < this.outputBusSize) {
				peer.setOutputPinValue(loop, false, time);
				loop++;
			}
		}
	}

	public void createEnginePeer(EnginePeerList epl) {
		EnginePeer ep = new EnginePeer(this.numOfInputs * this.INPUT_BUS_SIZE, this.outputBusSize, this);

		int loop, index, position;

		position = 0;
		//input
		for (index = 0; index < this.numOfInputs; index++) {
			for (loop = 0; loop < this.INPUT_BUS_SIZE; loop++) {
				ep.setInputPin(position, this.input.getItemAt(index).getNodes().getItemAt(loop));
				position++;
			}
		}

		position = 0;

		//output
		for (index = 0; index < this.outputBusSize; index++) {
			ep.setOutputPin(position, this.output.getNodes().getItemAt(index));
			position++;
		}

		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 (Integer.toString(this.outputBusSize) + Wrapper.SEPARATOR + Double.toString(this.delay) + Wrapper.SEPARATOR);
	}

	public void loadWrapper(String[] specificParameters) throws SimException {
		if (specificParameters.length == this.getNumberOfSpecificParameters()) {
			try {
				this.setBusSize(Integer.valueOf(specificParameters[0]).intValue());
				this.delay = Double.valueOf(specificParameters[1]).doubleValue();
			} catch (NumberFormatException e) {
				throw (new SimException("incorrect parameter type"));
			}
		} else {
			throw (new SimException("incorrect number of parameters"));
		}
	}

	public int getNumberOfSpecificParameters() {
		return 2;
	}
	/* ==================================================================
	 Rotation abd Flipping Part
	 ================================================================= */

	protected void paintNormal_0(Graphics g) {


		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;

			g.setColor(this.brush);


			int height = this.numOfInputs * gridGap;
			int width = 2 * gridGap;
			//top-left corner
			int xOffset = gridGap;
			int yOffset = 2 * increment;
			int middle = height / 2;
			int rightEdge = xOffset + width;
			//body
			g.drawRect(xOffset, yOffset, width, height);
			g.drawLine(xOffset, yOffset, xOffset + width , yOffset + gridGap);
			g.drawLine(xOffset, yOffset + height, xOffset + width, yOffset + height - gridGap);
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));

			// input bus
			int offset = 0;
			int inThickness = 1;
			int cableLen = gridGap;
			FontMetrics fm = this.getFontMetrics(this.getFont());

			if (this.outputBusSize != 1) {
				offset = -1;
				inThickness = 3;
			}

			g.fillRect(rightEdge, middle + offset, cableLen, inThickness);

			//label
			g.drawString("E", rightEdge - increment - fm.stringWidth("E"), middle + yOffset);

			//output bus
			int up = gridGap;
			int outThickness = 1;
			int textOffset = xOffset + increment;
			for (int index = 0; index < this.numOfInputs; index++) {
				g.drawString(Integer.toString(index), textOffset, up + yOffset);
				g.fillRect(0, up, cableLen, outThickness);
				up += gridGap;
			}
		}
	}

	protected void paintNormal_90(Graphics g) {
		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;

			g.setColor(this.brush);


			int width = this.numOfInputs * gridGap;
			int height = 2 * gridGap;
			//top-left corner
			int yOffset = gridGap;
			int xOffset = 2 * increment;
			int middle = width / 2;

			//body
			g.drawRect(xOffset, yOffset, width, height);
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));
			g.drawLine(xOffset + gridGap, yOffset, xOffset  , yOffset + height);
			g.drawLine(xOffset + width - gridGap, yOffset, xOffset + width, yOffset + height);

			// input bus
			int offset = 0;
			int inThickness = 1;
			int cableLen = gridGap;

			if (this.outputBusSize != 1) {
				offset = -1;
				inThickness = 3;
			}

			g.fillRect(middle + offset, 0, inThickness, cableLen);

			//label
			g.drawString("E", xOffset + middle, yOffset + height / 2 - increment);


			//output bus
			int left = gridGap;
			int outThickness = 1;
			int bottomEdge = yOffset + height;
			int textOffset = bottomEdge - increment;
			for (int index = 0; index < this.numOfInputs; index++) {
				g.drawString(Integer.toString(index), left, textOffset);
				g.fillRect(left, bottomEdge, outThickness, cableLen);
				left += gridGap;
			}
		}
	}

	protected void paintNormal_180(Graphics g) {


		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;

			g.setColor(this.brush);


			int height = this.numOfInputs * gridGap;
			int width = 2 * gridGap;
			//top-left corner
			int xOffset = gridGap;
			int yOffset = 2 * increment;
			int middle = height / 2;

			//body
			g.drawRect(xOffset, yOffset, width, height);
			g.drawLine(xOffset, yOffset + gridGap, xOffset + width , yOffset );
			g.drawLine(xOffset, yOffset + height - gridGap, xOffset + width, yOffset + height );
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));

			// input bus
			int offset = 0;
			int inThickness = 1;
			int cableLen = gridGap;
			FontMetrics fm = this.getFontMetrics(this.getFont());

			if (this.outputBusSize != 1) {
				offset = -1;
				inThickness = 3;
			}

			g.fillRect(0, middle + offset, cableLen, inThickness);

			//label
			g.drawString("E", xOffset + increment, middle + yOffset);


			//output bus
			int bottom = this.numOfInputs * gridGap;
			int outThickness = 1;
			int rightEdge = xOffset + width;
			int textOffset = rightEdge - increment;
			for (int index = 0; index < this.numOfInputs; index++) {
				g.drawString(Integer.toString(index), textOffset - fm.stringWidth(Integer.toString(index)), bottom + yOffset);
				g.fillRect(rightEdge, bottom, cableLen, outThickness);
				bottom -= gridGap;
			}
		}
	}

	protected void paintNormal_270(Graphics g) {
		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;

			g.setColor(this.brush);


			int width = this.numOfInputs * gridGap;
			int height = 2 * gridGap;
			//top-left corner
			int yOffset = gridGap;
			int xOffset = 2 * increment;
			int middle = width / 2;

			//body
			g.drawRect(xOffset, yOffset, width, height);
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));
			g.drawLine(xOffset, yOffset, xOffset + gridGap , yOffset + height);
			g.drawLine(xOffset + width, yOffset, xOffset + width - gridGap, yOffset + height);

			// input bus
			int offset = 0;
			int inThickness = 1;
			int cableLen = gridGap;

			if (this.outputBusSize != 1) {
				offset = -1;
				inThickness = 3;
			}

			g.fillRect(middle + offset, yOffset + height, inThickness, cableLen);

			//label
			g.drawString("E", xOffset + middle, height + yOffset - increment);


			//output bus
			int right = this.numOfInputs * gridGap;
			int outThickness = 1;
			int topEdge = yOffset;
			int textOffset = 2 * topEdge - increment;
			for (int index = 0; index < this.numOfInputs; index++) {
				g.drawString(Integer.toString(index), right, textOffset);
				g.fillRect(right, 0, outThickness, cableLen);
				right -= gridGap;
			}
		}
	}

	protected void paintFlipped_0(Graphics g) {

		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;

			g.setColor(this.brush);


			int height = this.numOfInputs * gridGap;
			int width = 2 * gridGap;
			//top-left corner
			int xOffset = gridGap;
			int yOffset = 2 * increment;
			int middle = height / 2;
			int rightEdge = xOffset + width;
			//body
			g.drawRect(xOffset, yOffset, width, height);
			g.drawLine(xOffset, yOffset, xOffset + width , yOffset + gridGap);
			g.drawLine(xOffset, yOffset + height, xOffset + width, yOffset + height - gridGap);
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));

			// input bus
			int offset = 0;
			int inThickness = 1;
			int cableLen = gridGap;
			FontMetrics fm = this.getFontMetrics(this.getFont());

			if (this.outputBusSize != 1) {
				offset = -1;
				inThickness = 3;
			}

			g.fillRect(rightEdge, middle + offset, cableLen, inThickness);

			//label
			g.drawString("E", rightEdge - increment - fm.stringWidth("E"), middle + yOffset);


			//output bus
			int bottom = this.numOfInputs * gridGap;
			int outThickness = 1;
			int textOffset = xOffset + increment;
			for (int index = 0; index < this.numOfInputs; index++) {
				g.drawString(Integer.toString(index), textOffset, bottom + yOffset);
				g.fillRect(0, bottom, cableLen, outThickness);
				bottom -= gridGap;
			}
		}
	}

	protected void paintFlipped_90(Graphics g) {
		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;

			g.setColor(this.brush);


			int width = this.numOfInputs * gridGap;
			int height = 2 * gridGap;
			//top-left corner
			int yOffset = gridGap;
			int xOffset = 2 * increment;
			int middle = width / 2;

			//body
			g.drawRect(xOffset, yOffset, width, height);
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));
			g.drawLine(xOffset, yOffset, xOffset + gridGap , yOffset + height);
			g.drawLine(xOffset + width, yOffset, xOffset + width - gridGap, yOffset + height);


			// input bus
			int offset = 0;
			int inThickness = 1;
			int cableLen = gridGap;

			if (this.outputBusSize != 1) {
				offset = -1;
				inThickness = 3;
			}

			g.fillRect(middle + offset, yOffset + height, inThickness, cableLen);

			//label
			g.drawString("E", xOffset + middle, height + yOffset - increment);


			//output bus
			int left = gridGap;
			int outThickness = 1;
			int topEdge = yOffset;
			int textOffset = 2 * topEdge - increment;
			for (int index = 0; index < this.numOfInputs; index++) {
				g.drawString(Integer.toString(index), left, textOffset);
				g.fillRect(left, 0, outThickness, cableLen);
				left += gridGap;
			}
		}
	}

	protected void paintFlipped_180(Graphics g) {


		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;

			g.setColor(this.brush);


			int height = this.numOfInputs * gridGap;
			int width = 2 * gridGap;
			//top-left corner
			int xOffset = gridGap;
			int yOffset = 2 * increment;
			int middle = height / 2;

			//body
			g.drawRect(xOffset, yOffset, width, height);
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));
			g.drawLine(xOffset, yOffset + gridGap, xOffset + width , yOffset );
			g.drawLine(xOffset, yOffset + height - gridGap, xOffset + width, yOffset + height );

			// input bus
			int offset = 0;
			int inThickness = 1;
			int cableLen = gridGap;
			FontMetrics fm = this.getFontMetrics(this.getFont());

			if (this.outputBusSize != 1) {
				offset = -1;
				inThickness = 3;
			}

			g.fillRect(0, middle + offset, cableLen, inThickness);

			//label
			g.drawString("E", xOffset + increment, middle + yOffset);


			//output bus
			int up = gridGap;
			int outThickness = 1;
			int rightEdge = xOffset + width;
			int textOffset = rightEdge - increment;
			for (int index = 0; index < this.numOfInputs; index++) {
				g.drawString(Integer.toString(index), textOffset - fm.stringWidth(Integer.toString(index)), up + yOffset);
				g.fillRect(rightEdge, up, cableLen, outThickness);
				up += gridGap;
			}
		}
	}

	protected void paintFlipped_270(Graphics g) {
		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;

			g.setColor(this.brush);


			int width = this.numOfInputs * gridGap;
			int height = 2 * gridGap;
			//top-left corner
			int yOffset = gridGap;
			int xOffset = 2 * increment;
			int middle = width / 2;

			//body
			g.drawRect(xOffset, yOffset, width, height);
			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));
			g.drawLine(xOffset + gridGap, yOffset, xOffset  , yOffset + height);
			g.drawLine(xOffset + width - gridGap, yOffset, xOffset + width, yOffset + height);

			// input bus
			int offset = 0;
			int inThickness = 1;
			int cableLen = gridGap;

			if (this.outputBusSize != 1) {
				offset = -1;
				inThickness = 3;
			}

			g.fillRect(middle + offset, 0, inThickness, cableLen);

			//label
			g.drawString("E", xOffset + middle, yOffset + height / 2 - increment);


			//output bus
			int right = this.numOfInputs * gridGap;
			int outThickness = 1;
			int bottomEdge = yOffset + height;
			int textOffset = bottomEdge - increment;
			for (int index = 0; index < this.numOfInputs; index++) {
				g.drawString(Integer.toString(index), right, textOffset);
				g.fillRect(right, bottomEdge, outThickness, cableLen);
				right -= gridGap;
			}
		}
	}

	protected boolean canDropNormal_0() {

		boolean result = true;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.x + this.gridSize.width;
		int loop;

		// check outputs
		for (loop = 0; (loop < this.numOfInputs) && result; loop++) {
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 1 + loop, this.INPUT_BUS_SIZE);
		}

		// check inputs

		if (result) {
			result = Wrapper.canDropJuncion(end, this.gridLocation.y + middle, this.outputBusSize);
		}

		return result;
	}

	protected boolean canDropNormal_90() {

		boolean result = true;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.y + this.gridSize.height;
		int loop;

		// check outputs
		for (loop = 0; (loop < this.numOfInputs) && result; loop++) {
			result = Wrapper.canDropJuncion(this.gridLocation.x + 1 + loop, end, this.INPUT_BUS_SIZE);
		}

		// check inputs

		if (result) {
			result = Wrapper.canDropJuncion(this.gridLocation.x + middle, this.gridLocation.y, this.outputBusSize);
		}

		return result;
	}

	protected boolean canDropNormal_180() {

		boolean result = true;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.x + this.gridSize.width;
		int loop;

		// check outputs
		for (loop = 0; (loop < this.numOfInputs) && result; loop++) {
			result = Wrapper.canDropJuncion(end, this.gridLocation.y + this.numOfInputs - loop, this.INPUT_BUS_SIZE);
		}

		// check inputs

		if (result) {
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + middle, this.outputBusSize);
		}

		return result;
	}

	protected boolean canDropNormal_270() {

		boolean result = true;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.y + this.gridSize.height;
		int loop;

		// check outputs
		for (loop = 0; (loop < this.numOfInputs) && result; loop++) {
			result = Wrapper.canDropJuncion(this.gridLocation.x + this.numOfInputs - loop, this.gridLocation.y, this.INPUT_BUS_SIZE);
		}

		// check inputs

		if (result) {
			result = Wrapper.canDropJuncion(this.gridLocation.x + middle, end, this.outputBusSize);
		}

		return result;
	}

	protected boolean canDropFlipped_0() {

		boolean result = true;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.x + this.gridSize.width;
		int loop;

		// check outputs
		for (loop = 0; (loop < this.numOfInputs) && result; loop++) {
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + this.numOfInputs - loop, this.INPUT_BUS_SIZE);
		}

		// check inputs

		if (result) {
			result = Wrapper.canDropJuncion(end, this.gridLocation.y + middle, this.outputBusSize);
		}

		return result;
	}

	protected boolean canDropFlipped_90() {

		boolean result = true;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.y + this.gridSize.height;
		int loop;

		// check outputs
		for (loop = 0; (loop < this.numOfInputs) && result; loop++) {
			result = Wrapper.canDropJuncion(this.gridLocation.x + 1 + loop, this.gridLocation.y, this.INPUT_BUS_SIZE);
		}

		// check inputs

		if (result) {
			result = Wrapper.canDropJuncion(this.gridLocation.x + middle, end, this.outputBusSize);
		}

		return result;
	}

	protected boolean canDropFlipped_180() {

		boolean result = true;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.x + this.gridSize.width;
		int loop;

		// check outputs
		for (loop = 0; (loop < this.numOfInputs) && result; loop++) {
			result = Wrapper.canDropJuncion(end, this.gridLocation.y + 1 + loop, this.INPUT_BUS_SIZE);
		}

		// check inputs

		if (result) {
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + middle, this.outputBusSize);
		}

		return result;
	}

	protected boolean canDropFlipped_270() {

		boolean result = true;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.y + this.gridSize.height;
		int loop;

		// check outputs
		for (loop = 0; (loop < this.numOfInputs) && result; loop++) {
			result = Wrapper.canDropJuncion(this.gridLocation.x + this.numOfInputs - loop, end, this.INPUT_BUS_SIZE);
		}

		// check inputs

		if (result) {
			result = Wrapper.canDropJuncion(this.gridLocation.x + middle, this.gridLocation.y, this.outputBusSize);
		}

		return result;
	}

	protected void dropedNormal_0() {

		int loop;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.x + this.gridSize.width;

		//output
		this.input.setSize(this.numOfInputs);

		for (loop = 0; loop < this.numOfInputs; loop++) {
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + loop, this.INPUT_BUS_SIZE));
		}

		//input
		this.output = Wrapper.setPinAt(end, this.gridLocation.y + middle, this.outputBusSize);

		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}

	protected void dropedNormal_90() {

		int loop;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.y + this.gridSize.height;

		//output
		this.input.setSize(this.numOfInputs);

		for (loop = 0; loop < this.numOfInputs; loop++) {
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, end, this.INPUT_BUS_SIZE));
		}

		//input
		this.output = Wrapper.setPinAt(this.gridLocation.x + middle, this.gridLocation.y, this.outputBusSize);

		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}

	protected void dropedNormal_180() {

		int loop;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.x + this.gridSize.width;

		//output
		this.input.setSize(this.numOfInputs);

		for (loop = 0; loop < this.numOfInputs; loop++) {
			this.input.changeItem(loop, Wrapper.setPinAt(end, this.gridLocation.y + this.numOfInputs - loop, this.INPUT_BUS_SIZE));
		}

		//input
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + middle, this.outputBusSize);

		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}

	protected void dropedNormal_270() {

		int loop;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.y + this.gridSize.height;

		//output
		this.input.setSize(this.numOfInputs);

		for (loop = 0; loop < this.numOfInputs; loop++) {
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + this.numOfInputs - loop, this.gridLocation.y, this.INPUT_BUS_SIZE));
		}

		//input
		this.output = Wrapper.setPinAt(this.gridLocation.x + middle, end, this.outputBusSize);

		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}

	protected void dropedFlipped_0() {

		int loop;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.x + this.gridSize.width;

		//output
		this.input.setSize(this.numOfInputs);

		for (loop = 0; loop < this.numOfInputs; loop++) {
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + this.numOfInputs - loop, this.INPUT_BUS_SIZE));
		}

		//input
		this.output = Wrapper.setPinAt(end, this.gridLocation.y + middle, this.outputBusSize);

		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}

	protected void dropedFlipped_90() {

		int loop;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.y + this.gridSize.height;

		//output
		this.input.setSize(this.numOfInputs);

		for (loop = 0; loop < this.numOfInputs; loop++) {
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y, this.INPUT_BUS_SIZE));
		}

		//input
		this.output = Wrapper.setPinAt(this.gridLocation.x + middle, end, this.outputBusSize);

		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}

	protected void dropedFlipped_180() {

		int loop;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.x + this.gridSize.width;

		//output
		this.input.setSize(this.numOfInputs);

		for (loop = 0; loop < this.numOfInputs; loop++) {
			this.input.changeItem(loop, Wrapper.setPinAt(end, this.gridLocation.y + 1 + loop, this.INPUT_BUS_SIZE));
		}

		//input
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + middle, this.outputBusSize);

		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}

	protected void dropedFlipped_270() {

		int loop;
		int middle = this.numOfInputs / 2;
		int end = this.gridLocation.y + this.gridSize.height;

		//output
		this.input.setSize(this.numOfInputs);

		for (loop = 0; loop < this.numOfInputs; loop++) {
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + this.numOfInputs - loop, end, this.INPUT_BUS_SIZE));
		}

		//input
		this.output = Wrapper.setPinAt(this.gridLocation.x + middle, this.gridLocation.y, this.outputBusSize);

		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}

	protected void adjustToChanges() {
		if (this.angle % 180 == 0) {
			this.setGridSize(4, this.numOfInputs + 1);
		} else {
			this.setGridSize(this.numOfInputs + 1, 4);
		}
	}
	/* ==================================================================
	 Popup Part
	 ================================================================= */
	private int oldBusSize = 0;

	public boolean hasProperties() {
		return true;
	}

	public Component getPropertyWindow() {
		return (new EncoderProperties(this.outputBusSize, this.delay));
	}

	public void respondToChanges(Component property) {
		EncoderProperties dp = (EncoderProperties) property;

		this.delay = dp.getDelay();

		if (this.oldBusSize == 0) {
			this.oldBusSize = this.outputBusSize;
		}

		CentralPanel.ACTIVE_GRID.eraseComponent(this);
		this.setBusSize(dp.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) {
		String fileName = "gates.vhd";
		File file = new File(path+"\\"+fileName);
		boolean written = false;
		try {
			Scanner scanner = new Scanner(file);
			while(scanner.hasNextLine()){
				String line = scanner.nextLine();
				if(line.contains(this.getVHDLName())){
					written = true;
				}
			}
			scanner.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		
		if(!written){
			Writer writer = null;
			try {
				writer = new FileWriter(path+"\\"+fileName,true);
				writer.write("\nlibrary IEEE;\n");
				writer.write("use IEEE.STD_LOGIC_1164.ALL;\n");
				
				writer.write("\nentity " + this.getVHDLName() + " is\n");
				writer.write("port(\n");
				for(int i = 0; i < Math.pow(2,this.outputBusSize); i++){
					if(i != 0)
						writer.write(",");
					writer.write("x" + i);
				}
				writer.write(" : in std_logic;\n");
				for(int i = 0; i < Math.pow(2,this.outputBusSize); i++){
					if(i != 0)
						writer.write(",");
					writer.write("f" + i);
				}
				writer.write(" : out std_logic);\n");
				writer.write("end "+ this.getVHDLName() + ";\n\n");
				
				writer.write("architecture behav of "+ this.getVHDLName() + " is\n");
				writer.write("begin\n");
				for(int j = 0; j < Math.pow(2,this.outputBusSize); j++){
					writer.write("f"+j+" <= x"+j+";\n");
				}
				writer.write("end behav;\n");
				writer.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
	}

	@Override
	public String getVHDLName() {
		// TODO Auto-generated method stub
		return "ENCODER_"+this.outputBusSize;
	}
}
