package sim;

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

import sim.lib.wires.Junction;
//import sim.util.PointList;

/* =======================================================================
	Handling editing of existing components
	====================================================================== */
public class EditListener extends MouseAdapter implements MouseMotionListener
{
	private Point press;
	private boolean isDragging;
	private boolean isSelecting;
	private Dimension draggChange;
	private WrapperList selected;
	private Point oldGridLocations[] = null;
	private boolean oldFlips[] = null;
	private int oldAngles[] = null;
	private boolean waitForScrolling;
	
	public EditListener()
	{
		this.press = new Point();
		this.draggChange = new Dimension();
		this.isDragging = false;
		this.isSelecting = false;
		this.selected = new WrapperList();
	}
	
/* ==================================================================
	Mouse listener
	================================================================= */
	public void mousePressed(MouseEvent event)
	{
		this.press.x = event.getX();
		this.press.y = event.getY();
		this.draggChange.width = 0;
		this.draggChange.height = 0;
		Wrapper source = (Wrapper)CentralPanel.ACTIVE_GRID.getComponentAt(this.press);
		
		// mouse button 2
		if((event.getModifiers() & Event.META_MASK) != 0)
		{
			if((source == CentralPanel.ACTIVE_GRID.getGridBackground()) || (source instanceof Junction))
			{
				if(!this.selected.isEmpty())
				{
					this.deselect(true);
				}
				
			}
			else if(this.selected.isEmpty())
			{
				source.selected();
				source.checkAfterSelected();
				CentralPanel.ACTIVE_GRID.paintComponent(source);
				this.selected.insertItem(source);
				this.oldGridLocations = new Point[1];
				this.oldGridLocations[0] = new Point(source.getGridLocation());
				this.oldAngles = new int[1];
				this.oldAngles[0] = source.getAngle();
				this.oldFlips = new boolean[1];
				this.oldFlips[0] = source.isFlipped();
				
				NorthPanel.EDIT_SHORTCUT.selected(this.selected);
				CentralPanel.WRAPPER_POPUP.setUp(this.selected);
				CentralPanel.WRAPPER_POPUP.show(CentralPanel.ACTIVE_GRID, this.press.x, this.press.y);
			}
			else if(!this.selected.contains(source))
			{
				this.deselect(false);
				
				source = (Wrapper)CentralPanel.ACTIVE_GRID.getComponentAt(event.getPoint());			
				
				if(!(source instanceof Junction))
				{
					source.selected();
					source.checkAfterSelected();
					CentralPanel.ACTIVE_GRID.paintComponent(source);
					this.selected.insertItem(source);
					this.oldGridLocations = new Point[1];
					this.oldGridLocations[0] = new Point(source.getGridLocation());
					this.oldAngles = new int[1];
					this.oldAngles[0] = source.getAngle();
					this.oldFlips = new boolean[1];
					this.oldFlips[0] = source.isFlipped();
					
					this.isDragging = true;
					NorthPanel.EDIT_SHORTCUT.selected(this.selected);
				}
				
				CentralPanel.WRAPPER_POPUP.setUp(this.selected);
				CentralPanel.WRAPPER_POPUP.show(CentralPanel.ACTIVE_GRID, this.press.x, this.press.y);
			}
			else
			{
				CentralPanel.WRAPPER_POPUP.setUp(this.selected);
				CentralPanel.WRAPPER_POPUP.show(CentralPanel.ACTIVE_GRID, this.press.x, this.press.y);
			}
		}
		// mouse button 3
		else if((event.getModifiers() & Event.ALT_MASK) != 0)
		{
		}
		// mouse button 1
		else
		{
			this.waitForScrolling = true;
			
			if((source == CentralPanel.ACTIVE_GRID.getGridBackground()) || (source instanceof Junction))
			{
				if(!this.selected.isEmpty())
				{
					this.deselect(true);
				}
				
				this.isSelecting = true;
			}
			else if(this.selected.isEmpty())
			{
				source.selected();
				source.checkAfterSelected();
				CentralPanel.ACTIVE_GRID.paintComponent(source);
				this.selected.insertItem(source);
				this.oldGridLocations = new Point[1];
				this.oldGridLocations[0] = new Point(source.getGridLocation());
				this.oldAngles = new int[1];
				this.oldAngles[0] = source.getAngle();
				this.oldFlips = new boolean[1];
				this.oldFlips[0] = source.isFlipped();
				
				this.isDragging = true;
				NorthPanel.EDIT_SHORTCUT.selected(this.selected);
			}
			else if(!this.selected.contains(source))
			{
				this.deselect(false);
				
				source = (Wrapper)CentralPanel.ACTIVE_GRID.getComponentAt(event.getPoint());			
				
				if(!(source instanceof Junction))
				{
					source.selected();
					source.checkAfterSelected();
					CentralPanel.ACTIVE_GRID.paintComponent(source);
					this.selected.insertItem(source);
					this.oldGridLocations = new Point[1];
					this.oldGridLocations[0] = new Point(source.getGridLocation());
					this.oldAngles = new int[1];
					this.oldAngles[0] = source.getAngle();
					this.oldFlips = new boolean[1];
					this.oldFlips[0] = source.isFlipped();
					
					this.isDragging = true;
					NorthPanel.EDIT_SHORTCUT.selected(this.selected);
				}
			}
			else
			{
				this.isDragging = true;
			}	
		}
	}
	
