package sim.lib.sorces;

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

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

public class Clock extends RotatableFlippableWrapperPainted implements EngineModule
{
/* ==================================================================
	Creation Part
	================================================================= */
	private static Image ICON = GuiFileLink.getImage("sim/lib/sorces/ClockIcon.gif");
	
	public Image getIcon()
	{
		return Clock.ICON;
	}
	
	public Wrapper createWrapper()
	{
		return (new Clock());
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		Clock result = new Clock();
		result.setGridLocation(gridPosition);
		return result;
	}
	
	public String getBubbleHelp()
	{
		return "Clock";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	public Clock()
	{
		super();
	}

	public void initializeGridSize()
	{
		this.setGridSize(4, 4);
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	private Junction output = null;
	
	public void selected()
	{
		this.output.removePin();
		
		this.changeColor(Color.green);
	}
	
	public void checkAfterSelected()
	{
		Wrapper.checkPin(this.output);
	}
	
/* ==================================================================
	Simulation part
	================================================================= */
	protected double halfPeriod = 4;
	protected boolean past;
	
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer)
	{
		this.past = !this.past;
		peer.normalTransaction(0, new Signal(this.past, currentTime + this.halfPeriod, false, peer, 0));
		peer.setWakeUp(currentTime + this.halfPeriod);
	}
	
	public void createEnginePeer(EnginePeerList epl)
	{
		EnginePeer ep = new EnginePeer(0, 1, this);
		
		ep.setOutputPin(0, this.output.getNodes().getItemAt(0));
		ep.normalTransaction(0, new Signal(false, 0, false, ep, 0));
		ep.normalTransaction(0, new Signal(true, this.halfPeriod, false, ep, 0));
		this.past = true;
		ep.setWakeUp(this.halfPeriod);
		
		epl.insertItem(ep);
	}
	
	public void reset()
	{
	}
	
	public Wrapper getParentWrapper()
	{
		return this;
	}

	public double getPeriod()
	{
		return (this.halfPeriod * 2);
	}
	
	public void changePeriod(double newValue)
	{
		this.halfPeriod = newValue / 2;
	}
	
/* ==================================================================
	Storage Part
	================================================================= */
	public String getSpecificParameters()
	{
		return (Double.toString(this.halfPeriod) + Wrapper.SEPARATOR);
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.halfPeriod = Double.valueOf(specificParameters[0]).doubleValue();
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 1;
	}
	
/* ==================================================================
	Rotation abd Flipping Part
	================================================================= */
	
	protected void paintNormal_0(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = 3 * gridGap / 4;
		int width = 4 * gridGap - 2 * increment;
		
		g.setColor(this.brush);
		
		g.drawOval(increment, increment, width, width);
		
		int gap = 2 * gridGap;
		
		g.drawLine(width + increment, gap, 2 * gap, gap);
		
		increment = gap + gridGap;
		
		g.drawLine(gap, gap, gap, gridGap);
		g.drawLine(gap, gap, increment, gap);
		
		increment = increment - 2;
		int small = gap - 1;
		gap = gap + 1;
		
		g.drawLine(increment, small, increment, gap);
		
		increment = gridGap + 2;
		
		g.drawLine(small, increment, gap, increment);
	}
	
	protected void paintNormal_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = 3 * gridGap / 4;
		int width = 4 * gridGap - 2 * increment;
		
		g.setColor(this.brush);
		
		g.drawOval(increment, increment, width, width);
		
		int gap = 2 * gridGap;
		
		g.drawLine(gap, 0, gap, increment);
		
		increment = gap + gridGap;
		
		g.drawLine(gap, gap, gap, gridGap);
		g.drawLine(gap, gap, increment, gap);
		
		increment = increment - 2;
		int small = gap - 1;
		gap = gap + 1;
		
		g.drawLine(increment, small, increment, gap);
		
		increment = gridGap + 2;
		
		g.drawLine(small, increment, gap, increment);
	}
	
	protected void paintNormal_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = 3 * gridGap / 4;
		int width = 4 * gridGap - 2 * increment;
		
		g.setColor(this.brush);
		
		g.drawOval(increment, increment, width, width);
		
		int gap = 2 * gridGap;
		
		g.drawLine(0, gap, increment, gap);
		
		increment = gap + gridGap;
		
