package sim.lib.gates;

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

import gjt.BulletinLayout;

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

public abstract class MultiInputGate extends RotatableFlippableWrapperPainted 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
	================================================================= */
	public MultiInputGate()
	{
		super();
		
		this.setInputSize(2);
	}
	
	protected abstract Wrapper getCopy();
	
	public void initializeGridSize()
	{
		this.setGridSize(6, 4);
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	private JunctionList input = new JunctionList();
	private Junction output = null;
	protected boolean invertOutput = false;
	protected boolean invertInputs[];
	protected int inputSize;
	
	public void setInputSize(int size)
	{
		this.inputSize = size;
		
		this.input.setSize(size);
		this.invertInputs = new boolean[size];
		
		for(int x = 0; x < size; x++)
			this.invertInputs[x] = false;
		
		this.adjustToChanges();
	}
	
	public void setOutputInvert(boolean invert)
	{
		this.invertOutput = invert;
	}
	
	public void setInputInvert(boolean invert, int index)
	{
		this.invertInputs[index] = invert;
	}
	
	public void selected()
	{
		for(int loop = 0; loop < this.inputSize; loop++)
			this.input.getItemAt(loop).removePin();
		
		this.output.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);
	}
	
/* ==================================================================
	Simulation part
	================================================================= */
	protected double delay = 1;
	
	public void createEnginePeer(EnginePeerList epl)
	{
		EnginePeer ep = new EnginePeer(this.inputSize, 1, this);
		
		for(int loop = 0; loop < this.inputSize; loop++)
			ep.setInputPin(loop, this.input.getItemAt(loop).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()
	{
		String inputInverts;
		
		if(this.invertInputs[0])
			inputInverts = "1";
		else
			inputInverts = "0";
		
		for(int index = 1; index < this.inputSize; index ++)
		{
			if(this.invertInputs[index])
				inputInverts = inputInverts + "1";
			else
				inputInverts = inputInverts + "0";
		}
		
		return (Double.toString(this.delay) + Wrapper.SEPARATOR + Integer.toString(this.inputSize) + Wrapper.SEPARATOR + this.invertOutput + Wrapper.SEPARATOR + inputInverts + Wrapper.SEPARATOR);
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.delay = Double.valueOf(specificParameters[0]).doubleValue();
				
				this.setInputSize(Integer.valueOf(specificParameters[1]).intValue());
				this.setOutputInvert(Boolean.valueOf(specificParameters[2]).booleanValue());
				
				if(specificParameters[3].length() != this.inputSize)
					throw (new SimException("incorrect number of parameters"));
				
				for(int index = 0; index < this.inputSize; index++)
				{
					if(specificParameters[3].charAt(index) == '1')
						this.setInputInvert(true, index);
				}
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 4;
	}
	
/* ==================================================================
	Rotation abd Flipping Part
	================================================================= */
	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 + 1 + loop, 1);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 1 + middle, 1);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 1 + loop, 1);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2 + loop, 1);
			}
		}
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 6, this.gridLocation.y + 1 + middle, 1);
		
		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 + 1 + loop, this.gridLocation.y + 6, 1);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 1 + middle, this.gridLocation.y + 6, 1);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 1 + loop, this.gridLocation.y + 6, 1);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y + 6, 1);
			}
		}
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 1 + middle, this.gridLocation.y, 1);
		
		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 + 6, this.gridLocation.y + 1 + loop, 1);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 6, this.gridLocation.y + 1 + middle, 1);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 6, this.gridLocation.y + 1 + loop, 1);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 6, this.gridLocation.y + 2 + loop, 1);
			}
		}
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 1 + middle, 1);
		
		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 + 1 + loop, this.gridLocation.y, 1);
		
		if(result)
		{
			if((this.inputSize % 2) != 0)
			{
				result = Wrapper.canDropJuncion(this.gridLocation.x + 1 + middle, this.gridLocation.y, 1);
				for(loop = middle + 1; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 1 + loop, this.gridLocation.y, 1);
			}
			else
			{
				for(loop = middle; (loop < this.inputSize) && result; loop++)
					result = Wrapper.canDropJuncion(this.gridLocation.x + 2 + loop, this.gridLocation.y, 1);
			}
		}
		
		// check output
		if(result)
			result = Wrapper.canDropJuncion(this.gridLocation.x + 1 + middle, this.gridLocation.y + 6, 1);
		
		return result;
	}
	
	protected boolean canDropFlipped_0()
	{
		return this.canDropNormal_0();
	}
	
	protected boolean canDropFlipped_90()
	{
		return this.canDropNormal_270();
	}
	
	protected boolean canDropFlipped_180()
	{
		return this.canDropNormal_180();
	}
	
	protected boolean canDropFlipped_270()
	{
		return this.canDropNormal_90();
	}
	
	
	
	
	protected void dropedNormal_0()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + loop, 1));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(middle, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + middle, 1));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + loop, 1));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + loop, 1));
		}
		
		this.output = Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 1 + middle, 1);
		
		this.changeColor(Color.black);
		this.oldInputInvert = null;
	}
	
	protected void dropedNormal_90()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y + 6, 1));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(middle, Wrapper.setPinAt(this.gridLocation.x + 1 + middle, this.gridLocation.y + 6, 1));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y + 6, 1));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y + 6, 1));
		}
		
		this.output = Wrapper.setPinAt(this.gridLocation.x + 1 + middle, this.gridLocation.y, 1);
		
		this.changeColor(Color.black);
		this.oldInputInvert = null;
	}
	
	protected void dropedNormal_180()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 1 + loop, 1));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(this.inputSize - 1 - middle, Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 1 + middle, 1));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 1 + loop, 1));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 2 + loop, 1));
		}
		
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + middle, 1);
		
		this.changeColor(Color.black);
		this.oldInputInvert = null;
	}
	
	protected void dropedNormal_270()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y, 1));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(this.inputSize - 1 - middle, Wrapper.setPinAt(this.gridLocation.x + 1 + middle, this.gridLocation.y, 1));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y, 1));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y, 1));
		}
		
		this.output = Wrapper.setPinAt(this.gridLocation.x + 1 + middle, this.gridLocation.y + 6, 1);
		
		this.changeColor(Color.black);
		this.oldInputInvert = null;
	}
	
	protected void dropedFlipped_0()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + loop, 1));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(this.inputSize - 1 - middle, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + middle, 1));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + loop, 1));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2 + loop, 1));
		}
		
		this.output = Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 1 + middle, 1);
		
		this.changeColor(Color.black);
		this.oldInputInvert = null;
	}
	
	protected void dropedFlipped_90()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y, 1));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(middle, Wrapper.setPinAt(this.gridLocation.x + 1 + middle, this.gridLocation.y, 1));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y, 1));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y, 1));
		}
		
		this.output = Wrapper.setPinAt(this.gridLocation.x + 1 + middle, this.gridLocation.y + 6, 1);
		
		this.changeColor(Color.black);
		this.oldInputInvert = null;
	}
	
	protected void dropedFlipped_180()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 1 + loop, 1));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(middle, Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 1 + middle, 1));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 1 + loop, 1));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(loop, Wrapper.setPinAt(this.gridLocation.x + 6, this.gridLocation.y + 2 + loop, 1));
		}
		
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 1 + middle, 1);
		
		this.changeColor(Color.black);
		this.oldInputInvert = null;
	}
	
	protected void dropedFlipped_270()
	{
		int loop;
		int middle = this.inputSize / 2;
		
		for(loop = 0; loop < middle; loop++)
			this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y + 6, 1));
		
		if((this.inputSize % 2) != 0)
		{
			this.input.changeItem(this.inputSize - 1 - middle, Wrapper.setPinAt(this.gridLocation.x + 1 + middle, this.gridLocation.y + 6, 1));
			for(loop = middle + 1; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 1 + loop, this.gridLocation.y + 6, 1));
		}
		else
		{
			for(loop = middle; loop < this.inputSize; loop++)
				this.input.changeItem(this.inputSize - 1 - loop, Wrapper.setPinAt(this.gridLocation.x + 2 + loop, this.gridLocation.y + 6, 1));
		}
		
		this.output = Wrapper.setPinAt(this.gridLocation.x + 1 + middle, this.gridLocation.y, 1);
		
		this.changeColor(Color.black);
		this.oldInputInvert = null;
	}
		
	
	
	
	protected void adjustToChanges()
	{
		if(this.angle % 180 == 0)
			this.setGridSize(6, 2 * (this.inputSize / 2) + 2);
		else
			this.setGridSize(2 * (this.inputSize / 2) + 2, 6);
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	private int oldInputSize;
	private boolean[] oldInputInvert = null;
	private boolean oldOutputInvert;
	
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new MultiInputGateProperties(this.delay, this.inputSize, this.invertOutput, this.invertInputs));
	}
		
	public void respondToChanges(Component property)
	{
		this.oldInputSize = this.inputSize;
		this.oldInputInvert = this.invertInputs;
		this.oldOutputInvert = this.oldOutputInvert;
		
		CentralPanel.ACTIVE_GRID.eraseComponent(this);
		
		MultiInputGateProperties x = (MultiInputGateProperties)property;
		
		this.delay = x.getDelay();
		this.setInputSize(x.getInputSize());
		this.setOutputInvert(x.getOutputInvert());
		
		for(int loop = 0; loop < this.inputSize; loop++)
			this.setInputInvert(x.getInputInvert(loop), loop);
		
		CentralPanel.ACTIVE_GRID.paintComponent(this);
	}
	
	public void restoreOriginalProperties()
	{
		if(this.oldInputInvert != null)
		{
			this.setInputSize(this.oldInputSize);
			this.setOutputInvert(this.oldOutputInvert);
			this.invertInputs = this.oldInputInvert;
			this.oldInputInvert = null;
		}
	}
	
	private class MultiInputGateProperties extends Container implements ActionListener, FocusListener
	{
		private TextField editDelay = new TextField(10);
		private TextField editInputSize = new TextField(5);
		
		private ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
		private Panel inputInvertHolder = new Panel(new GridBagLayout());
		private Checkbox[] editInvertInputs;
		private Checkbox editInvertOutput = new Checkbox("Invert Output");
		
		private double delay;
		private int size;
		
		private Label simulation = new Label("Simulation");
		private Label configure = new Label("Configuration");
		
		public MultiInputGateProperties(double propagationDelay, int inputSize, boolean invertOutput, boolean[] invertInputs)
		{
			super();
			
			this.delay = propagationDelay;
			this.size = inputSize;
			this.editInvertOutput.setState(invertOutput);
			
			this.editInvertInputs = new Checkbox[this.size];
			
			this.editDelay.addActionListener(this);
			this.editDelay.setText(Double.toString(this.delay));
			this.editDelay.addFocusListener(this);
			this.editInputSize.addActionListener(this);
			this.editInputSize.setText(Integer.toString(this.size));
			this.editInputSize.addFocusListener(this);
			
			this.setLayout(new BorderLayout(0, 15));
			
			// configuration
			Panel big = new Panel();
			big.setLayout(new BorderLayout(0, 15));
			
			Panel p = new Panel(new BorderLayout());
			p.add(this.configure, BorderLayout.WEST);
			p.add(new SimSeparator(), BorderLayout.CENTER);
			big.add(p, BorderLayout.NORTH);
			
			Panel p2 = new Panel(new GridBagLayout());
			
			p = new Panel(new FlowLayout(FlowLayout.LEFT, 0, 0));
			p.add(new Label("Input Size"));
			p.add(this.editInputSize);
			GridBagConstraints c = new GridBagConstraints();
			c.gridx = 0;
			c.gridy = 0;
			c.gridwidth = 1;
			c.gridheight = 1;
			c.fill = GridBagConstraints.NONE;
			c.anchor = GridBagConstraints.WEST;
			c.weightx = 1;
			c.weighty = 0;
			p2.add(p, c);
			
			p = new Panel(new FlowLayout(FlowLayout.LEFT, 0, 0));
			p.add(this.editInvertOutput);
			c.gridx = 1;
			c.anchor = GridBagConstraints.EAST;
			p2.add(p, c);
			big.add(p2, BorderLayout.CENTER);
			
			this.scroller.setSize(290, 100);
			this.scroller.getVAdjustable().setBlockIncrement(25);
			
			c.gridwidth = 1;
			c.gridheight = 1;
			c.insets = new Insets(2, 20, 2, 20);
			c.fill = GridBagConstraints.NONE;
			c.anchor = GridBagConstraints.CENTER;
			c.weighty = 0;
			c.weightx = 1;
			c.gridx = 0;
			
			for(int loop = 0; loop < this.size; loop++)
			{
				this.editInvertInputs[loop] = new Checkbox("Invert input " + Integer.toString(loop), invertInputs[loop]);
				c.gridy = loop;
				this.inputInvertHolder.add(this.editInvertInputs[loop], c);
			}
			
			p = new Panel(new BulletinLayout());
			p.add(this.inputInvertHolder);
			this.scroller.add(p);
			big.add(this.scroller, BorderLayout.SOUTH);
			this.add(big, BorderLayout.NORTH);
			
			// simulation
			big = new Panel();
			big.setLayout(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);
			
			p = new Panel(new FlowLayout(FlowLayout.LEFT, 0, 0));
			p.add(new Label("Propagation Delay"));
			p.add(this.editDelay);
			big.add(p, BorderLayout.CENTER);
			
			this.add(big, BorderLayout.CENTER);
		}
		
		public void addNotify()
		{
			super.addNotify();
			
			this.setSize(290, this.editDelay.getPreferredSize().height * 2 + this.simulation.getPreferredSize().height * 2 + 160);
			
			this.inputInvertHolder.setSize(250, this.size * (4 + this.editInvertInputs[0].getPreferredSize().height));
			this.scroller.doLayout();
		}
		
		public void actionPerformed(ActionEvent e)
		{
			Component source = (Component)e.getSource();
			
			if(source == this.editDelay)
				this.getDelay();
			else if(source == this.editInputSize)
				this.getInputSize();
		}
		
		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.editInputSize)
				this.getInputSize();
			
			source.setText(source.getText());
		}
		
		public double getDelay()
		{
			double newDelay;
			
			try
			{
				newDelay = Double.valueOf(this.editDelay.getText()).doubleValue();
				
				if(newDelay >= 0)
					this.delay = newDelay;
				else
					this.editDelay.setText(Double.toString(this.delay));
			}
			catch(NumberFormatException nfe)
			{
				this.editDelay.setText(Double.toString(this.delay));
			}
			
			return this.delay;
		}
		
		public boolean getOutputInvert()
		{
			return this.editInvertOutput.getState();
		}
		
		public boolean getInputInvert(int index)
		{
			return this.editInvertInputs[index].getState();
		}
		
		public int getInputSize()
		{
			int newSize;
			
			try
			{
				newSize = Integer.valueOf(this.editInputSize.getText()).intValue();
				
				if(newSize > 1 && (newSize != this.size))
				{
					this.size = newSize;
					this.inputInvertHolder.setSize(250, this.size * (4 + this.editInvertInputs[0].getPreferredSize().height));
					
					Checkbox[] oldInputInverts = this.editInvertInputs;
					this.editInvertInputs = new Checkbox[this.size];
					
					this.inputInvertHolder.removeAll();
					
					GridBagConstraints c = new GridBagConstraints();
					c.gridwidth = 1;
					c.gridheight = 1;
					c.insets = new Insets(2, 20, 2, 20);
					c.fill = GridBagConstraints.NONE;
					c.anchor = GridBagConstraints.CENTER;
					c.weightx = 1;
					c.weighty = 0;
					c.gridx = 0;
					
					for(int loop = 0; loop < this.size; loop++)
					{
						if(loop < oldInputInverts.length)
							this.editInvertInputs[loop] = oldInputInverts[loop];
						else
							this.editInvertInputs[loop] = new Checkbox("Invert input " + Integer.toString(loop), false);
						
						c.gridy = loop;
						this.inputInvertHolder.add(this.editInvertInputs[loop], c);
					}
					
					this.scroller.validate();
				}
				else if(newSize < 2)
				{
					this.editInputSize.setText(Integer.toString(this.size));
				}
			}
			catch(NumberFormatException nfe)
			{
				this.editInputSize.setText(Integer.toString(this.size));
			}
			
			return this.size;
		}
		
		public Dimension getPreferredSize()
		{
			return this.getSize();
		}
		
		public Dimension getMinimumSize()
		{
			return this.getSize();
		}
	
		public Dimension getMaximumSize()
		{
			return this.getSize();
		}
	}
}
