package sim.lib.sorces;

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

import gjt.BulletinLayout;

import sim.*;
import sim.engine.*;
import sim.lib.wires.Junction;
import sim.lib.EditBusSize;
import sim.util.LightTextField;
import sim.util.LightTextFieldParent;


public class BusConstant extends RotatableFlippableWrapperPainted implements EngineModule, LightTextFieldParent
{
/* ==================================================================
	Creation Part
	================================================================= */
	private static Image ICON = GuiFileLink.getImage("sim/lib/sorces/BusConstantIcon.gif");
	
	public Image getIcon()
	{
		return BusConstant.ICON;
	}
	
	public Wrapper createWrapper()
	{
		BusConstant result = new BusConstant();
		result.setupWrapper();
		result.setBusSize(this.busSize);
		return result;
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		BusConstant result = new BusConstant();
		result.setupWrapper();
		result.setBusSize(this.busSize);
		result.setGridLocation(gridPosition);
		return result;
	}
	
	public String getBubbleHelp()
	{
		return "Bus Constant";
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	private LightTextField field;
	private int constant;
	private int busSize;
	private int valueLenght;
	
	public BusConstant()
	{
		super();

		this.setBusSize(8);
	}
	
	public void setupWrapper()
	{
		int gap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		
		this.constant = 0;
		this.field = new LightTextField(this, this.valueLenght, new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * gap / 4), '0');
		
		this.setLayout(new BulletinLayout());
		this.add(this.field);
	}
	
	public void initializeGridSize()
	{
		this.setGridSize(5, 4);
	}
	
	public void scale()
	{
		int gap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		Dimension fieldSize, thisSize;
		
		this.field.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * gap / 4));
		
		fieldSize = this.field.getSize();
		thisSize = this.getSize();
		this.field.setLocation((thisSize.width - fieldSize.width) / 2, (thisSize.height - fieldSize.height) / 2);
		this.field.doLayout();
	}
	
	public void setBusSize(int size)
	{
		this.busSize = size;
		
		String max = Integer.toHexString((int)Math.pow(2, this.busSize) - 1).toUpperCase();
		this.valueLenght = max.length();
		
		if(this.field != null)
		{
			int gap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			Dimension fieldSize, thisSize;
			
			this.field.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * gap / 4));
			this.field.setColumns(this.valueLenght);
			
			fieldSize = this.field.getSize();
			this.setGridSize(fieldSize.width / gap + 4, 4);
			thisSize = this.getSize();
			this.field.setLocation((thisSize.width - fieldSize.width) / 2, (thisSize.height - fieldSize.height) / 2);
			this.field.doLayout();
		}
	}
	
	public void addNotify()
	{
		super.addNotify();
		
		this.scale();
	}	
	
	public int getBusSize()
	{
		return this.busSize;
	}
	
	public String getConstantValue()
	{
		String result = Integer.toHexString(constant);
		
		for(int index = result.length(); index < this.valueLenght; index++)
			result = "0" + result;
		
		return result.toUpperCase();
	}
	
/* ==================================================================
	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
	================================================================= */
	private EnginePeer peer = null;
	
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer)
	{
	}
	
	public void createEnginePeer(EnginePeerList epl)
	{
		this.peer = new EnginePeer(0, this.busSize, this);
		String binary = this.getBinaryValue();
		
		for(int index = 0; index < this.busSize; index++)
			this.peer.setOutputPin(index, this.output.getNodes().getItemAt(index));
		
		for(int index = 0; index < this.busSize; index++)
			this.peer.normalTransaction(this.busSize - index - 1, new Signal(binary.charAt(index) == '1', 0, false, this.peer, this.busSize - index - 1));
		
		epl.insertItem(this.peer);
	}
	
	public void reset()
	{
		this.peer = null;
	}
	
	public Wrapper getParentWrapper()
	{
		return this;
	}
	
	private String getBinaryValue()
	{
		String result = Integer.toBinaryString(constant);
		
		for(int index = result.length(); index < this.busSize; index++)
			result = "0" + result;
		
		return result;
	}
	