		g.drawLine(gap, gap, gap, gridGap);
		g.drawLine(gap, gap, increment, gap);
		
		increment = increment - 2;
		int small = gap - 1;
		gap = gap + 1;
		
		g.drawLine(increment, small, increment, gap);
		
		increment = gridGap + 2;
		
		g.drawLine(small, increment, gap, increment);
	}
	
	protected void paintNormal_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int increment = 3 * gridGap / 4;
		int width = 4 * gridGap - 2 * increment;
		
		g.setColor(this.brush);
		
		g.drawOval(increment, increment, width, width);
		
		int gap = 2 * gridGap;
		
		g.drawLine(gap, width + increment, gap, 2 * gap);
		
		increment = gap + gridGap;
		
		g.drawLine(gap, gap, gap, gridGap);
		g.drawLine(gap, gap, increment, gap);
		
		increment = increment - 2;
		int small = gap - 1;
		gap = gap + 1;
		
		g.drawLine(increment, small, increment, gap);
		
		increment = gridGap + 2;
		
		g.drawLine(small, increment, gap, increment);
	}
	
	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()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width, this.gridLocation.y + 2, 1);
	}
	
	protected boolean canDropNormal_90()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + 2, this.gridLocation.y, 1);
	}
	
	protected boolean canDropNormal_180()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2, 1);
	}
	
	protected boolean canDropNormal_270()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + 2, this.gridLocation.y + this.gridSize.height, 1);
	}
	
	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()
	{
		this.output = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width, this.gridLocation.y + 2, 1);
		this.changeColor(Color.black);	
	}
	
	protected void dropedNormal_90()
	{
		this.output = Wrapper.setPinAt(this.gridLocation.x + 2, this.gridLocation.y , 1);
		this.changeColor(Color.black);	
	}
	
	protected void dropedNormal_180()
	{
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2, 1);
		this.changeColor(Color.black);	
	}
	
	protected void dropedNormal_270()
	{
		this.output = Wrapper.setPinAt(this.gridLocation.x + 2, this.gridLocation.y + this.gridSize.height, 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()
	{
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new EditPeriod(2 * this.halfPeriod));
	}
		
	public void respondToChanges(Component property)
	{
		this.halfPeriod = ((EditPeriod)property).getHalfPeriod();
	}
	
	private class EditPeriod extends Container implements ActionListener
	{
		private TextField editPeriod = new TextField(10);
		private Label simulation = new Label("Simulation");
		private double old;
		
		public EditPeriod(double initial)
		{
			super();
			this.setLayout(new BorderLayout(0, 15));
			this.old = initial;
			
			Panel p = new Panel(new BorderLayout());
			p.add(this.simulation, BorderLayout.WEST);
			p.add(new SimSeparator(), BorderLayout.CENTER);
			this.add(p, BorderLayout.NORTH);
			
			p = new Panel(new FlowLayout(FlowLayout.LEFT, 0, 0));
			p.add(new Label("Clock Period"));
			p.add(this.editPeriod);
			this.add(p, BorderLayout.CENTER);
			
			this.editPeriod.addActionListener(this);
			this.editPeriod.setText(Double.toString(initial));
		}
		
		public void addNotify()
		{
			super.addNotify();
			this.setSize(290, this.editPeriod.getPreferredSize().height + this.simulation.getPreferredSize().height + 15);
		}
		
		public void actionPerformed(ActionEvent e)
		{
			double newPerid;
			
			try
			{
				newPerid = Double.valueOf(this.editPeriod.getText()).doubleValue();
				
				if(newPerid < 0)
					this.editPeriod.setText(Double.toString(this.old));
				else
					this.old = newPerid;
			}
			catch(NumberFormatException nfe)
			{
				this.editPeriod.setText(Double.toString(this.old));
			}
		}
		
		public double getHalfPeriod()
		{
			double newPerid;
			
			try
			{
				newPerid = Double.valueOf(this.editPeriod.getText()).doubleValue();
				
				if(newPerid > 0)
					this.old = newPerid;
			}
			catch(NumberFormatException nfe)
			{
			}
			
			return this.old / 2;
		}
		
		public Dimension getPreferredSize()
		{
			return this.getSize();
		}
		
		public Dimension getMinimumSize()
		{
			return this.getSize();
		}
	
		public Dimension getMaximumSize()
		{
			return this.getSize();
		}
	}
}
