package sim.lib.gates;

import java.awt.*;

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

import sim.MainWindow;

public class GateAND extends MultiInputGate
{
/* ==================================================================
	Creation Part
	================================================================= */
	private static Image ICON = GuiFileLink.getImage("sim/lib/gates/AndIcon.gif");
	
	public Image getIcon()
	{
		return GateAND.ICON;
	}
	
	public String getBubbleHelp()
	{
		return "AND gate";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	public GateAND()
	{
		super();
	}
	
	protected Wrapper getCopy()
	{
		GateAND result = new GateAND();
		
		result.setInputSize(this.inputSize);
		result.setOutputInvert(this.invertOutput);
		
		for(int loop = 0; loop < this.inputSize; loop++)
			result.setInputInvert(this.invertInputs[loop], loop);
		
		result.changeDelay(this.delay);
		
		return result;
	}
	
/* ==================================================================
	Simulation part
	================================================================= */
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer)
	{
		double time = this.delay + currentTime;
		
		boolean foundFalse = false;
		boolean foundUndefined = false;
		
		for(int loopNumber = 0; (loopNumber < this.inputSize) && (!foundFalse); loopNumber++)
			if(currentInputs[loopNumber].isUndefined())
				foundUndefined = true;
			else if(!(currentInputs[loopNumber].getValue() ^ this.invertInputs[loopNumber]))
				foundFalse = true;
		
		if(foundFalse)
			peer.setOutputPinValue(0, this.invertOutput, time);
		else if(foundUndefined)
			peer.setOutputPinUndefined(0, time);
		else
			peer.setOutputPinValue(0, !this.invertOutput, time);
	}
	
/* ==================================================================
	Rotation abd Flipping Part
	================================================================= */
	