/* ==================================================================
	Storage Part
	================================================================= */
	public String getSpecificParameters()
	{
		return (Integer.toString(this.constant) + Wrapper.SEPARATOR + Integer.toString(this.busSize) + Wrapper.SEPARATOR);
	}
	
	public void loadWrapper(String[] specificParameters) throws SimException
	{
		this.setupWrapper();
		
		if(specificParameters.length == this.getNumberOfSpecificParameters())
		{
			try
			{
				this.constant = Integer.valueOf(specificParameters[0]).intValue();
				this.setBusSize(Integer.valueOf(specificParameters[1]).intValue());
				this.field.setText(this.getConstantValue());
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
	public int getNumberOfSpecificParameters()
	{
		return 2;
	}
	
/* ==================================================================
	Handle text Field
	================================================================= */
	public void respondToChanges()
	{
		CentralPanel.ACTIVE_GRID.paintComponent(BusConstant.this);
	}
	
	public void escape()
	{
		this.field.setText(this.getConstantValue());
		this.field.transferFocus();
		CentralPanel.ACTIVE_GRID.paintComponent(BusConstant.this);
	}
	
	public void respondToString(String s)
	{
		int change;
		String binary;
		
		try
		{
			change = Integer.valueOf(s, 16).intValue();
		
			if(Math.abs(change) >= (int)Math.pow(2, this.busSize))
				this.field.setText(this.getConstantValue());
			else
			{
				this.constant = change;
				
				this.field.setText(this.getConstantValue());
				
				if(this.peer != null)
				{
					binary = this.getBinaryValue();
					
					for(change = 0; change < this.busSize; change++)
						this.peer.normalTransaction(this.busSize - 1 - change, new Signal(binary.charAt(change) == '1', RunShortcut.LINK.getRealTime(), false, this.peer, this.busSize - 1 - change));
				}
			}
		}
		catch(NumberFormatException ex)
		{
			field.setText(this.getConstantValue());
		}
		
	//	this.field.transferFocus();
		CentralPanel.ACTIVE_GRID.paintComponent(BusConstant.this);
	}
	
/* ==================================================================
	Rotation abd Flipping Part
	================================================================= */
	
	protected void paintNormal_0(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int middle = 2 * gridGap;
		int right = (this.gridSize.width - 1) * gridGap;
		int bottom = 3 * gridGap;
		
		g.setColor(WrapperPainted.BACKGROUND);
		g.fill3DRect(gridGap + 1, gridGap + 1, right - gridGap - 1, middle - 1, true);
		
		g.setColor(this.brush);
		g.fillRect(right, middle - 1, gridGap, 3);
		g.drawRect(gridGap, gridGap, right - gridGap, middle);
		
		Rectangle fieldBounds = this.field.getBounds();
		Graphics fg = g.create(fieldBounds.x, fieldBounds.y, fieldBounds.width, fieldBounds.height);
		this.field.paint(fg);
		fg.dispose();
		
		g.setColor(WrapperPainted.BACKGROUND.darker());
		right = fieldBounds.x + fieldBounds.width;
		bottom = fieldBounds.y + fieldBounds.height;
		
		g.drawLine(fieldBounds.x - 2, fieldBounds.y - 2, fieldBounds.x - 2,  bottom + 1);
		g.drawLine(fieldBounds.x - 2, fieldBounds.y - 2, right, fieldBounds.y - 2);
		
		g.setColor(WrapperPainted.BACKGROUND.brighter());
		g.drawLine(right + 1, fieldBounds.y - 2, right + 1, bottom + 1);
		g.drawLine(fieldBounds.x - 1, bottom + 1, right + 1, bottom + 1);
	}
	
	protected void paintNormal_90(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int middle = 2 * gridGap;
		int right = (this.gridSize.width - 1) * gridGap;
		int bottom = 3 * gridGap;
		
		g.setColor(WrapperPainted.BACKGROUND);
		g.fill3DRect(gridGap + 1, gridGap + 1, right - gridGap - 1, middle - 1, true);
		
		g.setColor(this.brush);
		g.drawRect(gridGap, gridGap, right - gridGap, middle);
		right = this.gridSize.width / 2;
		g.fillRect(gridGap * right - 1, 0, 3 ,gridGap);
		
		Rectangle fieldBounds = this.field.getBounds();
		Graphics fg = g.create(fieldBounds.x, fieldBounds.y, fieldBounds.width, fieldBounds.height);
		this.field.paint(fg);
		fg.dispose();
		
		g.setColor(WrapperPainted.BACKGROUND.darker());
		right = fieldBounds.x + fieldBounds.width;
		bottom = fieldBounds.y + fieldBounds.height;
		
		g.drawLine(fieldBounds.x - 2, fieldBounds.y - 2, fieldBounds.x - 2,  bottom + 1);
		g.drawLine(fieldBounds.x - 2, fieldBounds.y - 2, right, fieldBounds.y - 2);
		
		g.setColor(WrapperPainted.BACKGROUND.brighter());
		g.drawLine(right + 1, fieldBounds.y - 2, right + 1, bottom + 1);
		g.drawLine(fieldBounds.x - 1, bottom + 1, right + 1, bottom + 1);
	}
	
	protected void paintNormal_180(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int middle = 2 * gridGap;
		int right = (this.gridSize.width - 1) * gridGap;
		int bottom = 3 * gridGap;
		
		g.setColor(WrapperPainted.BACKGROUND);
		g.fill3DRect(gridGap + 1, gridGap + 1, right - gridGap - 1, middle - 1, true);
		
		g.setColor(this.brush);
		g.fillRect(0, middle - 1, gridGap, 3);
		g.drawRect(gridGap, gridGap, right - gridGap, middle);
		
		Rectangle fieldBounds = this.field.getBounds();
		Graphics fg = g.create(fieldBounds.x, fieldBounds.y, fieldBounds.width, fieldBounds.height);
		this.field.paint(fg);
		fg.dispose();
		
		g.setColor(WrapperPainted.BACKGROUND.darker());
		right = fieldBounds.x + fieldBounds.width;
		bottom = fieldBounds.y + fieldBounds.height;
		
		g.drawLine(fieldBounds.x - 2, fieldBounds.y - 2, fieldBounds.x - 2,  bottom + 1);
		g.drawLine(fieldBounds.x - 2, fieldBounds.y - 2, right, fieldBounds.y - 2);
		
		g.setColor(WrapperPainted.BACKGROUND.brighter());
		g.drawLine(right + 1, fieldBounds.y - 2, right + 1, bottom + 1);
		g.drawLine(fieldBounds.x - 1, bottom + 1, right + 1, bottom + 1);
	}
	
	protected void paintNormal_270(Graphics g)
	{
		int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		int middle = 2 * gridGap;
		int right = (this.gridSize.width - 1) * gridGap;
		int bottom = 3 * gridGap;
		
		g.setColor(WrapperPainted.BACKGROUND);
		g.fill3DRect(gridGap + 1, gridGap + 1, right - gridGap - 1, middle - 1, true);
		
		g.setColor(this.brush);
		g.drawRect(gridGap, gridGap, right - gridGap, middle);
		right = this.gridSize.width / 2;
		g.fillRect(gridGap * right - 1, bottom, 3 ,gridGap);
		
		Rectangle fieldBounds = this.field.getBounds();
		Graphics fg = g.create(fieldBounds.x, fieldBounds.y, fieldBounds.width, fieldBounds.height);
		this.field.paint(fg);
		fg.dispose();
		
		g.setColor(WrapperPainted.BACKGROUND.darker());
		right = fieldBounds.x + fieldBounds.width;
		bottom = fieldBounds.y + fieldBounds.height;
		
		g.drawLine(fieldBounds.x - 2, fieldBounds.y - 2, fieldBounds.x - 2,  bottom + 1);
		g.drawLine(fieldBounds.x - 2, fieldBounds.y - 2, right, fieldBounds.y - 2);
		
		g.setColor(WrapperPainted.BACKGROUND.brighter());
		g.drawLine(right + 1, fieldBounds.y - 2, right + 1, bottom + 1);
		g.drawLine(fieldBounds.x - 1, bottom + 1, right + 1, bottom + 1);
	}
	
	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, this.busSize);
	}
	
	protected boolean canDropNormal_90()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y, this.busSize);
	}
	
	protected boolean canDropNormal_180()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x, this.gridLocation.y + 2, this.busSize);
	}
	
	protected boolean canDropNormal_270()
	{
		return Wrapper.canDropJuncion(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y + 4, this.busSize);
	}
	
	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, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_90()
	{
		this.output = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_180()
	{
		this.output = Wrapper.setPinAt(this.gridLocation.x, this.gridLocation.y + 2, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	protected void dropedNormal_270()
	{
		this.output = Wrapper.setPinAt(this.gridLocation.x + this.gridSize.width / 2, this.gridLocation.y + 4, this.busSize);
		this.changeColor(Color.black);
		this.oldBusSize = 0;
	}
	
	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
	================================================================= */
	private int oldBusSize = 0;
	
	public boolean hasProperties()
	{
		return true;
	}
	
	public Component getPropertyWindow()
	{
		return (new EditBusSize(this.busSize));
	}
		
	public void respondToChanges(Component property)
	{
		if(this.oldBusSize == 0)
			this.oldBusSize = this.busSize;
		
		CentralPanel.ACTIVE_GRID.eraseComponent(this);
		this.setBusSize(((EditBusSize)property).getBusSize());
		CentralPanel.ACTIVE_GRID.paintComponent(this);
	}
	
	public void restoreOriginalProperties()
	{
		if(this.oldBusSize != 0)
		{
			this.setBusSize(this.oldBusSize);
			this.oldBusSize = 0;
		}
	}
}