package sim.lib.functions;

import java.awt.*;
import java.awt.event.*;

import sim.*;
import sim.engine.*;
import sim.util.SimSeparator;
import sim.lib.wires.Junction;

public abstract class CompareTo extends WrapperPainted implements EngineModule
{
/* ==================================================================
	Creation Part
	================================================================= */
	public Wrapper createWrapper()
	{
		return this.getCopy();
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		Wrapper result = this.getCopy();
		result.setGridLocation(gridPosition);
		return result;
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	protected int busSize;
	protected int constant = 0;
	
	public CompareTo()
	{
		super();
		
		this.setBusSize(8);
	}
	
	protected abstract Wrapper getCopy();
	
	public void initializeGridSize()
	{
		this.setGridSize(2, 4);
	}
	
	public int getBusSize()
	{
		return this.busSize;
	}
	
	public void setBusSize(int size)
	{
		this.busSize = size;
	}
/* ==================================================================
	Maintanance Part
	================================================================= */
	private Junction inputX = null;
	private Junction output = null;
	
	public boolean canDrop()
	{
		boolean result = Wrapper.canDropJuncion(this.gridLocation.x + 1, this.gridLocation.y, this.busSize);
		result = result && Wrapper.canDropJuncion(this.gridLocation.x + 1, this.gridLocation.y + 4, 1);
		
		return result;
	}
		
	public void droped()
	{
		this.inputX = Wrapper.setPinAt(this.gridLocation.x + 1, this.gridLocation.y, this.busSize);
		this.output = Wrapper.setPinAt(this.gridLocation.x + 1, this.gridLocation.y + 4, 1);
		
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	public void selected()
	{
		this.output.removePin();
		this.inputX.removePin();
		
		this.changeColor(Color.green);
	}
	
	public void checkAfterSelected()
	{
		Wrapper.checkPin(this.output);
		Wrapper.checkPin(this.inputX);
	}
/* ==================================================================
	Simulation part
	================================================================= */
	protected double delay = 1;
	
	public void createEnginePeer(EnginePeerList epl)
	{
		int loop;		
		EnginePeer ep = new EnginePeer(this.busSize, 1, this);
		
		for(loop = 0; loop < this.busSize; loop++)
			ep.setInputPin(loop, this.inputX.getNodes().getItemAt(loop));
		
		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 + this.busSize + Wrapper.SEPARATOR + this.constant + Wrapper.SEPARATOR);
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.delay = Double.valueOf(specificParameters[0]).doubleValue();
				this.busSize = Integer.valueOf(specificParameters[1]).intValue();
				this.constant = Integer.valueOf(specificParameters[2]).intValue();
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 3;
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	private int oldBusSize = 0;
	private int oldConstant = 0;
	
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new CompareToProperties(this.busSize, this.delay, this.constant));
	}
		
	public void respondToChanges(Component property)
	{
		CompareToProperties etp = (CompareToProperties)property;
		
		this.delay = etp.getDelay();
		
		if(this.oldBusSize == 0)
			this.oldBusSize = this.busSize;
		
		CentralPanel.ACTIVE_GRID.eraseComponent(this);
		this.setBusSize(etp.getBusSize());
		this.constant = etp.getConstant();
		CentralPanel.ACTIVE_GRID.paintComponent(this);
		
	}
	
	public void restoreOriginalProperties()
	{
		if(this.oldBusSize != 0)
		{
			this.setBusSize(this.oldBusSize);
			this.oldBusSize = 0;
			this.constant = this.oldConstant;
		}
	}
	
	private class CompareToProperties extends Container implements ActionListener, FocusListener
	{
		private TextField editBus = new TextField(10);
		private TextField editDelay = new TextField(5);
		private TextField editConstant = new TextField(5);
		private double oldDelay;
		private int oldBus;
		private int oldConstant;
		
		private Label pins = new Label("Pins");
		private Label simulation = new Label("Simulation");
		
