package sim;

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

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

public class WireAddListener extends MouseAdapter implements MouseMotionListener
{
	private Wire prototype;
	private boolean isDragging;
	private Point gridPress;
	private Point gridDrag;
	private boolean isCreating;

	public WireAddListener()
	{
		this.prototype = null;
		this.isDragging = false;
		this.gridPress = null;
		this.gridDrag = new Point();
		this.isCreating = false;
	}

	public void setStructure(CreationModule cm)
	{
		this.prototype = (Wire)cm.createWrapper();
		this.prototype.changeColor(Color.green);
	}

	public void mouseEntered(MouseEvent e)
	{
		CentralPanel.ACTIVE_GRID.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
	}

	public void mouseExited(MouseEvent e)
	{
		CentralPanel.ACTIVE_GRID.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
	}

	public void mouseClicked(MouseEvent e)
	{
	}

	public void mousePressed(MouseEvent e)
	{
		// mouse button 2
		if((e.getModifiers() & Event.META_MASK) != 0)
		{
		}
		// mouse button 3
		else if((e.getModifiers() & Event.ALT_MASK) != 0)
		{
		}
		// mouse button 1
		else
		{
			this.isDragging = true;
			this.gridPress = this.convertToGrid(e.getX(), e.getY());

			this.gridDrag.x = this.gridPress.x;
			this.gridDrag.y = this.gridPress.y;

			CentralPanel.ACTIVE_GRID.addModule(this.prototype, false);
			this.prototype.setGridLocation(this.gridPress);
			this.isCreating = false;
		}
	}

