/* Copyright (c) 2010, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.gui.appear;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JViewport;
import javax.swing.Popup;
import javax.swing.PopupFactory;

import com.cburch.draw.canvas.SelectionEvent;
import com.cburch.draw.canvas.SelectionListener;
import com.cburch.draw.model.CanvasObject;
import com.cburch.logisim.circuit.CircuitState;
import com.cburch.logisim.circuit.appear.AppearancePort;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.gui.generic.CanvasPane;
import com.cburch.logisim.instance.Instance;

class LayoutPopupManager implements SelectionListener, MouseListener, MouseMotionListener {
	private CanvasPane canvasPane;
	private AppearanceCanvas canvas;
	private Popup curPopup;
	private long curPopupTime;
	private Location dragStart;
	
	public LayoutPopupManager(CanvasPane canvasPane, AppearanceCanvas canvas) {
		this.canvasPane = canvasPane;
		this.canvas = canvas;
		this.curPopup = null;
		this.dragStart = null;
		
		canvas.getSelection().addSelectionListener(this);
		canvas.addMouseListener(this);
		canvas.addMouseMotionListener(this);
	}
	
	public void hideCurrentPopup() {
		Popup cur = curPopup;
		if (cur != null) {
			curPopup = null;
			dragStart = null;
			cur.hide();
		}
	}

	public void selectionChanged(SelectionEvent e) {
		int act = e.getAction();
		if (act == SelectionEvent.ACTION_ADDED) {
			Set<AppearancePort> ports = shouldShowPopup(e.getAffected());
			if (ports == null) {
				hideCurrentPopup();
			} else {
				showPopup(ports);
			}
		}
	}
	
	private Set<AppearancePort> shouldShowPopup(Collection<CanvasObject> add) {
		boolean found = false;
		for (CanvasObject o : add) {
			if (o instanceof AppearancePort) {
				found = true;
				break;
			}
		}
		if (found) {
			Set<AppearancePort> ports = getSelectedPorts();
			if (!ports.isEmpty() && isPortUnselected(ports)) {
				return ports;
			}
		}
		return null;
	}
	
	// returns all the ports in the current selection
	private Set<AppearancePort> getSelectedPorts() {
		HashSet<AppearancePort> ports = new HashSet<AppearancePort>();
		for (CanvasObject o : canvas.getSelection().getSelected()) {
			if (o instanceof AppearancePort) {
				ports.add((AppearancePort) o);
			}
		}
		return ports;
	}
	
	// returns true if the canvas contains any port not in the given set
	private boolean isPortUnselected(Set<AppearancePort> selected) {
		for (CanvasObject o : canvas.getModel().getObjectsFromBottom()) {
			if (o instanceof AppearancePort) {
				if (!selected.contains(o)) {
					return true;
				}
			}
		}
		return false;
	}

	private void showPopup(Set<AppearancePort> portObjects) {
		dragStart = null;
		CircuitState circuitState = canvas.getCircuitState();
		if (circuitState == null) return;
		ArrayList<Instance> ports = new ArrayList<Instance>(portObjects.size());
		for (AppearancePort portObject : portObjects) {
			ports.add(portObject.getPin());
		}
		
		hideCurrentPopup();
		LayoutThumbnail layout = new LayoutThumbnail();
		layout.setCircuit(circuitState, ports);
		JViewport owner = canvasPane.getViewport();
		Point ownerLoc = owner.getLocationOnScreen();
		Dimension ownerDim = owner.getSize();
		Dimension layoutDim = layout.getPreferredSize();
		int x = ownerLoc.x + Math.max(0, ownerDim.width - layoutDim.width - 5);
		int y = ownerLoc.y + Math.max(0, ownerDim.height - layoutDim.height - 5);
		PopupFactory factory = PopupFactory.getSharedInstance();
		Popup popup = factory.getPopup(canvasPane.getViewport(), layout, x, y);
		popup.show();
		curPopup = popup;
		curPopupTime = System.currentTimeMillis();
	}

	public void mouseClicked(MouseEvent e) { }

	public void mouseEntered(MouseEvent e) {
		hideCurrentPopup();
	}

	public void mouseExited(MouseEvent e) {
		long sincePopup = System.currentTimeMillis() - curPopupTime;
		if (sincePopup > 50) hideCurrentPopup();
	}

	public void mousePressed(MouseEvent e) {
		long sincePopup = System.currentTimeMillis() - curPopupTime;
		if (sincePopup > 50) hideCurrentPopup();
		dragStart = Location.create(e.getX(), e.getY());
	}

	public void mouseReleased(MouseEvent e) { }

	public void mouseDragged(MouseEvent e) {
		Location start = dragStart;
		if (start != null && start.manhattanDistanceTo(e.getX(), e.getY()) > 4) {
			hideCurrentPopup();
		}
	}

	public void mouseMoved(MouseEvent arg0) { }
	

}