		public CompareToProperties(int bus, double delay, int constant)
		{
			super();
			this.setLayout(new BorderLayout(0, 15));
			
			this.oldBus = bus;
			this.editBus.addActionListener(this);
			this.editBus.addFocusListener(this);
			this.editBus.setText(Integer.toString(bus));
			
			this.oldDelay = delay;
			this.editDelay.addActionListener(this);
			this.editDelay.addFocusListener(this);
			this.editDelay.setText(Double.toString(delay));
			
			this.oldConstant = constant;
			this.editConstant.addActionListener(this);
			this.editConstant.addFocusListener(this);
			this.editConstant.setText(Integer.toString(constant, 16));
			
			Panel big = new Panel(new BorderLayout(0, 15));
			Panel p = new Panel(new BorderLayout());
			p.add(this.pins, BorderLayout.WEST);
			p.add(new SimSeparator(), BorderLayout.CENTER);
			big.add(p, BorderLayout.NORTH);
			
			p = new Panel(new FlowLayout(FlowLayout.LEFT, 0, 0));
			p.add(new Label("Bus Size"));
			p.add(this.editBus);
			big.add(p, BorderLayout.CENTER);
			
			this.add(big, BorderLayout.NORTH);
			
			big = new Panel(new BorderLayout(0, 15));
			p = new Panel(new BorderLayout());
			p.add(this.simulation, BorderLayout.WEST);
			p.add(new SimSeparator(), BorderLayout.CENTER);
			big.add(p, BorderLayout.NORTH);
			
			Panel p2 = new Panel(new GridBagLayout());
			
			GridBagConstraints c = new GridBagConstraints();
			
			c.fill = GridBagConstraints.NONE;
			c.gridheight = 1;
			c.gridwidth = 1;
			c.weightx = 1;
			c.weighty = 0;
			c.gridy = 0;
			
			c.gridx = 0;
			c.anchor = GridBagConstraints.WEST;
			
			p = new Panel(new FlowLayout(FlowLayout.LEFT, 0, 0));
			p.add(new Label("Delay"));
			p.add(this.editDelay);
			p2.add(p, c);
			
			c.gridx = 1;
			c.anchor = GridBagConstraints.EAST;
			
			p = new Panel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
			p.add(new Label("Constant"));
			p.add(this.editConstant);
			p2.add(p, c);
			
			big.add(p2, BorderLayout.CENTER);
			
			this.add(big, BorderLayout.CENTER);
		}
		
		public void addNotify()
		{
			super.addNotify();
			this.setSize(290, this.editBus.getPreferredSize().height * 2 + this.pins.getPreferredSize().height * 2 + 45);
		}
		
		public void actionPerformed(ActionEvent e)
		{
			TextField source = (TextField)e.getSource();
			
			if(source == this.editDelay)
				this.getDelay();
			else if(source == this.editBus)
				this.getBusSize();
			else if(source == this.editConstant)
				this.getConstant();
		}
		
		public void focusGained(FocusEvent e)
		{
		}
		
		public void focusLost(FocusEvent e)
		{
			TextField source = (TextField)e.getSource();
			
			if(source == this.editDelay)
				this.getDelay();
			else if(source == this.editBus)
				this.getBusSize();
			else if(source == this.editConstant)
				this.getConstant();
			
			source.setText(source.getText());
		}
		
		public int getBusSize()
		{
			int newBus;
			
			try
			{
				newBus = Integer.valueOf(this.editBus.getText()).intValue();
				
				if(newBus > 1)
				{
					this.oldBus = newBus;
					
					if(this.oldConstant >= Math.pow(2, this.oldBus))
					{
						this.oldConstant = 0;
						this.editConstant.setText("0");
					}
				}
				else
					this.editBus.setText(Integer.toString(this.oldBus));
			}
			catch(NumberFormatException nfe)
			{
				this.editBus.setText(Integer.toString(this.oldBus));
			}
			
			return this.oldBus;
		}
		
		public double getDelay()
		{
			double newDelay;
			
			try
			{
				newDelay = Double.valueOf(this.editDelay.getText()).doubleValue();
				
				if(newDelay >= 0)
					this.oldDelay = newDelay;
				else
					this.editDelay.setText(Double.toString(this.oldBus));
			}
			catch(NumberFormatException nfe)
			{
				this.editDelay.setText(Double.toString(this.oldBus));
			}
			
			return this.oldDelay;
		}
		
		public int getConstant()
		{
			int newConstant;
			
			try
			{
				newConstant = Integer.valueOf(this.editConstant.getText(), 16).intValue();
				
				if((newConstant >= 0) && (newConstant < Math.pow(2, this.oldBus)))
					this.oldConstant = newConstant;
				else
					this.editConstant.setText(Integer.toString(this.oldConstant, 16));
			}
			catch(NumberFormatException nfe)
			{
				this.editConstant.setText(Integer.toString(this.oldConstant, 16));
			}
			
			return this.oldConstant;
		}
		
		public Dimension getPreferredSize()
		{
			return this.getSize();
		}
		
		public Dimension getMinimumSize()
		{
			return this.getSize();
		}
	
		public Dimension getMaximumSize()
		{
			return this.getSize();
		}
	}
}
