package sim.lib.others;

import java.awt.*;

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

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

public class Multiplexer extends RotatableFlippableWrapperPainted implements EngineModule
{
/* ==================================================================
	Creation Part
	================================================================= */
	public Image getIcon()
	{
		return GuiFileLink.getImage("sim/lib/others/MuxIcon.gif");
	}
	
	public Wrapper createWrapper()
	{
		return this.getCopy();
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		Multiplexer result = this.getCopy();
		result.setGridLocation(gridPosition);
		return result;
	}
	
	public String getBubbleHelp()
	{
		return "Multiplexer";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	public Multiplexer()
	{
		super();
		
		this.setInputSize(2);
		this.setBusSize(1);
	}
	
	private Multiplexer getCopy()
	{
		Multiplexer result = new Multiplexer();
		
		result.setInputSize(this.inputSize);
		result.setBusSize(this.busSize);
		result.changeDelay(this.delay);
		
		return result;
	}
	
	public void initializeGridSize()
	{
		this.setGridSize(4, 6);
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	private JunctionList input = new JunctionList();
	private Junction select = null;
	private Junction output = null;
	private int inputSize;
	private int requiredSelect;
	private int busSize;
	
	public void setInputSize(int size)
	{
		this.inputSize = size;
		this.requiredSelect = (int)Math.ceil(Math.log(size) / Math.log(2));
		this.adjustToChanges();
	}
	
	public void setBusSize(int size)
	{
		this.busSize = size;
	}
	
	public void selected()
	{
		for(int loop = 0; loop < this.inputSize; loop++)
			this.input.getItemAt(loop).removePin();
		
		this.output.removePin();
		this.select.removePin();
		
		this.changeColor(Color.green);
	}
	
	public void checkAfterSelected()
	{
		for(int loop = 0; loop < this.inputSize; loop++)
			Wrapper.checkPin(this.input.getItemAt(loop));
		
		Wrapper.checkPin(this.output);
		Wrapper.checkPin(this.select);
	}
/* ==================================================================
	Simulation part
	================================================================= */
	protected double delay = 1;
	
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer)
	{
		boolean foundUndefined = false;
		int position = 0;
		int base = 1;
		double time = currentTime + this.delay;
		
		int loop;
		
		for(loop = 0; (loop < this.requiredSelect) && (!foundUndefined); loop++)
		{
			if(currentInputs[loop].isUndefined())
			{
				foundUndefined = true;
			}
			else
			{
				if(currentInputs[loop].getValue())
					position = position + base;
				
				base = 2 * base;
			}
		}
		
		if(foundUndefined || (position >= this.inputSize))
		{
			for(loop = 0; loop < this.busSize; loop++)
				peer.setOutputPinUndefined(loop, time);
		}
		else
		{
			base = this.requiredSelect + this.busSize * position;
			
			for(loop = 0; loop < this.busSize; loop++)
			{
				if(currentInputs[base].isUndefined())
					peer.setOutputPinUndefined(loop, time);
				else
					peer.setOutputPinValue(loop, currentInputs[base].getValue(), time);
				
				base++;
			}
		}
	}
	
	public void createEnginePeer(EnginePeerList epl)
	{
		EnginePeer ep = new EnginePeer(this.inputSize * this.busSize + this.requiredSelect, this.busSize, this);
		
		int loop, index, position;
		
		for(loop = 0; loop < this.requiredSelect; loop++)
			ep.setInputPin(loop, this.select.getNodes().getItemAt(loop));
		
		position = this.requiredSelect;
		
		for(loop = 0; loop < this.inputSize; loop++)
			for(index = 0; index < this.busSize; index++)
			{
				ep.setInputPin(position, this.input.getItemAt(loop).getNodes().getItemAt(index));
				position++;
			}
		
		for(loop = 0; loop < this.busSize; loop++)
			ep.setOutputPin(loop, this.output.getNodes().getItemAt(loop));
		
		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.inputSize) + Wrapper.SEPARATOR + Integer.toString(this.busSize) + Wrapper.SEPARATOR + Double.toString(this.delay) + Wrapper.SEPARATOR);
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.setInputSize(Integer.valueOf(specificParameters[0]).intValue());
				this.setBusSize(Integer.valueOf(specificParameters[1]).intValue());
				this.delay = Double.valueOf(specificParameters[2]).doubleValue();
			}
			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 increment = gridGap / 4;
		int middle = this.inputSize / 2;
		int textOffset = gridGap + increment;
		
		g.setColor(this.brush);
		
		int up = 7 * increment;
		int height = gridGap * this.getGridSize().height;
		int center = 3 * gridGap;
		
		g.drawLine(gridGap, gridGap, gridGap, height - gridGap);
		g.drawLine(center, up, center, height - up);
		
		g.drawLine(gridGap, gridGap, center, up);
		g.drawLine(gridGap, height - gridGap, center, height - up);
		
		up = 2 * gridGap;
		
		if(this.requiredSelect == 1)
			g.drawLine(up, 0, up, textOffset);
		else
			g.fillRect(up - 1, 0, 3, textOffset);
		
		
		center = height / 2;
		g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));			
		
		int offset = 0;
		int thickness = 1;
		
		if(this.busSize != 1)
		{
			offset = -1;
			thickness = 3;
		}
		
		g.fillRect(3 * gridGap, center + offset, gridGap, thickness);
		
		for(height = 0; height < middle; height++)
		{
			g.drawString(Integer.toString(height), textOffset, up + increment);
			g.fillRect(0, up + offset, gridGap, thickness);
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			g.drawString(Integer.toString(height), textOffset, up + increment);
			g.fillRect(0, up + offset, gridGap, thickness);
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				g.drawString(Integer.toString(height), textOffset, up + increment);
				g.fillRect(0, up + offset, gridGap, thickness);
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				g.drawString(Integer.toString(height), textOffset, up + increment);
				g.fillRect(0, up + offset, gridGap, thickness);
				up = up + gridGap;
			}
		}
	}
	
	protected void paintNormal_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int middle = this.inputSize / 2;
		int textOffset = gridGap + increment;
		int tripple = 3 * gridGap;
		
		g.setColor(this.brush);
		
		int up = 7 * increment;
		int height = gridGap * this.getGridSize().width;
		int center = height / 2;
		
		g.drawLine(gridGap, tripple, height - gridGap, tripple);
		g.drawLine(up, gridGap, height - up, gridGap);
		
		g.drawLine(gridGap, tripple, up, gridGap);
		g.drawLine(height - gridGap, tripple, height - up, gridGap);
		
		up = 2 * gridGap;
		
		if(this.requiredSelect == 1)
			g.drawLine(0, up, textOffset, up);
		else
			g.fillRect(0, up - 1, textOffset, 3);
		
		g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));			
		
		int offset = 0;
		int thickness = 1;
		
		if(this.busSize != 1)
		{
			offset = -1;
			thickness = 3;
		}
		
		g.fillRect(center + offset, 0, thickness, gridGap);
		
		textOffset = tripple - increment;
		FontMetrics fm = g.getFontMetrics();
		String toDraw;
		
		for(height = 0; height < middle; height++)
		{
			toDraw = Integer.toString(height);
			g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
			g.fillRect(up + offset, tripple, thickness, gridGap);
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			toDraw = Integer.toString(height);
			g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
			g.fillRect(up + offset, tripple, thickness, gridGap);
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(height);
				g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
				g.fillRect(up + offset, tripple, thickness, gridGap);
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(height);
				g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
				g.fillRect(up + offset, tripple, thickness, gridGap);
				up = up + gridGap;
			}
		}
	}
	
	protected void paintNormal_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int middle = this.inputSize / 2;
		int textOffset = gridGap + increment;
		int tripple = 3 * gridGap;
		
		g.setColor(this.brush);
		
		int up = 7 * increment;
		int height = gridGap * this.getGridSize().height;
		int center = height / 2;
		
		g.drawLine(tripple, gridGap, tripple, height - gridGap);
		g.drawLine(gridGap, up, gridGap, height - up);
		
		g.drawLine(tripple, gridGap, gridGap, up);
		g.drawLine(tripple, height - gridGap, gridGap, height - up);
		
		up = 2 * gridGap;
		
		if(this.requiredSelect == 1)
			g.drawLine(up, this.getSize().height - textOffset, up, this.getSize().height);
		else
			g.fillRect(up - 1, this.getSize().height - textOffset, 3, textOffset);
		
		g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));			
		
		int offset = 0;
		int thickness = 1;
		
		if(this.busSize != 1)
		{
			offset = -1;
			thickness = 3;
		}
		
		g.fillRect(0, center + offset, gridGap, thickness);
		
		textOffset = tripple - increment;
		FontMetrics fm = g.getFontMetrics();
		String toDraw;
		
		for(height = 0; height < middle; height++)
		{
			toDraw = Integer.toString(this.inputSize - 1 - height);
			g.drawString(toDraw, textOffset - fm.stringWidth(toDraw), up + increment);
			g.fillRect(tripple, up + offset, gridGap, thickness);
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			toDraw = Integer.toString(this.inputSize - 1 - height);
			g.drawString(toDraw, textOffset - fm.stringWidth(toDraw), up + increment);
			g.fillRect(tripple, up + offset, gridGap, thickness);
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(this.inputSize - 1 - height);
				g.drawString(toDraw, textOffset - fm.stringWidth(toDraw), up + increment);
				g.fillRect(tripple, up + offset, gridGap, thickness);
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(this.inputSize - 1 - height);
				g.drawString(toDraw, textOffset - fm.stringWidth(toDraw), up + increment);
				g.fillRect(tripple, up + offset, gridGap, thickness);
				up = up + gridGap;
			}
		}
	}
	
	protected void paintNormal_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int middle = this.inputSize / 2;
		int textOffset = gridGap + increment;
		
		g.setColor(this.brush);
		
		int up = 7 * increment;
		int height = gridGap * this.getGridSize().width;
		int center = 3 * gridGap;
		
		g.drawLine(gridGap, gridGap, height - gridGap, gridGap);
		g.drawLine(up, center, height - up, center);
		
		g.drawLine(gridGap, gridGap, up, center);
		g.drawLine(height - gridGap, gridGap, height - up, center);
		
		up = 2 * gridGap;
		
		if(this.requiredSelect == 1)
			g.drawLine(this.getSize().width - textOffset, up, this.getSize().width, up);
		else
			g.fillRect(this.getSize().width - textOffset, up - 1, textOffset, 3);
		
		
		center = height / 2;
		g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));			
		
		int offset = 0;
		int thickness = 1;
		
		if(this.busSize != 1)
		{
			offset = -1;
			thickness = 3;
		}
		
		g.fillRect(center + offset, 3 * gridGap, thickness, gridGap);
		
		FontMetrics fm = g.getFontMetrics();
		String toDraw;
		textOffset = textOffset + fm.getAscent();
		
		for(height = 0; height < middle; height++)
		{
			toDraw = Integer.toString(this.inputSize - 1 - height);
			g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
			g.fillRect(up + offset, 0, thickness, gridGap);
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			toDraw = Integer.toString(this.inputSize - 1 - height);
			g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
			g.fillRect(up + offset, 0, thickness, gridGap);
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(this.inputSize - 1 - height);
				g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
				g.fillRect(up + offset, 0, thickness, gridGap);
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(this.inputSize - 1 - height);
				g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
				g.fillRect(up + offset, 0, thickness, gridGap);
				up = up + gridGap;
			}
		}
		
	}
	
	protected void paintFlipped_0(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int middle = this.inputSize / 2;
		int textOffset = gridGap + increment;
		
		g.setColor(this.brush);
		
		int up = 7 * increment;
		int height = gridGap * this.getGridSize().height;
		int center = 3 * gridGap;
		
		g.drawLine(gridGap, gridGap, gridGap, height - gridGap);
		g.drawLine(center, up, center, height - up);
		
		g.drawLine(gridGap, gridGap, center, up);
		g.drawLine(gridGap, height - gridGap, center, height - up);
		
		up = 2 * gridGap;
		
		if(this.requiredSelect == 1)
			g.drawLine(up, this.getSize().height - textOffset, up, this.getSize().height);
		else
			g.fillRect(up - 1, this.getSize().height - textOffset, 3, textOffset);
		
		
		center = height / 2;
		g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));			
		
		int offset = 0;
		int thickness = 1;
		//int tripple = 3 * gridGap;
		
		if(this.busSize != 1)
		{
			offset = -1;
			thickness = 3;
		}
		
		g.fillRect(3 * gridGap, center + offset, gridGap, thickness);
		
		for(height = 0; height < middle; height++)
		{
			g.drawString(Integer.toString(this.inputSize - 1 - height), textOffset, up + increment);
			g.fillRect(0, up + offset, gridGap, thickness);
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			g.drawString(Integer.toString(this.inputSize - 1 - height), textOffset, up + increment);
			g.fillRect(0, up + offset, gridGap, thickness);
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				g.drawString(Integer.toString(this.inputSize - 1 - height), textOffset, up + increment);
				g.fillRect(0, up + offset, gridGap, thickness);
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				g.drawString(Integer.toString(this.inputSize - 1 - height), textOffset, up + increment);
				g.fillRect(0, up + offset, gridGap, thickness);
				up = up + gridGap;
			}
		}
	}
	
	protected void paintFlipped_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int middle = this.inputSize / 2;
		int textOffset = gridGap + increment;
		
		g.setColor(this.brush);
		
		int up = 7 * increment;
		int height = gridGap * this.getGridSize().width;
		int center = 3 * gridGap;
		
		g.drawLine(gridGap, gridGap, height - gridGap, gridGap);
		g.drawLine(up, center, height - up, center);
		
		g.drawLine(gridGap, gridGap, up, center);
		g.drawLine(height - gridGap, gridGap, height - up, center);
		
		up = 2 * gridGap;
		
		if(this.requiredSelect == 1)
			g.drawLine(0, up, textOffset, up);
		else
			g.fillRect(0, up - 1, textOffset, 3);
		
		
		center = height / 2;
		g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));			
		
		int offset = 0;
		int thickness = 1;
		
		if(this.busSize != 1)
		{
			offset = -1;
			thickness = 3;
		}
		
		g.fillRect(center + offset, 3 * gridGap, thickness, gridGap);
		
		FontMetrics fm = g.getFontMetrics();
		String toDraw;
		textOffset = textOffset + fm.getAscent();
		
		for(height = 0; height < middle; height++)
		{
			toDraw = Integer.toString(height);
			g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
			g.fillRect(up + offset, 0, thickness, gridGap);
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			toDraw = Integer.toString(height);
			g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
			g.fillRect(up + offset, 0, thickness, gridGap);
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(height);
				g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
				g.fillRect(up + offset, 0, thickness, gridGap);
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(height);
				g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
				g.fillRect(up + offset, 0, thickness, gridGap);
				up = up + gridGap;
			}
		}
	}
	
	protected void paintFlipped_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int middle = this.inputSize / 2;
		int textOffset = gridGap + increment;
		int tripple = 3 * gridGap;
		
		g.setColor(this.brush);
		
		int up = 7 * increment;
		int height = gridGap * this.getGridSize().height;
		int center = height / 2;
		
		g.drawLine(tripple, gridGap, tripple, height - gridGap);
		g.drawLine(gridGap, up, gridGap, height - up);
		
		g.drawLine(tripple, gridGap, gridGap, up);
		g.drawLine(tripple, height - gridGap, gridGap, height - up);
		
		up = 2 * gridGap;
		
		if(this.requiredSelect == 1)
			g.drawLine(up, 0, up, textOffset);
		else
			g.fillRect(up - 1, 0, 3, textOffset);
		
		g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));			
		
		int offset = 0;
		int thickness = 1;
		
		if(this.busSize != 1)
		{
			offset = -1;
			thickness = 3;
		}
		
		g.fillRect(0, center + offset, gridGap, thickness);
		
		textOffset = tripple - increment;
		FontMetrics fm = g.getFontMetrics();
		String toDraw;
		
		for(height = 0; height < middle; height++)
		{
			toDraw = Integer.toString(height);
			g.drawString(toDraw, textOffset - fm.stringWidth(toDraw), up + increment);
			g.fillRect(tripple, up + offset, gridGap, thickness);
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			toDraw = Integer.toString(height);
			g.drawString(toDraw, textOffset - fm.stringWidth(toDraw), up + increment);
			g.fillRect(tripple, up + offset, gridGap, thickness);
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(height);
				g.drawString(toDraw, textOffset - fm.stringWidth(toDraw), up + increment);
				g.fillRect(tripple, up + offset, gridGap, thickness);
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(height);
				g.drawString(toDraw, textOffset - fm.stringWidth(toDraw), up + increment);
				g.fillRect(tripple, up + offset, gridGap, thickness);
				up = up + gridGap;
			}
		}
	}
	
	protected void paintFlipped_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int middle = this.inputSize / 2;
		int textOffset = gridGap + increment;
		int tripple = 3 * gridGap;
		
		g.setColor(this.brush);
		
		int up = 7 * increment;
		int height = gridGap * this.getGridSize().width;
		int center = height / 2;
		
		g.drawLine(gridGap, tripple, height - gridGap, tripple);
		g.drawLine(up, gridGap, height - up, gridGap);
		
		g.drawLine(gridGap, tripple, up, gridGap);
		g.drawLine(height - gridGap, tripple, height - up, gridGap);
		
		up = 2 * gridGap;
		
		if(this.requiredSelect == 1)
			g.drawLine(this.getSize().width - textOffset, up, this.getSize().width, up);
		else
			g.fillRect(this.getSize().width - textOffset, up - 1, textOffset, 3);
		
		g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));			
		
		int offset = 0;
		int thickness = 1;
		
		if(this.busSize != 1)
		{
			offset = -1;
			thickness = 3;
		}
		
		g.fillRect(center + offset, 0, thickness, gridGap);
		
		textOffset = tripple - increment;
		FontMetrics fm = g.getFontMetrics();
		String toDraw;
		
		for(height = 0; height < middle; height++)
		{
			toDraw = Integer.toString(this.inputSize - 1 - height);
			g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
			g.fillRect(up + offset, tripple, thickness, gridGap);
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			toDraw = Integer.toString(this.inputSize - 1 - height);
			g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
			g.fillRect(up + offset, tripple, thickness, gridGap);
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(this.inputSize - 1 - height);
				g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
				g.fillRect(up + offset, tripple, thickness, gridGap);
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				toDraw = Integer.toString(this.inputSize - 1 - height);
				g.drawString(toDraw, up - fm.stringWidth(toDraw) / 2, textOffset);
				g.fillRect(up + offset, tripple, thickness, gridGap);
				up = up + gridGap;
			}
		}
	}
	
	
	
	
	protected boolean canDropNormal_0()
	{
		boolean result = true;
		int middle = this.inputSize / 2;
		int loop;
		
		// check inputs
		for(loop = 0; (loop < middle) && result; loop++)
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + loop, this.busSize);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + middle, this.busSize);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + loop, this.busSize);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 3 + loop, this.busSize);
			}
		}
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2, this.gridLocation.y, this.requiredSelect);
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 2 + middle, this.busSize);
		
		return result;
	}
	
	protected boolean canDropNormal_90()
	{
		boolean result = true;
		int middle = this.inputSize / 2;
		int loop;
		
		// check inputs
		for(loop = 0; (loop < middle) && result; loop++)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y + 4, this.busSize);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + middle, this.gridLocation.y + 4, this.busSize);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y + 4, this.busSize);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 3 + loop, this.gridLocation.y + 4, this.busSize);
			}
		}
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2, this.requiredSelect);
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + middle, this.gridLocation.y, this.busSize);
		
		return result;
	}
	
	protected boolean canDropNormal_180()
	{
		boolean result = true;
		int middle = this.inputSize / 2;
		int loop;
		
		// check inputs
		for(loop = 0; (loop < middle) && result; loop++)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 2 + loop, this.busSize);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 2 + middle, this.busSize);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 2 + loop, this.busSize);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 3 + loop, this.busSize);
			}
		}
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2, this.gridLocation.y + this.gridSize.height, this.requiredSelect);
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + middle, this.busSize);
		
		return result;
	}
	
	protected boolean canDropNormal_270()
	{
		boolean result = true;
		int middle = this.inputSize / 2;
		int loop;
		
		// check inputs
		for(loop = 0; (loop < middle) && result; loop++)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y, this.busSize);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + middle, this.gridLocation.y, this.busSize);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y, this.busSize);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 3 + loop, this.gridLocation.y, this.busSize);
			}
		}
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width, this.gridLocation.y + 2, this.requiredSelect);
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + middle, this.gridLocation.y + 4, this.busSize);
		
		return result;
	}
	
	protected boolean canDropFlipped_0()
	{
		boolean result = true;
		int middle = this.inputSize / 2;
		int loop;
		
		// check inputs
		for(loop = 0; (loop < middle) && result; loop++)
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + loop, this.busSize);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + middle, this.busSize);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + loop, this.busSize);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 3 + loop, this.busSize);
			}
		}
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2, this.gridLocation.y + this.gridSize.height, this.requiredSelect);
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 2 + middle, this.busSize);
		
		return result;
	}
	
	protected boolean canDropFlipped_90()
	{
		boolean result = true;
		int middle = this.inputSize / 2;
		int loop;
		
		// check inputs
		for(loop = 0; (loop < middle) && result; loop++)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y, this.busSize);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + middle, this.gridLocation.y, this.busSize);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y, this.busSize);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 3 + loop, this.gridLocation.y, this.busSize);
			}
		}
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2, this.requiredSelect);
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + middle, this.gridLocation.y + 4, this.busSize);
		
		return result;
	}
	
	protected boolean canDropFlipped_180()
	{
		boolean result = true;
		int middle = this.inputSize / 2;
		int loop;
		
		// check inputs
		for(loop = 0; (loop < middle) && result; loop++)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 2 + loop, this.busSize);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 2 + middle, this.busSize);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 2 + loop, this.busSize);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 3 + loop, this.busSize);
			}
		}
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2, this.gridLocation.y, this.requiredSelect);
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + middle, this.busSize);
		
		return result;
	}
	
	protected boolean canDropFlipped_270()
	{
		boolean result = true;
		int middle = this.inputSize / 2;
		int loop;
		
		// check inputs
		for(loop = 0; (loop < middle) && result; loop++)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y + 4, this.busSize);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + middle, this.gridLocation.y + 4, this.busSize);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y + 4, this.busSize);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 3 + loop, this.gridLocation.y + 4, this.busSize);
			}
		}
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width, this.gridLocation.y + 2, this.requiredSelect);
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + middle, this.gridLocation.y, this.busSize);
		
		return result;
	}
	
	
	
	
	protected void dropedNormal_0()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		this.input.setSize(this.inputSize);
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + loop, this.busSize));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(middle, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + middle, this.busSize));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + loop, this.busSize));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 3 + loop, this.busSize));
		}
		
		this.select = Wrapper.setPinAt(this.gridLocation.x + 2, this.gridLocation.y, this.requiredSelect);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 2 + middle, this.busSize);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_90()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		this.input.setSize(this.inputSize);
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y + 4, this.busSize));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(middle, Wrapper.setPinAt(this.gridLocation.x + 2 + middle, this.gridLocation.y + 4, this.busSize));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y + 4, this.busSize));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 3 + loop, this.gridLocation.y + 4, this.busSize));
		}
		
		this.select = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2, this.requiredSelect);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 2 + middle, this.gridLocation.y, this.busSize);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_180()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		this.input.setSize(this.inputSize);
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 2 + loop, this.busSize));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(this.inputSize - 1 - middle, Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 2 + middle, this.busSize));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 2 + loop, this.busSize));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 3 + loop, this.busSize));
		}
		
		this.select = Wrapper.setPinAt(this.gridLocation.x + 2, this.gridLocation.y + this.gridSize.height, this.requiredSelect);
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + middle, this.busSize);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_270()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		this.input.setSize(this.inputSize);
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y, this.busSize));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(this.inputSize - 1 - middle, Wrapper.setPinAt(this.gridLocation.x + 2 + middle, this.gridLocation.y, this.busSize));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y, this.busSize));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 3 + loop, this.gridLocation.y, this.busSize));
		}
		
		this.select = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width, this.gridLocation.y + 2, this.requiredSelect);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 2 + middle, this.gridLocation.y + 4, this.busSize);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedFlipped_0()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		this.input.setSize(this.inputSize);
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + loop, this.busSize));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(this.inputSize - 1 - middle, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + middle, this.busSize));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + loop, this.busSize));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 3 + loop, this.busSize));
		}
		
		this.select = Wrapper.setPinAt(this.gridLocation.x + 2, this.gridLocation.y + this.gridSize.height, this.requiredSelect);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 2 + middle, this.busSize);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedFlipped_90()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		this.input.setSize(this.inputSize);
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y, this.busSize));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(middle, Wrapper.setPinAt(this.gridLocation.x + 2 + middle, this.gridLocation.y, this.busSize));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y, this.busSize));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 3 + loop, this.gridLocation.y, this.busSize));
		}
		
		this.select = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2, this.requiredSelect);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 2 + middle, this.gridLocation.y + 4, this.busSize);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedFlipped_180()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		this.input.setSize(this.inputSize);
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 2 + loop, this.busSize));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(middle, Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 2 + middle, this.busSize));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 2 + loop, this.busSize));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 3 + loop, this.busSize));
		}
		
		this.select = Wrapper.setPinAt(this.gridLocation.x + 2, this.gridLocation.y, this.requiredSelect);
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + middle, this.busSize);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedFlipped_270()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		this.input.setSize(this.inputSize);
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y + 4, this.busSize));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(this.inputSize - 1 - middle, Wrapper.setPinAt(this.gridLocation.x + 2 + middle, this.gridLocation.y + 4, this.busSize));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y + 4, this.busSize));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 3 + loop, this.gridLocation.y + 4, this.busSize));
		}
		
		this.select = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width, this.gridLocation.y + 2, this.requiredSelect);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 2 + middle, this.gridLocation.y, this.busSize);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
		
	
	
	
	protected void adjustToChanges()
	{
		if(this.angle % 180 == 0)
			this.setGridSize(4, 2 * (this.inputSize / 2) + 4);
		else
			this.setGridSize(2 * (this.inputSize / 2) + 4, 4);
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	private int oldBusSize = 0;
	private int oldSelectSize;
	
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new MuxProperties(this.busSize, this.requiredSelect, this.delay));
	}
		
	public void respondToChanges(Component property)
	{
		MuxProperties dp = (MuxProperties)property;
		
		this.delay = dp.getDelay();
		
		if(this.oldBusSize == 0)
		{
			this.oldBusSize = this.busSize;
			this.oldSelectSize = this.requiredSelect;
		}
		
		CentralPanel.ACTIVE_GRID.eraseComponent(this);
		this.setBusSize(dp.getBusSize());
		this.setInputSize((int)Math.pow(2, dp.getInputSize()));
		CentralPanel.ACTIVE_GRID.paintComponent(this);
		
	}
	
	public void restoreOriginalProperties()
	{
		if(this.oldBusSize != 0)
		{
			this.setBusSize(this.oldBusSize);
			this.oldBusSize = 0;
			this.setInputSize((int)Math.pow(2, this.oldSelectSize));
		}
	}
}