	public void mouseReleased(MouseEvent e)
	{
		// mouse button 2
		if((e.getModifiers() & Event.META_MASK) != 0)
		{
			// change prototype for removing
			if(this.isDragging)
			{
				if(this.prototype.getGridLocation().x > this.gridPress.x)
				{
					this.prototype.setGridSize(this.prototype.getGridLocation().x - this.gridPress.x, 0);
					this.prototype.setGridLocation(this.gridPress);
				}
				else if(this.prototype.getGridLocation().x < this.gridPress.x)
				{
					this.prototype.setGridSize(this.gridPress.x - this.prototype.getGridLocation().x, 0);
					this.prototype.setGridLocation(this.prototype.getGridLocation());
				}
				else if(this.prototype.getGridLocation().y > this.gridPress.y)
				{
					this.prototype.setGridSize(0, this.prototype.getGridLocation().y - this.gridPress.y);
					this.prototype.setGridLocation(this.gridPress);
				}
				else if(this.prototype.getGridLocation().y < this.gridPress.y)
				{
					this.prototype.setGridSize(0, this.gridPress.y - this.prototype.getGridLocation().y);
					this.prototype.setGridLocation(this.prototype.getGridLocation());
				}
			}

			WestPanel.MODE_CONTROL.switchToEditMode();
			CentralPanel.ACTIVE_GRID.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
		}
		// mouse button 3
		else if((e.getModifiers() & Event.ALT_MASK) != 0)
		{
		}
		// mouse button 1
		else
		{
			SouthPanel.STATUS_BOX.resetComment();
			this.isDragging = false;
			Wire toAdd;

			CentralPanel.ACTIVE_GRID.removeModule(this.prototype, false);
			// add component
			if(this.prototype.getGridLocation().x > this.gridPress.x)
			{
				toAdd = (Wire)this.prototype.createWrapper(this.gridPress);
				toAdd.setGridSize(this.prototype.getGridLocation().x - this.gridPress.x, 0);
				if(toAdd.canDrop())
				{
					CentralPanel.ACTIVE_GRID.addModule(toAdd);
					toAdd.droped();
				}
				else
				{
					SouthPanel.STATUS_BOX.setComment("can not drop here");
					CentralPanel.ACTIVE_GRID.eraseComponent(toAdd);
				}
			}
			else if(this.prototype.getGridLocation().x < this.gridPress.x)
			{
				toAdd = (Wire)this.prototype.createWrapper(this.prototype.getGridLocation());
				toAdd.setGridSize(this.gridPress.x - this.prototype.getGridLocation().x, 0);
				if(toAdd.canDrop())
				{
					CentralPanel.ACTIVE_GRID.addModule(toAdd);
					toAdd.droped();
				}
				else
				{
					SouthPanel.STATUS_BOX.setComment("can not drop here");
					CentralPanel.ACTIVE_GRID.eraseComponent(toAdd);
				}
			}
			else if(this.prototype.getGridLocation().y > this.gridPress.y)
			{
				toAdd = (Wire)this.prototype.createWrapper(this.gridPress);
				toAdd.setGridSize(0, this.prototype.getGridLocation().y - this.gridPress.y);
				if(toAdd.canDrop())
				{
					CentralPanel.ACTIVE_GRID.addModule(toAdd);
					toAdd.droped();
				}
				else
				{
					SouthPanel.STATUS_BOX.setComment("can not drop here");
					CentralPanel.ACTIVE_GRID.eraseComponent(toAdd);
				}
			}
			else if(this.prototype.getGridLocation().y < this.gridPress.y)
			{
				toAdd = (Wire)this.prototype.createWrapper(this.prototype.getGridLocation());
				toAdd.setGridSize(0, this.gridPress.y - this.prototype.getGridLocation().y);
				if(toAdd.canDrop())
				{
					CentralPanel.ACTIVE_GRID.addModule(toAdd);
					toAdd.droped();
				}
				else
				{
					SouthPanel.STATUS_BOX.setComment("can not drop here");
					CentralPanel.ACTIVE_GRID.eraseComponent(toAdd);
				}
			}
			else
			{
				// handle junction
				Component clicked = CentralPanel.ACTIVE_GRID.getComponentAt(e.getPoint());

				Junction j;
				Wire overlap;

				if(clicked instanceof Junction)
				{
					j = (Junction)clicked;
					if(j.isFull())
					{
						j.getWireAbove().mergeWith(j.getWireBelow());
						j.getWireOnLeft().mergeWith(j.getWireOnRight());
						CentralPanel.ACTIVE_GRID.removeModule(j);
					}
				}
				else if(clicked instanceof Wire)
				{
					Wire w = (Wire)clicked;

					if(w.isHorizontal())
					{
						overlap = w.getOverlapedVerticalWire(this.convertToGrid(e.getPoint()));

						if(overlap != null)
							if(overlap.getNumberOfNodes() == w.getNumberOfNodes())
							{
								j = new Junction(w.getNumberOfNodes());
								j.setGridLocation(this.convertToGrid(e.getPoint()));
								w.breakAt(j);
								overlap.breakAt(j);
								CentralPanel.ACTIVE_GRID.addModule(j);
								j.check();
							}
					}
					else
					{
						overlap = w.getOverlapedHorizontalWire(this.convertToGrid(e.getPoint()));

						if(overlap != null)
							if(overlap.getNumberOfNodes() == w.getNumberOfNodes())
							{
								j = new Junction(w.getNumberOfNodes());
								j.setGridLocation(this.convertToGrid(e.getPoint()));
								w.breakAt(j);
								overlap.breakAt(j);
								CentralPanel.ACTIVE_GRID.addModule(j);
								j.check();
							}
					}
				}
			}
		}
	}

	public void mouseDragged(MouseEvent event)
	{
		if(this.isDragging)
		{
			Point oldDrag = this.gridDrag;
			this.gridDrag = this.convertToGrid(event.getX(), event.getY());

			if(!this.isCreating)
			{
				this.isCreating = true;
				oldDrag.x = this.gridPress.x;
				oldDrag.y = this.gridPress.y;

				if(Math.abs(this.gridDrag.x - this.gridPress.x) > Math.abs(this.gridDrag.y - this.gridPress.y))
				{
					this.prototype.setGridSize(1, 0);
					this.dragHorizontally(oldDrag);
				}
				else
				{
					this.prototype.setGridSize(0, 1);
					this.dragVertically(oldDrag);
				}

			}

			if(this.prototype.isVertical())
			{
				this.dragVertically(oldDrag);
				this.isCreating = (this.gridDrag.y  != this.gridPress.y);
			}
			else
			{
				this.dragHorizontally(oldDrag);
				this.isCreating = (this.gridDrag.x  != this.gridPress.x);
			}
		}
	}

	public void mouseMoved(MouseEvent e)
	{
	}

