/*
 * Created on ?
 *
 * Multimediale Algorithmen und Datenstrukturen Assessments
 */
package mauda.plugin.fibheap;

import mauda.*;
import mauda.operation.*;

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

/**
 * Generates and controls the interactive access to FibHeaps.
 * 
 * @author Markus Krebs
 */
public class FibHeapInteractive implements MouseListener, ActionListener { //, ItemListener {

	private Exercise exercise;
	
	protected CompPanel compPanel;
	private Panel panel;
	protected FibHeapExt fheap;
	private FibNode[] node;
	private HeapOpMenu popupOnNode;
	private HeapOpMenu popupOffNode;
	private HeapSubOpMenu popupSubOnNode;
	private HeapSubOpMenu popupSubOffNode;
	
	private PopupMenu popupAllOnNode;
	private PopupMenu popupAllOffNode;
	
	private String opName;

	private int currentNode;  // key of currently active node in the GUI
	private int highlight;

	/**
	 * Creates a FibHeap-Interactive
	 * @param exercise
	 */
	public FibHeapInteractive(Exercise exercise) {
		compPanel = Exercise.compPanel;
		
		this.exercise = exercise;
		fheap = (FibHeapExt)exercise.getDSObject();
		node = fheap.getAllNodes();
		//node = new FibNode[100];

		panel = compPanel.getDrawPanel();
		//panel.addMouseListener(this);	// wird in Exercise erledigt

		popupOnNode = new HeapOpMenu(true, this);
		popupOffNode = new HeapOpMenu(false, this);
		panel.add(popupOnNode);
		panel.add(popupOffNode);

		popupSubOnNode = new HeapSubOpMenu(true, this);
		popupSubOffNode = new HeapSubOpMenu(false, this);
		panel.add(popupSubOnNode);
		panel.add(popupSubOffNode);
		
		popupAllOnNode = new HeapAllOpMenu(true, this);
		popupAllOffNode = new HeapAllOpMenu(false, this);
		panel.add(popupAllOnNode);
		panel.add(popupAllOffNode);

		highlight = 0;
	}
	
	private boolean decreasable(int nodeKey) {
		boolean d = false;
		for (int i=1; i<nodeKey; i++) {
			if (node[i] == null)
				d = true;
		}
		return d;
	}

