package sim;

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

import sim.lib.wires.Junction;
import sim.lib.wires.Wire;

public abstract class Wrapper extends Container implements CreationModule, StorageModule, PopupModule
{
/* ==================================================================
	GUI part
	================================================================= */
	public static String FONT_NAME = "TimesRoman";
	
	protected Point gridLocation;
	protected Dimension gridSize;
	
	public Wrapper()
	{
		super();
		
		this.gridLocation = new Point(0, 0);
		this.gridSize = new Dimension();
		this.initializeGridSize();
	}
	
	public void setGridSize(int gridWidth, int gridHeight)
	{
		this.gridSize.width = gridWidth;
		this.gridSize.height = gridHeight;
		
		int gap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		
		this.setSize(gap * this.gridSize.width, gap * this.gridSize.height);
	}
	
	public void setGridSize(Dimension gridSize)
	{
		this.setGridSize(gridSize.width, gridSize.height);
	}
	
	public Dimension getGridSize()
	{
		return this.gridSize;
	}
	
	public Rectangle getGridBounds()
	{
		return (new Rectangle(this.gridLocation, this.gridSize));
	}
	
	public Point getGridLocation()
	{
		return this.gridLocation;
	}
	
	public void setGridLocation(int x, int y)
	{
		this.gridLocation.x = x;
		this.gridLocation.y = y;
		
		int size = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		
		this.setLocation(x * size, y * size);
	}
	
	public void setGridLocation(Point p)
	{
		this.setGridLocation(p.x, p.y);		
	}
		
	public void update(Graphics g)
	{
		this.paint(g);
	}
	
	public Dimension getPreferredSize()
	{
		return this.getSize();
	}
	
	protected abstract void initializeGridSize();
	public abstract void changeColor(Color c);
	public abstract void scale();
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	public abstract boolean canDrop();
	public abstract void droped();
	public abstract void selected();
	public abstract void checkAfterSelected();
	
	public static boolean canDropJuncion(Point gridPosition, int nodes)
	{
		boolean result = (gridPosition.x > 0) && (gridPosition.y > 0);
		
		result = result && (gridPosition.x < CentralPanel.ACTIVE_GRID.getGridSize().width);
		result = result && (gridPosition.y < CentralPanel.ACTIVE_GRID.getGridSize().height);
		
		if(result)
		{
			Component comp = CentralPanel.ACTIVE_GRID.getComponentAt(CentralPanel.ACTIVE_GRID.convertToNormal(gridPosition));
			
			if(comp instanceof Wire)
			{
				Wire w = (Wire)comp;			
				result = (w.getNumberOfNodes() == nodes);
				
				if(result)
					if(w.isHorizontal())
					{
						w = Wire.getOverlapedVerticalWire(gridPosition);
						
						if(w != null)
							result = (w.getNumberOfNodes() == nodes);
					}
					else
					{
						w = Wire.getOverlapedHorizontalWire(gridPosition);
						
						if(w != null)
							result = (w.getNumberOfNodes() == nodes);
					}
			}
			else if(comp instanceof Junction)
				result = ( ((Junction)comp).getNumberOfNodes() == nodes );
		}
		
		return result;
	}
	
	public static boolean canDropJuncion(int gridX, int gridY, int nodes)
	{
		return Wrapper.canDropJuncion(new Point(gridX, gridY), nodes);
	}
	
	protected static Junction setPinAt(Point gridPosition, int nodes)
	{
		Junction result;
		Wrapper w = (Wrapper)CentralPanel.ACTIVE_GRID.getComponentAt(CentralPanel.ACTIVE_GRID.convertToNormal(gridPosition));
		
		if(w instanceof Junction)
		{
			result = (Junction)w;
			result.addPin();
			result.configure();
		}
		else if(w instanceof Wire)
		{
			result = new Junction(nodes);
			result.addPin();
			result.setGridLocation(gridPosition);
			result.dropedOnWire((Wire)w);
		}
		else
		{
			result = new Junction(nodes);
			result.addPin();
			result.setGridLocation(gridPosition);
			result.droped();
		}
		
		return result;
	}
	
	public static Junction setPinAt(int gridX, int gridY, int nodes)
	{
		return Wrapper.setPinAt(new Point(gridX, gridY), nodes);
	}
	
	public static void checkPin(Junction pin)
	{
		if(CentralPanel.ACTIVE_GRID.isAncestorOf(pin))
			pin.check();
	}
	
/* ==================================================================
	Storage Part
	================================================================= */
	public static char SEPARATOR = '|';
	
	public String getParameters()
	{
		String result;
		
		// Wrapper Basic
		result = Integer.toString(this.gridSize.width) + Wrapper.SEPARATOR + Integer.toString(this.gridSize.height) + Wrapper.SEPARATOR;
		result = result + Integer.toString(this.gridLocation.x) + Wrapper.SEPARATOR + Integer.toString(this.gridLocation.y) + Wrapper.SEPARATOR;
		
		// Wrapper Specifics
		result = result + this.getSpecificParameters();
		
		return result;
	}
	
	public int getNumberOfBasicParameters()
	{
		return 4;
	}
	
	public void initialiseWrapper(String[] basicParameters, String[] specificParameters) throws SimException
	{
		if(basicParameters.length == this.getNumberOfBasicParameters())
		{
			try
			{
				this.setGridSize(Integer.valueOf(basicParameters[0]).intValue(), Integer.valueOf(basicParameters[1]).intValue());
				this.setGridLocation(Integer.valueOf(basicParameters[2]).intValue(), Integer.valueOf(basicParameters[3]).intValue());
				this.loadWrapper(specificParameters);
			}
			catch(NumberFormatException e)
			{
				throw (new SimException("incorrect parameter type"));
			}
		}
		else
			throw (new SimException("incorrect number of parameters"));
	}
	
/* ==================================================================
	Rotation abd Flipping Part
	================================================================= */
	protected int angle = 0;
	protected boolean isFlipped = false;
	
	public abstract boolean canRotate();
	public abstract boolean canFlip();
	public abstract void flipHorizontal();
	public abstract void flipVertical();
	public abstract void setAngle(int newAngle);
	public abstract void setFlipped(boolean newFlip);
	public abstract void restoreAngleFlipped(int oldAngle, boolean oldFlip);
	public abstract void rotateLeft();
	public abstract void rotateRight();
	
	public int getAngle()
	{
		return this.angle;
	}
	
	public boolean isFlipped()
	{
		return this.isFlipped;
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	public boolean hasProperties()
	{
		return false;
	}
	public Component getPropertyWindow()
	{
		return null;
	}
		
	public void respondToChanges(Component property)
	{
	}
	
	public int getNumberOfMenuItems()
	{
		return 0;
	}
	
	public String getMenuItemName(int index)
	{
		return null;
	}
	
	public void respondToMenuItem(String itemName)
	{
	}
	
	public void restoreOriginalProperties()
	{	
	}
}