	protected Point convertToGrid(int x, int y)
	{
		int gap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
		Point result = CentralPanel.ACTIVE_GRID.convertToGrid(x, y);

		if((x % gap) >= (gap / 2))
			result.x = result.x + 1;

		if((y % gap) >= (gap / 2))
			result.y = result.y + 1;

		return result;
	}

	protected Point convertToGrid(Point p)
	{
		return this.convertToGrid(p.x, p.y);
	}

	public Wrapper getPrototype()
	{
		return this.prototype;
	}

	public CreationModule getStructure()
	{
		return this.prototype;
	}

	public void resetStructure()
	{
		this.prototype = null;
	}

/* ==================================================================
	Function to handle dragging
	================================================================= */
	private void dragHorizontally(Point oldDrag)
	{
		if((oldDrag.x > this.gridPress.x) && (this.gridDrag.x < oldDrag.x))
		{
			while((this.prototype.getGridLocation().x > this.gridDrag.x) && (this.prototype.getGridLocation().x > this.gridPress.x))
			{
				this.prototype.setGridLocation(this.prototype.getGridLocation().x - 1, this.prototype.getGridLocation().y);
				CentralPanel.ACTIVE_GRID.eraseComponent(this.prototype);
			}
		}
		else if((oldDrag.x < this.gridPress.x) && (this.gridDrag.x > oldDrag.x))
		{
			while((this.prototype.getGridLocation().x < this.gridDrag.x) && (this.prototype.getGridLocation().x < this.gridPress.x))
			{
				CentralPanel.ACTIVE_GRID.eraseComponent(this.prototype);
				this.prototype.setGridLocation(this.prototype.getGridLocation().x + 1, this.prototype.getGridLocation().y);
			}
		}

		// draw
		if((oldDrag.x >= this.gridPress.x) && (this.gridDrag.x > oldDrag.x))
		{
			while(this.prototype.getGridLocation().x < this.gridDrag.x)
			{
				CentralPanel.ACTIVE_GRID.paintComponent(this.prototype);
				this.prototype.setGridLocation(this.prototype.getGridLocation().x + 1, this.prototype.getGridLocation().y);
			}
		}
		else if((oldDrag.x <= this.gridPress.x) && (this.gridDrag.x < oldDrag.x))
		{
			while(this.prototype.getGridLocation().x > this.gridDrag.x)
			{
				this.prototype.setGridLocation(this.prototype.getGridLocation().x - 1, this.prototype.getGridLocation().y);
				CentralPanel.ACTIVE_GRID.paintComponent(this.prototype);
			}
		}
	}

	private void dragVertically(Point oldDrag)
	{
		//erase old lines
		if((oldDrag.y > this.gridPress.y) && (this.gridDrag.y < oldDrag.y))
		{
			while((this.prototype.getGridLocation().y > this.gridDrag.y) && (this.prototype.getGridLocation().y > this.gridPress.y))
			{
				this.prototype.setGridLocation(this.prototype.getGridLocation().x, this.prototype.getGridLocation().y - 1);
				CentralPanel.ACTIVE_GRID.eraseComponent(this.prototype);
			}
		}
		else if((oldDrag.y < this.gridPress.y) && (this.gridDrag.y > oldDrag.y))
		{
			while((this.prototype.getGridLocation().y < this.gridDrag.y) && (this.prototype.getGridLocation().y < this.gridPress.y))
			{
				CentralPanel.ACTIVE_GRID.eraseComponent(this.prototype);
				this.prototype.setGridLocation(this.prototype.getGridLocation().x, this.prototype.getGridLocation().y + 1);
			}
		}

		// draw
		if((oldDrag.y >= this.gridPress.y) && (this.gridDrag.y > oldDrag.y))
		{
			while(this.prototype.getGridLocation().y < this.gridDrag.y)
			{
				CentralPanel.ACTIVE_GRID.paintComponent(this.prototype);
				this.prototype.setGridLocation(this.prototype.getGridLocation().x, this.prototype.getGridLocation().y + 1);
			}
		}
		else if((oldDrag.y <= this.gridPress.y) && (this.gridDrag.y < oldDrag.y))
		{
			while(this.prototype.getGridLocation().y > this.gridDrag.y)
			{
				this.prototype.setGridLocation(this.prototype.getGridLocation().x, this.prototype.getGridLocation().y - 1);
				CentralPanel.ACTIVE_GRID.paintComponent(this.prototype);
			}
		}
	}
}