	public void mouseReleased(MouseEvent event)
	{
		this.isDragging = false;
		
		if(this.isSelecting)
		{
			int index, total;
			Wrapper w;
			
			this.isSelecting = false;
			
			Point location = new Point();
			
			if(this.draggChange.width < 0)
				location.x = this.press.x;
			else
				location.x = this.press.x - this.draggChange.width;
				
			if(this.draggChange.height < 0)
				location.y = this.press.y;
			else
				location.y = this.press.y - this.draggChange.height;
			
			if((this.draggChange.width != 0) && (this.draggChange.height != 0))
			{
				this.drawRectangleOnGrid(location.x, location.y, Math.abs(this.draggChange.width), Math.abs(this.draggChange.height));
				this.drawRectangleOnGridBuffer(location.x, location.y, Math.abs(this.draggChange.width), Math.abs(this.draggChange.height));
				
				if(this.draggChange.width > 0)
					this.press.x = this.press.x - this.draggChange.width;
				
				if(this.draggChange.height > 0)
					this.press.y = this.press.y - this.draggChange.height;
				
				this.draggChange.width = Math.abs(this.draggChange.width);
				this.draggChange.height = Math.abs(this.draggChange.height);
				
				index = CentralPanel.ACTIVE_GRID.getNumberOfJunctions();
				total = index + CentralPanel.ACTIVE_GRID.getNumberOfComponents() + CentralPanel.ACTIVE_GRID.getNumberOfWires() + CentralPanel.ACTIVE_GRID.getNumberOfSplitters();
				
				for(; index < total; index++)
				{
					w = (Wrapper)CentralPanel.ACTIVE_GRID.getComponent(index);
					if(this.trapped(this.press.x, this.press.y, this.draggChange.width, this.draggChange.height, w))
					{
						this.selected.insertItem(w);
						w.selected();
					}
				}
				
				NorthPanel.EDIT_SHORTCUT.selected(this.selected);
				
				total = this.selected.getSize();
				
				this.oldGridLocations = new Point[total];
				this.oldAngles = new int[total];
				this.oldFlips = new boolean[total];
				
				for(index = 0; index < total; index++)
				{
					w = this.selected.getItemAt(index);
					w.checkAfterSelected();
					this.oldGridLocations[index] = new Point(w.getGridLocation());
					this.oldAngles[index] = w.getAngle();
					this.oldFlips[index] = w.isFlipped();
					
					CentralPanel.ACTIVE_GRID.paintComponent(w);
				}
			}
		}
	}
	
	public void mouseClicked(MouseEvent event)
	{
		CentralPanel.ACTIVE_GRID.requestFocus();
	}
	
	public void mouseMoved(MouseEvent event)
	{
	}
	
	public void mouseDragged(MouseEvent event)
	{
		ScrollPane sp = CentralPanel.ACTIVE_GRID.getScroller();
		Point location = sp.getScrollPosition();
		boolean needToScroll = false;
		
		if(event.getX() < location.x)
		{
			location.x = event.getX();
			needToScroll = true;
		}
		else if(event.getX() > (location.x + sp.getViewportSize().width))
		{
			location.x = event.getX() - sp.getViewportSize().width;
			needToScroll = true;
		}
		
		if(event.getY() < location.y)
		{
			location.y = event.getY();
			needToScroll = true;
		}
		else if(event.getY() > (location.y + sp.getViewportSize().height))
		{
			location.y = event.getY() - sp.getViewportSize().height;
			needToScroll = true;
		}
		
		if(needToScroll)
		{
			if(this.waitForScrolling)
				this.waitForScrolling = false;
			else
			{
				sp.setScrollPosition(location);
				this.waitForScrolling = true;
			}
		}
		
		if(this.isDragging)
		{
			Dimension change = new Dimension();
			
			int currentGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			
			change.width = (this.press.x - event.getX()) / currentGap;
			change.height = (this.press.y - event.getY()) / currentGap;
			
			this.draggChange.width = this.draggChange.width - change.width;
			this.draggChange.height = this.draggChange.height - change.height;
			
			if((Math.abs(this.draggChange.width) + Math.abs(this.draggChange.height)) != 0)
			{
				Rectangle bounds = CentralPanel.ACTIVE_GRID.eraseComponents(this.selected);
				
				Wrapper wr;
				
				for(int loop = 0; loop < this.selected.getSize(); loop++)
				{
					wr = this.selected.getItemAt(loop);
					wr.setGridLocation(wr.getGridLocation().x + this.draggChange.width, wr.getGridLocation().y + this.draggChange.height);
				}
				
				bounds = bounds.union(CentralPanel.ACTIVE_GRID.paintComponents(this.selected));
				
				CentralPanel.ACTIVE_GRID.blitWorkplaceToScreen(bounds);
			}
			
			this.draggChange = change;
		}
		else if(this.isSelecting)
		{
			if((this.draggChange.width != 0) && (this.draggChange.height != 0))
			{
				if(this.draggChange.width < 0)
					location.x = this.press.x;
				else
					location.x = this.press.x - this.draggChange.width;
				
				if(this.draggChange.height < 0)
					location.y = this.press.y;
				else
					location.y = this.press.y - this.draggChange.height;
				
				this.drawRectangleOnGrid(location.x, location.y, Math.abs(this.draggChange.width), Math.abs(this.draggChange.height));
				this.drawRectangleOnGridBuffer(location.x, location.y, Math.abs(this.draggChange.width), Math.abs(this.draggChange.height));
			}
			
			this.draggChange.width = this.press.x - event.getX();
			this.draggChange.height = this.press.y - event.getY();
			
			if((this.draggChange.width != 0) && (this.draggChange.height != 0))
			{
				if(this.draggChange.width < 0)
					location.x = this.press.x;
				else
					location.x = this.press.x - this.draggChange.width;
				
				if(this.draggChange.height < 0)
					location.y = this.press.y;
				else
					location.y = this.press.y - this.draggChange.height;
				
				this.drawRectangleOnGrid(location.x, location.y, Math.abs(this.draggChange.width), Math.abs(this.draggChange.height));
				this.drawRectangleOnGridBuffer(location.x, location.y, Math.abs(this.draggChange.width), Math.abs(this.draggChange.height));
			}
		}
	}
	