	protected void paintNormal_0(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int center = gridGap * this.gridSize.height / 2;
		int middle = this.inputSize / 2;
		int length = 7 * increment;
		int diameter = 2 * increment;
		
		int up = 3 * increment;
		int height = 17 * increment;
		
		int gWidth = 6 * gridGap;
		int gHeight = this.gridSize.height * gridGap;
		
		int cornerX = 10 * increment;
		
		g.setColor(this.brush);
		g.drawLine(length, up, length, gHeight - up);
		
		if(this.invertOutput)
		{
			g.drawLine(height + 1 + diameter, center, gWidth, center);
			g.drawOval(height + 1, center - increment, diameter, diameter);
		}
		else
			g.drawLine(height, center, gWidth, center);
		
		height = 3 * gridGap;
		
		up = center + gridGap + increment;
		g.drawLine(length, up, height, up);
		
		up = center - gridGap - increment;
		g.drawLine(length, up, height, up);
		
		Shape old = g.getClip();
		g.setClip(height, 0, gWidth - length, gHeight);
		g.drawOval(length, up, cornerX, cornerX);
		g.setClip(old);
		
		up = gridGap;
		cornerX = length - diameter - 1;
		
		for(height = 0; height < middle; height++)
		{
			if(this.invertInputs[height])
			{
				g.drawLine(0, up, cornerX, up);
				g.drawOval(cornerX, up - increment, diameter, diameter);
			}
			else
				g.drawLine(0, up, length, up);
			
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			if(this.invertInputs[middle])
			{
				g.drawLine(0, up, cornerX, up);
				g.drawOval(cornerX, up - increment, diameter, diameter);
			}
			else
				g.drawLine(0, up, length, up);
			
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				if(this.invertInputs[height])
				{
					g.drawLine(0, up, cornerX, up);
					g.drawOval(cornerX, up - increment, diameter, diameter);
				}
				else
					g.drawLine(0, up, length, up);
				
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				if(this.invertInputs[height])
				{
					g.drawLine(0, up, cornerX, up);
					g.drawOval(cornerX, up - increment, diameter, diameter);
				}
				else
					g.drawLine(0, up, length, up);
				
				up = up + gridGap;
			}
		}
	}
	
	protected void paintNormal_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int center = gridGap * this.gridSize.width / 2;
		int middle = this.inputSize / 2;
		int length = 7 * increment;
		int diameter = 2 * increment;
		
		int up = 3 * increment;
		int height = 17 * increment;
		
		int gWidth = 6 * gridGap;
		int gHeight = this.gridSize.width * gridGap;
		
		int cornerX = 3 * gridGap;
		
		g.setColor(this.brush);
		g.drawLine(up, height, gHeight - up, height);
		
		if(this.invertOutput)
		{
			g.drawLine(center, gridGap + increment - 1, center, 0);
			g.drawOval(center - increment, gridGap + increment - 1, diameter, diameter);
		}
		else
			g.drawLine(center, length, center, 0);
		
		up = center + gridGap + increment;
		g.drawLine(up, height, up, cornerX);
		
		up = center - gridGap - increment;
		g.drawLine(up, height, up, cornerX);
		
		cornerX = 10 * increment;
		
		Shape old = g.getClip();
		g.setClip(0, - gridGap - increment, gHeight, height);
		g.drawOval(up, length, cornerX, cornerX);
		g.setClip(old);
		
		up = gridGap;
		cornerX = height + diameter + 1;
		gWidth = gWidth - 1;
		
		for(gHeight = 0; gHeight < middle; gHeight++)
		{
			if(this.invertInputs[gHeight])
			{
				g.drawLine(up, gWidth, up, cornerX);
				g.drawOval(up - increment, height + 1, diameter, diameter);
			}
			else
				g.drawLine(up, gWidth, up, height);
			
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			if(this.invertInputs[middle])
			{
				g.drawLine(up, gWidth, up, cornerX);
				g.drawOval(up - increment, height + 1, diameter, diameter);
			}
			else
				g.drawLine(up, gWidth, up, height);
			
			up = up + gridGap;
			
			for(gHeight = middle + 1; gHeight < this.inputSize; gHeight++)
			{
				if(this.invertInputs[gHeight])
				{
					g.drawLine(up, gWidth, up, cornerX);
					g.drawOval(up - increment, height + 1, diameter, diameter);
				}
				else
					g.drawLine(up, gWidth, up, height);
				
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(gHeight = middle; gHeight < this.inputSize; gHeight++)
			{
				if(this.invertInputs[gHeight])
				{
					g.drawLine(up, gWidth, up, cornerX);
					g.drawOval(up - increment, height + 1, diameter, diameter);
				}
				else
					g.drawLine(up, gWidth, up, height);
				
				up = up + gridGap;
			}
		}
	}
	
	protected void paintNormal_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int center = gridGap * this.gridSize.height / 2;
		int middle = this.inputSize / 2;
		int length = 7 * increment;
		int diameter = 2 * increment;
		
		int up = 3 * increment;
		int height = 17 * increment;
		
		int gWidth = 6 * gridGap;
		int gHeight = this.gridSize.height * gridGap;
		
		int cornerX = 3 * gridGap;
		
		g.setColor(this.brush);
		g.drawLine(height, up, height, gHeight - up);
		
		if(this.invertOutput)
		{
			g.drawLine(gridGap + increment - 1, center, 0, center);
			g.drawOval(gridGap + increment - 1, center - increment, diameter, diameter);
		}
		else
			g.drawLine(length, center, 0, center);
		
		up = center + gridGap + increment;
		g.drawLine(height, up, cornerX, up);
		
		up = center - gridGap - increment;
		g.drawLine(height, up, cornerX, up);
		
		cornerX = 10 * increment;
		
		Shape old = g.getClip();
		g.setClip(- gridGap - increment, 0, height, gHeight);
		g.drawOval(length, up, cornerX, cornerX);
		g.setClip(old);
		
		up = gridGap;
		cornerX = height + diameter + 1;
		gWidth = gWidth - 1;
		
		for(gHeight = 0; gHeight < middle; gHeight++)
		{
			if(this.invertInputs[this.inputSize - 1 - gHeight])
			{
				g.drawLine(gWidth, up, cornerX, up);
				g.drawOval(height + 1, up - increment, diameter, diameter);
			}
			else
				g.drawLine(gWidth, up, height, up);
			
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			if(this.invertInputs[this.inputSize - 1 - middle])
			{
				g.drawLine(gWidth, up, cornerX, up);
				g.drawOval(height + 1, up - increment, diameter, diameter);
			}
			else
				g.drawLine(gWidth, up, height, up);
			
			up = up + gridGap;
			
			for(gHeight = middle + 1; gHeight < this.inputSize; gHeight++)
			{
				if(this.invertInputs[this.inputSize - 1 - gHeight])
				{
					g.drawLine(gWidth, up, cornerX, up);
					g.drawOval(height + 1, up - increment, diameter, diameter);
				}
				else
					g.drawLine(gWidth, up, height, up);
				
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(gHeight = middle; gHeight < this.inputSize; gHeight++)
			{
				if(this.invertInputs[this.inputSize - 1 - gHeight])
				{
					g.drawLine(gWidth, up, cornerX, up);
					g.drawOval(height + 1, up - increment, diameter, diameter);
				}
				else
					g.drawLine(gWidth, up, height, up);
				
				up = up + gridGap;
			}
		}
	}
	
	protected void paintNormal_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int center = gridGap * this.gridSize.width / 2;
		int middle = this.inputSize / 2;
		int length = 7 * increment;
		int diameter = 2 * increment;
		
		int up = 3 * increment;
		int height = 17 * increment;
		
		int gWidth = 6 * gridGap;
		int gHeight = this.gridSize.width * gridGap;
		
		int cornerX = 10 * increment;
		
		g.setColor(this.brush);
		
		g.drawLine(up, length, gHeight - up, length);
		
		if(this.invertOutput)
		{
			g.drawLine(center, height + 1 + diameter, center, gWidth);
			g.drawOval(center - increment, height + 1, diameter, diameter);
		}
		else
			g.drawLine(center, height, center, gWidth);
		
		height = 3 * gridGap;
		
		up = center + gridGap + increment;
		g.drawLine(up, length, up, height);
		
		up = center - gridGap - increment;
		g.drawLine(up, length, up, height);
		
		Shape old = g.getClip();
		g.setClip(0, height, gHeight, gWidth - length);
		g.drawOval(up, length, cornerX, cornerX);
		g.setClip(old);

		up = gridGap;
		cornerX = length - diameter - 1;
		
		for(height = 0; height < middle; height++)
		{
			if(this.invertInputs[this.inputSize - 1 - height])
			{
				g.drawLine(up, 0, up, cornerX);
				g.drawOval(up - increment, cornerX, diameter, diameter);
			}
			else
				g.drawLine(up, 0, up, length);
			
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			if(this.invertInputs[this.inputSize - 1 - middle])
			{
				g.drawLine(up, 0, up, cornerX);
				g.drawOval(up - increment, cornerX, diameter, diameter);
			}
			else
				g.drawLine(up, 0, up, length);
			
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				if(this.invertInputs[this.inputSize - 1 - height])
				{
					g.drawLine(up, 0, up, cornerX);
					g.drawOval(up - increment, cornerX, diameter, diameter);
				}
				else
					g.drawLine(up, 0, up, length);
				
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				if(this.invertInputs[this.inputSize - 1 - height])
				{
					g.drawLine(up, 0, up, cornerX);
					g.drawOval(up - increment, cornerX, diameter, diameter);
				}
				else
					g.drawLine(up, 0, up, length);
				
				up = up + gridGap;
			}
		}
	}
	
	protected void paintFlipped_0(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int center = gridGap * this.gridSize.height / 2;
		int middle = this.inputSize / 2;
		int length = 7 * increment;
		int diameter = 2 * increment;
		
		int up = 3 * increment;
		int height = 17 * increment;
		
		int gWidth = 6 * gridGap;
		int gHeight = this.gridSize.height * gridGap;
		
		int cornerX = 10 * increment;
		
		g.setColor(this.brush);
		g.drawLine(length, up, length, gHeight - up);
		
		if(this.invertOutput)
		{
			g.drawLine(height + 1 + diameter, center, gWidth, center);
			g.drawOval(height + 1, center - increment, diameter, diameter);
		}
		else
			g.drawLine(height, center, gWidth, center);
		
		height = 3 * gridGap;
		
		up = center + gridGap + increment;
		g.drawLine(length, up, height, up);
		
		up = center - gridGap - increment;
		g.drawLine(length, up, height, up);
		
		Shape old = g.getClip();
		g.setClip(height, 0, gWidth - length, gHeight);
		g.drawOval(length, up, cornerX, cornerX);
		g.setClip(old);
		
		
		up = gridGap;
		cornerX = length - diameter - 1;
		
		for(height = 0; height < middle; height++)
		{
			if(this.invertInputs[this.inputSize - 1 - height])
			{
				g.drawLine(0, up, cornerX, up);
				g.drawOval(cornerX, up - increment, diameter, diameter);
			}
			else
				g.drawLine(0, up, length, up);
			
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			if(this.invertInputs[this.inputSize - 1 - middle])
			{
				g.drawLine(0, up, cornerX, up);
				g.drawOval(cornerX, up - increment, diameter, diameter);
			}
			else
				g.drawLine(0, up, length, up);
			
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				if(this.invertInputs[this.inputSize - 1 - height])
				{
					g.drawLine(0, up, cornerX, up);
					g.drawOval(cornerX, up - increment, diameter, diameter);
				}
				else
					g.drawLine(0, up, length, up);
				
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				if(this.invertInputs[this.inputSize - 1 - height])
				{
					g.drawLine(0, up, cornerX, up);
					g.drawOval(cornerX, up - increment, diameter, diameter);
				}
				else
					g.drawLine(0, up, length, up);
				
				up = up + gridGap;
			}
		}
	}
	
	protected void paintFlipped_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int center = gridGap * this.gridSize.width / 2;
		int middle = this.inputSize / 2;
		int length = 7 * increment;
		int diameter = 2 * increment;
		
		int up = 3 * increment;
		int height = 17 * increment;
		
		int gWidth = 6 * gridGap;
		int gHeight = this.gridSize.width * gridGap;
		
		int cornerX = 10 * increment;
		
		g.setColor(this.brush);
		
		g.drawLine(up, length, gHeight - up, length);
		
		if(this.invertOutput)
		{
			g.drawLine(center, height + 1 + diameter, center, gWidth);
			g.drawOval(center - increment, height + 1, diameter, diameter);
		}
		else
			g.drawLine(center, height, center, gWidth);
		
		height = 3 * gridGap;
		
		up = center + gridGap + increment;
		g.drawLine(up, length, up, height);
		
		up = center - gridGap - increment;
		g.drawLine(up, length, up, height);
		
		Shape old = g.getClip();
		g.setClip(0, height, gHeight, gWidth - length);
		g.drawOval(up, length, cornerX, cornerX);
		g.setClip(old);

		up = gridGap;
		cornerX = length - diameter - 1;
		
		for(height = 0; height < middle; height++)
		{
			if(this.invertInputs[height])
			{
				g.drawLine(up, 0, up, cornerX);
				g.drawOval(up - increment, cornerX, diameter, diameter);
			}
			else
				g.drawLine(up, 0, up, length);
			
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			if(this.invertInputs[middle])
			{
				g.drawLine(up, 0, up, cornerX);
				g.drawOval(up - increment, cornerX, diameter, diameter);
			}
			else
				g.drawLine(up, 0, up, length);
			
			up = up + gridGap;
			
			for(height = middle + 1; height < this.inputSize; height++)
			{
				if(this.invertInputs[height])
				{
					g.drawLine(up, 0, up, cornerX);
					g.drawOval(up - increment, cornerX, diameter, diameter);
				}
				else
					g.drawLine(up, 0, up, length);
				
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(height = middle; height < this.inputSize; height++)
			{
				if(this.invertInputs[height])
				{
					g.drawLine(up, 0, up, cornerX);
					g.drawOval(up - increment, cornerX, diameter, diameter);
				}
				else
					g.drawLine(up, 0, up, length);
				
				up = up + gridGap;
			}
		}
	}
	
	protected void paintFlipped_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int center = gridGap * this.gridSize.height / 2;
		int middle = this.inputSize / 2;
		int length = 7 * increment;
		int diameter = 2 * increment;
		
		int up = 3 * increment;
		int height = 17 * increment;
		
		int gWidth = 6 * gridGap;
		int gHeight = this.gridSize.height * gridGap;
		
		int cornerX = 3 * gridGap;
		
		g.setColor(this.brush);
		g.drawLine(height, up, height, gHeight - up);
		
		if(this.invertOutput)
		{
			g.drawLine(gridGap + increment - 1, center, 0, center);
			g.drawOval(gridGap + increment - 1, center - increment, diameter, diameter);
		}
		else
			g.drawLine(length, center, 0, center);
		
		up = center + gridGap + increment;
		g.drawLine(height, up, cornerX, up);
		
		up = center - gridGap - increment;
		g.drawLine(height, up, cornerX, up);
		
		cornerX = 10 * increment;
		
		Shape old = g.getClip();
		g.setClip(- gridGap - increment, 0, height, gHeight);
		g.drawOval(length, up, cornerX, cornerX);
		g.setClip(old);
		
		up = gridGap;
		cornerX = height + diameter + 1;
		gWidth = gWidth - 1;
		
		for(gHeight = 0; gHeight < middle; gHeight++)
		{
			if(this.invertInputs[gHeight])
			{
				g.drawLine(gWidth, up, cornerX, up);
				g.drawOval(height + 1, up - increment, diameter, diameter);
			}
			else
				g.drawLine(gWidth, up, height, up);
			
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			if(this.invertInputs[middle])
			{
				g.drawLine(gWidth, up, cornerX, up);
				g.drawOval(height + 1, up - increment, diameter, diameter);
			}
			else
				g.drawLine(gWidth, up, height, up);
			
			up = up + gridGap;
			
			for(gHeight = middle + 1; gHeight < this.inputSize; gHeight++)
			{
				if(this.invertInputs[gHeight])
				{
					g.drawLine(gWidth, up, cornerX, up);
					g.drawOval(height + 1, up - increment, diameter, diameter);
				}
				else
					g.drawLine(gWidth, up, height, up);
				
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(gHeight = middle; gHeight < this.inputSize; gHeight++)
			{
				if(this.invertInputs[gHeight])
				{
					g.drawLine(gWidth, up, cornerX, up);
					g.drawOval(height + 1, up - increment, diameter, diameter);
				}
				else
					g.drawLine(gWidth, up, height, up);
				
				up = up + gridGap;
			}
		}
	}
	
	protected void paintFlipped_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int center = gridGap * this.gridSize.width / 2;
		int middle = this.inputSize / 2;
		int length = 7 * increment;
		int diameter = 2 * increment;
		
		int up = 3 * increment;
		int height = 17 * increment;
		
		int gWidth = 6 * gridGap;
		int gHeight = this.gridSize.width * gridGap;
		
		int cornerX = 3 * gridGap;
		
		g.setColor(this.brush);
		g.drawLine(up, height, gHeight - up, height);
		
		if(this.invertOutput)
		{
			g.drawLine(center, gridGap + increment - 1, center, 0);
			g.drawOval(center - increment, gridGap + increment - 1, diameter, diameter);
		}
		else
			g.drawLine(center, length, center, 0);
		
		up = center + gridGap + increment;
		g.drawLine(up, height, up, cornerX);
		
		up = center - gridGap - increment;
		g.drawLine(up, height, up, cornerX);
		
		cornerX = 10 * increment;
		
		Shape old = g.getClip();
		g.setClip(0, - gridGap - increment, gHeight, height);
		g.drawOval(up, length, cornerX, cornerX);
		g.setClip(old);
		
		up = gridGap;
		cornerX = height + diameter + 1;
		gWidth = gWidth - 1;
		
		for(gHeight = 0; gHeight < middle; gHeight++)
		{
			if(this.invertInputs[this.inputSize - 1 - gHeight])
			{
				g.drawLine(up, gWidth, up, cornerX);
				g.drawOval(up - increment, height + 1, diameter, diameter);
			}
			else
				g.drawLine(up, gWidth, up, height);
			
			up = up + gridGap;
		}
		
		if((this.inputSize % 2 ) != 0)
		{
			if(this.invertInputs[this.inputSize - 1 - middle])
			{
				g.drawLine(up, gWidth, up, cornerX);
				g.drawOval(up - increment, height + 1, diameter, diameter);
			}
			else
				g.drawLine(up, gWidth, up, height);
			
			up = up + gridGap;
			
			for(gHeight = middle + 1; gHeight < this.inputSize; gHeight++)
			{
				if(this.invertInputs[this.inputSize - 1 - gHeight])
				{
					g.drawLine(up, gWidth, up, cornerX);
					g.drawOval(up - increment, height + 1, diameter, diameter);
				}
				else
					g.drawLine(up, gWidth, up, height);
				
				up = up + gridGap;
			}
		}
		else
		{
			up = up + gridGap;
			
			for(gHeight = middle; gHeight < this.inputSize; gHeight++)
			{
				if(this.invertInputs[this.inputSize - 1 - gHeight])
				{
					g.drawLine(up, gWidth, up, cornerX);
					g.drawOval(up - increment, height + 1, diameter, diameter);
				}
				else
					g.drawLine(up, gWidth, up, height);
				
				up = up + gridGap;
			}
		}
	}
}