	//////////////////////////////////
	////// MouseListener methods /////
	//////////////////////////////////

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent e) {
		/*
		if (e.isPopupTrigger()) {
			System.out.println("Mouse clicked");
			commandBuffer.add("insert");
		} */

	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent e) {
		processMouse(e);
		/*
		if (e.isPopupTrigger()) {
			System.out.println("Mouse pressed");
			// commandBuffer.add("insert");
			popup = new HeapOpMenu(false);
		} else {
			System.out.println("Not popup");
		} */

	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent e) {
		processMouse(e);
	}
	/**
	 * Handles/Shows the popup-menu in respect to the operating-systems-popup-behaviour
	 * @param e The happened MouseEvent delivered from mousePressed and mouseReleased
	 */
	public void processMouse(MouseEvent e) {
		if(!exercise.actionsAllowed()) return;
		if(exercise.getInteractiveMode() == Exercise.NOPOPUP) return;
		if(!e.isPopupTrigger()) return;
		// System.out.println("mouse released");
		// System.out.println("Node: "+onNode(e));
		// Zur Sicherheit
		fheap = (FibHeapExt)exercise.getDSObject();
		node = fheap.getAllNodes();
		// Folgender Code war obendran
		int onNodeNr = onNode(e);
		if (onNodeNr > 0) {
			if (highlight != currentNode) {
				if (node[highlight] != null) {
					node[highlight].setHighlight(false);
				}
				else {
					if (highlight != 0) {
						System.out.println("ERROR!!! node[" + highlight +"] = null!!");
					}
				}
			}
			node[currentNode].setHighlight(true);
			highlight = currentNode;
		}
		int mode = exercise.getInteractiveMode();
		if (onNodeNr > 0) {
			if(mode == Exercise.OPERATION)
				popupOnNode.show(panel, e.getX(), e.getY());
			else if(mode == Exercise.SUBOPERATION)
				popupSubOnNode.show(panel, e.getX(), e.getY());
			else if(mode == Exercise.BOTHOPERATION)
				popupAllOnNode.show(panel, e.getX(), e.getY());				
		} else {
			if(mode == Exercise.OPERATION)
				popupOffNode.show(panel, e.getX(), e.getY());
			else if(mode == Exercise.SUBOPERATION)
				popupSubOffNode.show(panel, e.getX(), e.getY());
			else if(mode == Exercise.BOTHOPERATION)
				popupAllOffNode.show(panel, e.getX(), e.getY());				
		}
	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent e) {

	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent e) {

	}

	/**
	 * Returns the key of the node if a node was clicked, 0 otherwise.
	 * Still buggy!!!
	 *
	 * @param e the MouseEvent
	 * @return the key
	 */
	private int onNode(MouseEvent e) {
		double x = compPanel.getDrawAttr().getRelPosX(e.getX());
		double y = compPanel.getDrawAttr().getRelPosY(e.getY());
		int key = 0;

		for (int i=0; i<node.length; i++) {
			if (node[i] != null) {
				DPair pos = node[i].getAbsPos(fheap.getMainCompObj());
				if (inNode(pos, x, y)) {
					key = node[i].getKey();
					//System.out.println("Node "+key+" clicked.");
					break;
				}

			}
		}
		currentNode = key;
		return key;
	}

	private boolean inNode(DPair pos, double x, double y) {
		return ((Math.abs(pos.get(0) - x) < 0.02) && (Math.abs(pos.get(1) - y) < 0.02));
	}

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		if(e == null) return;
		// Zur Sicherheit
		fheap = (FibHeapExt)exercise.getDSObject();
		node = fheap.getAllNodes();
		if (highlight != 0 && node[highlight] != null) {
			node[highlight].setHighlight(false);
		}
		String op = e.getActionCommand();
		//System.out.println("Event-ActionCommand: "+op);
		int opnum = 0;
			
		java.util.StringTokenizer st = new java.util.StringTokenizer(op);
		String command = st.nextToken();
		if(st.hasMoreTokens()) {
			String number = st.nextToken();
			opnum = (new Integer(number.trim())).intValue();
		}
			
		// Operationen mit Argument
		if (opnum > 0) {
			if (command.equals(HeapOpMenu.insert)) {
				int k = opnum;
				if ((k <= 0) || (k >= 100)) exercise.commit("INSERT", "Key out of range");
				else if(node[k] != null) exercise.commit("INSERT", "Key already exists");
				//else if(fheap.min!=null&&!fheap.min.isRoot()) exercise.commit("INSERT", "Minimum is not in rootlist");
				else {
					Operation operation = new Operation(	"INSERT",
															k,
															Operation.NULL,
															Operation.NULL);
					exercise.commitInteractive(operation);
				}
			} else if (command.equals(HeapOpMenu.decreaseKeyNode)) {
				int from = currentNode;
				int to = opnum;
				if(from<=0||from>=100) exercise.commit("DECREASE_KEY", "from out of range");
				else if(to<=0||to>=100) exercise.commit("DECREASE_KEY", "to out of range");
				//else if(to>=from) exercise.commit("DECREASE_KEY", "to >= from");
				else if(node[from]==null) exercise.commit("DECREASE_KEY", "from-Node not exists");
				else if(node[to]!=null) exercise.commit("DECREASE_KEY", "to-Node already exists");
				else {
					if (currentNode == from) {
						currentNode = to;
					}
					if (highlight == from) {
						highlight = to;
					}
					Operation operation = new Operation(	"DECREASE_KEY",
															from,
															to,
															Operation.NULL);
					exercise.commitInteractive(operation);
				}
 			} else if (command.equals(HeapSubOpMenu.setKey)) {
				int k = opnum;
				if(k<=0||k>=100) exercise.commit("SETKEY", "key out of range");
				else if(node[k]!=null) exercise.commit("SETKEY", "to-Node already exists");
				else {
					SubOperation subOperation =
						new SubOperation("SETKEY", currentNode, k);
					exercise.commitInteractive(subOperation);
				}
			} else if(command.equals(HeapSubOpMenu.link)) {
				int from = currentNode;
				int to = opnum;
				if(node[from] == null) exercise.commit("LINK","from == null");
				else if(node[to] == null) exercise.commit("LINK","to == null");
				// TODO: link not root (erfordert Änderung in FibHeap-Klassen)
				// sehr aufwendige Änderung
				else if(!node[from].isRoot()) exercise.commit("LINK", "link not possible");// 'from' is not root");
				else if(!node[to].isRoot()) exercise.commit("LINK", "link not possible"); // 'to' is not root");
				else {
					//System.out.println("LINK: highlight="+highlight+" / currentNode="+currentNode);
					SubOperation subOperation =
						new SubOperation("LINK",
										from, to);
					exercise.commitInteractive(subOperation);
				}
			} else if(command.equals(HeapSubOpMenu.newFHeapMeld)) {
				if(opnum<=0||opnum>=100) exercise.commit("NEWFHEAPMELD", "key out of range");
				else if(node[opnum]!=null) exercise.commit("NEWFHEAPMELD", "key already exists");
				//else if(fheap.min!=null&&!fheap.min.isRoot()) exercise.commit("NEWFHEAPMELD", "Minimum is not in rootlist");
				else {
					SubOperation subOperation =
						new SubOperation("NEWFHEAPMELD", opnum);
					exercise.commitInteractive(subOperation);
				}
			}
		}
		// Operationen ohne Argument
		else {
			if (command.equals(HeapOpMenu.randomInsert)) {
				// Randomisiertes Insert
				if (fheap.getHeapSize() < 99) {
					//if(fheap.min!=null&&!fheap.min.isRoot()) exercise.commit("INSERT", "Minimum is not in rootlist");
					//else {
						int rnd;
						do {
							rnd = (int)(Math.random()*99) + 1;
						} while (node[rnd] != null);
						Operation operation = new Operation(	"INSERT",
																rnd,
																Operation.NULL,
																Operation.NULL);
						exercise.commitInteractive(operation);
					//}
				}
			} else if (command.equals(HeapOpMenu.randomDecreaseKeyNode)) {
				// System.out.println("Decrease to random key");
				int before = currentNode;
				if (decreasable(before)) {
					int after;
					do {
						after = (int)(Math.random()*(before-1)) + 1;
					} while (node[after] != null);
					Operation operation = new Operation(	"DECREASE_KEY",
															before,
															after,
															Operation.NULL);
					exercise.commitInteractive(operation);
				}	
			} else if (command.equals(HeapOpMenu.deleteMin)) {
				if(fheap.getHeapSize()<=0) exercise.commit("DELETE_MIN", "FibHeap ist empty");
				//else if(fheap.min!=null&&!fheap.min.isRoot()) exercise.commit("DELETE_MIN", "Minimum is not in rootlist");
				else {
					Operation operation = new Operation(	"DELETE_MIN",
															Operation.NULL,
															Operation.NULL,
															Operation.NULL);
					exercise.commitInteractive(operation);
				}				
			} else if (command.equals(HeapOpMenu.deleteNode)) {
				int k = currentNode;
				if(k<=0||k>=100) exercise.commit("DELETE", "key out of range");
				else if(node[k]==null) exercise.commit("DELETE", "key not exists");
				else { //if(k!=fheap.accessmin()) {
					if (highlight == k) {
						highlight = 0;
					}
					if (currentNode == k) {
						currentNode = 0;
					}
					Operation operation = new Operation(	"DELETE",
															k,
															Operation.NULL,
															Operation.NULL);
					exercise.commitInteractive(operation);
				} /*else if (k == fheap.accessmin()) {
					Operation operation = new Operation(	"DELETE_MIN",
															Operation.NULL,
															Operation.NULL,
															Operation.NULL);
					exercise.commit(operation);
				}*/												
			} else if (command.equals(HeapSubOpMenu.cutNode)) {
				int k = currentNode;
				if(k<=0||k>=100) exercise.commit("CUT", "key out of range");
				//else if(node[k].isRoot()) exercise.commit("CUT", "key is root");
				//else if(!fheap.min.isRoot()) exercise.commit("CUT", "Min is not root");
				else {
					SubOperation subOperation =
						new SubOperation("CUT", k);
					exercise.commitInteractive(subOperation);
				}
			} else if(command.equals(HeapSubOpMenu.markNode)) {
				int k = currentNode;
				if(k<=0||k>=100) exercise.commit("MARK", "key out of range");
				else {
					SubOperation subOperation =
						new SubOperation("MARK", k);
					exercise.commitInteractive(subOperation);
				}
			} else if(command.equals(HeapSubOpMenu.unmarkNode)) {
				int k = currentNode;
				if(k<=0||k>=100) exercise.commit("UNMARK", "key out of range");
				else {
					SubOperation subOperation =
						new SubOperation("UNMARK", k);
					exercise.commitInteractive(subOperation);
				}
			} else if(command.equals(HeapSubOpMenu.updateMinNode)) {
				int k = currentNode;
				if(k<=0||k>=100) exercise.commit("UPDATEMIN", "key out of range");
				//else if(!node[k].isRoot()) exercise.commit("UPDATEMIN", "key is not root");
				else {
					SubOperation subOperation =
						new SubOperation("UPDATEMIN", k);
					exercise.commitInteractive(subOperation);
				}
			} else if(command.equals(HeapSubOpMenu.removeNode)) {
				int k = currentNode;
				if(k<=0||k>=100) exercise.commit("REMOVE", "key out of range");
				//else if(!node[k].isRoot() && node[k].hasChildren()) exercise.commit("REMOVE", "node is a inner node");
				else {
					SubOperation subOperation =
						new SubOperation("REMOVE", k);
					exercise.commitInteractive(subOperation);
				}
			} else {
				System.out.println("Neither: '"+command+"'");
			}
		}
	}
}