	public void mouseEntered(MouseEvent e)
	{
	}
	
	public void mouseExited(MouseEvent e)
	{
	}
	
	public void deselect(boolean updateEditShortcut)
	{
		Wrapper toRemove;
		int loop;
		boolean valid = true;
		
		for(loop = 0; (loop < this.selected.getSize()) && valid; loop++)
			valid = this.selected.getItemAt(loop).canDrop();
		
		if(valid)
		{
			for(loop = 0; loop < this.selected.getSize(); loop++)
			{
				toRemove = this.selected.getItemAt(loop);
				toRemove.droped();
				toRemove.setVisible(true);
				CentralPanel.ACTIVE_GRID.paintComponent(toRemove);
			}
		}
		else
		{
			CentralPanel.ACTIVE_GRID.blitWorkplaceToScreen(CentralPanel.ACTIVE_GRID.eraseComponents(this.selected));
			
			toRemove = this.selected.getItemAt(0);
			toRemove.setGridLocation(this.oldGridLocations[0]);
			toRemove.restoreAngleFlipped(this.oldAngles[0], this.oldFlips[0]);
			toRemove.restoreOriginalProperties();
			
			for(loop = 1; loop < this.selected.getSize(); loop++)
			{
				toRemove = this.selected.getItemAt(loop);
				toRemove.setGridLocation(this.oldGridLocations[loop]);
				toRemove.restoreAngleFlipped(this.oldAngles[loop], this.oldFlips[loop]);
				toRemove.restoreOriginalProperties();
			}
			
			
			
			for(loop = 0; loop < this.selected.getSize(); loop++)
			{
				toRemove = this.selected.getItemAt(loop);
				toRemove.droped();
				toRemove.setVisible(true);
				CentralPanel.ACTIVE_GRID.paintComponent(toRemove);
			}
		}
		
		this.selected.removeAll();
		this.oldGridLocations = null;
		this.oldAngles = null;
		this.oldFlips = null;
		
		if(updateEditShortcut)
			NorthPanel.EDIT_SHORTCUT.deselect();
	}
	
	private void drawRectangleOnGrid(int x, int y, int width, int height)
	{
		Graphics g = CentralPanel.ACTIVE_GRID.getGraphics();
		g.setColor(Color.gray);
		g.setXORMode(Color.lightGray);
		g.drawRect(x, y, width, height);
		g.dispose();
	}
	
	private void drawRectangleOnGridBuffer(int x, int y, int width, int height)
	{
		Graphics g = CentralPanel.ACTIVE_GRID.getBuffer();
		g.setColor(Color.gray);
		g.setXORMode(Color.lightGray);
		g.drawRect(x, y, width, height);
		g.dispose();
	}
	
	private boolean trapped(int x, int y, int width, int height, Component comp)
	{
		Rectangle bounds = comp.getBounds();
		
		return (bounds.x >= x && bounds.y >= y && (bounds.x + bounds.width) < (x + width) && (bounds.y + bounds.height) < (y + height));
	}
	
/* ==================================================================
	Services provided to EditShortcut
	================================================================ */
	public void deleteSelected()
	{
		Wrapper toRemove;
		for(int loop = 0; loop < this.selected.getSize(); loop++)
		{
			toRemove = this.selected.getItemAt(loop);
			CentralPanel.ACTIVE_GRID.removeModule(toRemove);
		}
		
		this.selected.removeAll();
		this.oldGridLocations = null;
		this.oldAngles = null;
		this.oldFlips = null;
	}
}