package sim.lib.gates;

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

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

public class GateNOT extends RotatableFlippableWrapperPainted implements EngineModule
{
/* ==================================================================
	Creation Part
	================================================================= */
	private static Image ICON = GuiFileLink.getImage("sim/lib/gates/NotIcon.gif");
	
	public Image getIcon()
	{
		return GateNOT.ICON;
	}
	
	public Wrapper createWrapper()
	{
		return (new GateNOT());
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		GateNOT result = new GateNOT();
		result.setGridLocation(gridPosition);
		return result;
	}
	
	public String getBubbleHelp()
	{
		return "NOT gate";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	public GateNOT()
	{
		super();
		
		this.delay = GateNOT.default_delay;
	}
	
	public void initializeGridSize()
	{
		this.setGridSize(4, 2);
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	private Junction input = null;
	private Junction output = null;
	
	public void selected()
	{
		this.input.removePin();
		this.output.removePin();
		
		this.changeColor(Color.green);
	}
	
	public void checkAfterSelected()
	{
		Wrapper.checkPin(this.input);
		Wrapper.checkPin(this.output);
	}
/* ==================================================================
	Simulation part
	================================================================= */
	protected double delay;
	private static double default_delay = 1;
	
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer)
	{
		double time = this.delay + currentTime;
		
		if(currentInputs[0].isUndefined())
			peer.setOutputPinUndefined(0, time);
		else
			peer.setOutputPinValue(0, !currentInputs[0].getValue(), time);
	}
	
	public void createEnginePeer(EnginePeerList epl)
	{
		EnginePeer ep = new EnginePeer(1, 1, this);
		
		ep.setInputPin(0, this.input.getNodes().getItemAt(0));
		ep.setOutputPin(0, this.output.getNodes().getItemAt(0));
		
		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 (Double.toString(this.delay) + Wrapper.SEPARATOR);
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 1;
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.delay = Double.valueOf(specificParameters[0]).doubleValue();
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
/* ==================================================================
	Rotation abd Flipping Part
	================================================================= */
	
	protected void paintNormal_0(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int length = gridGap + 3 * increment / 2;
		int up = increment * 2;
		int down = up * 3;
		int corner = 2 * gridGap + increment;
		g.setColor(this.brush);
		
		g.drawLine(0, gridGap, length, gridGap);
		g.drawLine(length, up, length, down);
		
		g.drawLine(length, up, corner, gridGap);
		g.drawLine(length, down, corner, gridGap);
		
		corner = corner + 1;
		up = gridGap - increment;
		increment = 2 * increment;
		
		g.drawOval(corner, up, increment, increment);
		
		g.drawLine(corner + increment, gridGap, gridGap * 4, gridGap);
	}
	
	protected void paintNormal_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int length = 3 * (gridGap - increment / 2);
		int up = increment * 2;
		int down = up * 3;
		int corner = 2 * gridGap - increment;
		g.setColor(this.brush);
		
		g.drawLine(gridGap, 4 * gridGap, gridGap, length);
		g.drawLine(up, length, down, length);
		
		g.drawLine(up, length, gridGap, corner);
		g.drawLine(down, length, gridGap, corner);
		
		up = gridGap - increment;
		increment = 2 * increment;
		corner = corner - 1 - increment;
		
		g.drawOval(up, corner, increment, increment);
		
		g.drawLine(gridGap, corner, gridGap, 0);
	}
	
	protected void paintNormal_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int length = 3 * (gridGap - increment / 2);
		int up = increment * 2;
		int down = up * 3;
		int corner = 2 * gridGap - increment;
		g.setColor(this.brush);
		
		g.drawLine(4 * gridGap, gridGap, length, gridGap);
		g.drawLine(length, up, length, down);
		
		g.drawLine(length, up, corner, gridGap);
		g.drawLine(length, down, corner, gridGap);
		
		up = gridGap - increment;
		increment = 2 * increment;
		corner = corner - 1 - increment;
		
		g.drawOval(corner, up, increment, increment);
		
		g.drawLine(corner, gridGap, 0, gridGap);
	}
	
	protected void paintNormal_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = gridGap / 4;
		int length = gridGap + 3 * increment / 2;
		int up = increment * 2;
		int down = up * 3;
		int corner = 2 * gridGap + increment;
		g.setColor(this.brush);
		
		g.drawLine(gridGap, 0, gridGap, length);
		g.drawLine(up, length, down, length);
		
		g.drawLine(up, length, gridGap, corner);
		g.drawLine(down, length, gridGap, corner);
		
		corner = corner + 1;
		up = gridGap - increment;
		increment = 2 * increment;
		
		g.drawOval(up, corner, increment, increment);
		
		g.drawLine(gridGap, corner + increment, gridGap, gridGap * 4);
	}
	
	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()
	{
		boolean result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 1, 1);
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 4, this.gridLocation.y + 1, 1);
		
		return result;
	}
	
	protected boolean canDropNormal_90()
	{
		boolean result = Wrapper.canDropJuncion(this.gridLocation.x + 1, this.gridLocation.y, 1);
		
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 1, this.gridLocation.y + 4, 1);
		
		return result;
	}
	
	protected boolean canDropNormal_180()
	{
		return this.canDropNormal_0();
	}
	
	protected boolean canDropNormal_270()
	{
		return this.canDropNormal_90();
	}
	
	protected boolean canDropFlipped_0()
	{
		return this.canDropNormal_0();
	}
	
	protected boolean canDropFlipped_90()
	{
		return this.canDropNormal_90();
	}
	
	protected boolean canDropFlipped_180()
	{
		return this.canDropNormal_0();
	}
	
	protected boolean canDropFlipped_270()
	{
		return this.canDropNormal_90();
	}
	
	
	
	
	protected void dropedNormal_0()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1, 1);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 1, 1);
		this.changeColor(Color.black);
	}
	
	protected void dropedNormal_90()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x + 1, this.gridLocation.y + 4, 1);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 1, this.gridLocation.y, 1);
		this.changeColor(Color.black);
	}
	
	protected void dropedNormal_180()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x + 4, this.gridLocation.y + 1, 1);
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1, 1);
		this.changeColor(Color.black);
	}
	
	protected void dropedNormal_270()
	{
		this.input = Wrapper.setPinAt(this.gridLocation.x + 1, this.gridLocation.y, 1);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 1, this.gridLocation.y + 4, 1);
		this.changeColor(Color.black);
	}
	
	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()
	{
		if(this.angle % 180 == 0)
			this.setGridSize(4, 2);
		else
			this.setGridSize(2, 4);
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new EditDelay(this.delay));
	}
		
	public void respondToChanges(Component property)
	{
		this.delay = ((EditDelay)property).getDelay();
	}
	
	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(getVHDLName())){
					written = true;
				}
			}
			scanner.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		
		if(!written){
			sim.VHDLExporter.writeLib(path);
			FileWriter writer = null;
			try {
				writer = new FileWriter(path+"\\"+fileName,true);
				
				writer.write("\nentity NOT_ent is\n");
				writer.write("port( x: in std_logic;\n");
				writer.write("F: out std_logic);\n");
				
				writer.write("end NOT_ent;\n\n");
				
				writer.write("architecture behav of NOT_ent is\n");
				writer.write("begin\n");
				writer.write("F<=not x;\n");
				writer.write("end behav;\n");
				
				writer.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public String getVHDLName()
	{
		return "NOT_ent";
	}
}