// File: StrategyPanel.java
// Classes: StrategyPanel
// Author: Kedar Patankar

package edu.neu.ccs.demeter.tools.apstudio.graphedit;

import java.awt.Panel;
import java.awt.TextArea;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.BorderLayout;

import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.JTree;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class StrategyPanel extends Panel implements TreeSelectionListener
{
// Reference
	private Editor _editor;
// Description (Could be strategy edge or strategy graph)
	private TextArea _travText;
// Tree 
	private JTree _tree;

	private Vector _outStandingBehaviors;
	private Vector _openBehaviors;

	private int behCount;

	public StrategyPanel(Editor ed)
	{
		_editor=ed;
		behCount = 0;
		_outStandingBehaviors = new Vector();
		_openBehaviors = new Vector();

		JScrollPane panel1 = new JScrollPane();
		panel1.setPreferredSize(new Dimension(150,100));
		panel1.setMinimumSize(new Dimension(150,100));

		DefaultTreeModel treeModel = new DefaultTreeModel(new DefaultMutableTreeNode("Root"));
		_tree = new JTree(treeModel);
		_tree.setRootVisible(false);
		_tree.addTreeSelectionListener(this);
		panel1.getViewport().add(_tree);
		
		Panel panel2 = new Panel();
		panel2.setLayout(new BorderLayout());
		_travText=new TextArea("",2,30,TextArea.SCROLLBARS_VERTICAL_ONLY);
		_travText.setBackground(Color.white);
		_travText.setEditable(false);
		panel2.add("Center",_travText);

		JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,false,panel1,panel2);

		setLayout(new BorderLayout());
		add("Center",jsp);
	}

	public DefaultMutableTreeNode getCurrentBehaviorNode()
	{
		DefaultMutableTreeNode node = getSelectedNode();
		Object o = node.getUserObject();
		if(o instanceof BehaviorNode)
			return node;
		return null;
	}
	
	public boolean isBehaviorAlreadyOpen(String filename){return _openBehaviors.contains(filename);	}
	
	public Vector get_Mod_Behaviors()
	{
		Vector v = new Vector();
		DefaultMutableTreeNode root = (DefaultMutableTreeNode)_tree.getModel().getRoot();

		if(root.getChildCount()<1)
			return v;

		// separate into modified and non-modified nodes.
		Enumeration enum = root.children();
		while(enum.hasMoreElements())
		{
			DefaultMutableTreeNode node = (DefaultMutableTreeNode)enum.nextElement();
			if(((BehaviorNode)node.getUserObject()).needsSaving())
				v.addElement(node);
		}
		return v;
	}

	public void justSavedBehavior(String oldname, String newname)
	{
		_outStandingBehaviors.removeElement(oldname);
		_openBehaviors.removeElement(oldname);
		_openBehaviors.addElement(newname);
		nodeNameChanged(getSelectedNode());
	}
	
	public DefaultMutableTreeNode getSelectedNode()
	{
		TreePath selPath = _tree.getSelectionPath();
		if(selPath!=null)
			return (DefaultMutableTreeNode)selPath.getLastPathComponent();
		return null;
	}
	public DefaultMutableTreeNode getBehaviorRoot(){return (DefaultMutableTreeNode)_tree.getModel().getRoot();}

	public void addNewBehavior()
	{
		String name = "Beh"+ ++behCount;
		addBehavior(name);
	}

	public DefaultMutableTreeNode addBehavior(String name)
	{
		DefaultMutableTreeNode root = (DefaultMutableTreeNode)_tree.getModel().getRoot();
		BehaviorNode behnode = new BehaviorNode(name,_editor.getmenu());
		DefaultMutableTreeNode node = new DefaultMutableTreeNode(behnode);
		((DefaultTreeModel)_tree.getModel()).insertNodeInto(node,root,root.getChildCount());
		((DefaultTreeModel)_tree.getModel()).reload(root);
		_openBehaviors.addElement(name);

		TreePath current = new TreePath(((DefaultTreeModel)_tree.getModel()).getPathToRoot(node));
		_tree.setSelectionPath(current);
		return node;
	}

	public void removeBehavior(DefaultMutableTreeNode node)
	{
		String name = ((BehaviorNode)node.getUserObject()).getName();
		((DefaultTreeModel)_tree.getModel()).removeNodeFromParent(node);
		_openBehaviors.removeElement(name);
		_outStandingBehaviors.removeElement(name);
	}
// Methods for building strategy graphs from textual descriptions
	public DefaultMutableTreeNode addStrategyGraph(DefaultMutableTreeNode node,String name)
	{
		SGraphNode behnode = new SGraphNode(name,_editor.getmenu());
		DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(behnode);
		((DefaultTreeModel)_tree.getModel()).insertNodeInto(newNode,node,node.getChildCount());
		return newNode;
	}
	public DefaultMutableTreeNode addStrategyEdge(DefaultMutableTreeNode node)
	{
		String name = ((SGraphNode)node.getUserObject()).nextEdgeName();
		SEdgeNode behnode = new SEdgeNode(_editor,name);
		DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(behnode);
		((DefaultTreeModel)_tree.getModel()).insertNodeInto(newNode,node,node.getChildCount());
		_tree.expandPath(_tree.getLeadSelectionPath());
		return newNode;
	}
	public void addFrom(DefaultMutableTreeNode node,String name)
	{
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addFrom(name);
	}
	public void addTo(DefaultMutableTreeNode node,String name)
	{
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addTo(name);
	}
	public void addBypassV(DefaultMutableTreeNode node,String name)
	{
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addBypassV(name);
	}
	public void addOnlyThroughV(DefaultMutableTreeNode node,String name)
	{
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addOnlyThroughV(name);
	}
	public void addBypassE(DefaultMutableTreeNode node,String from,String name,String to)
	{
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addBypassE(from,name,to);
	}
	public void addOnlyThroughE(DefaultMutableTreeNode node,String from,String name,String to)
	{
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addOnlyThroughE(from,name,to);
	}

// All the actions below addStrategyGraph() , removeStrategyGraph() , addStrategyEdge() , removeStrategyEdge()
// addFrom() , addTo() , addBypassV() , addOnlyThroughV() , addBypassE() , addOnlyThroughE()
// necessiate strategy saving.
// Hence ((BehaviorNode)node.getUserObject()).needsSaving() returns true everytime it gets executed and hence no
// need to check for else condition.
	public void addStrategyGraph()
	{
		DefaultMutableTreeNode node = getSelectedNode(); // parent node
		String name = ((BehaviorNode)node.getUserObject()).nextGraphName();
		SGraphNode behnode = new SGraphNode(name,_editor.getmenu());
		DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(behnode);// new child
		((DefaultTreeModel)_tree.getModel()).insertNodeInto(newNode,node,node.getChildCount());

		((DefaultTreeModel)_tree.getModel()).nodeChanged(node);
//			((DefaultTreeModel)_tree.getModel()).reload(node);
		TreePath current = new TreePath(((DefaultTreeModel)_tree.getModel()).getPathToRoot(newNode));
		_tree.setSelectionPath(current);

		if ( ((BehaviorNode)node.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)node.getUserObject()).needsSaving() )
			{
				String naime = ((BehaviorNode)node.getUserObject()).toString();
				_outStandingBehaviors.removeElement(naime);
				_outStandingBehaviors.addElement(naime);
			}
		}
	}

	public void removeStrategyGraph()
	{
		DefaultMutableTreeNode node = getSelectedNode(); // strategy graph level
		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); // behavior node level
		TreePath current = new TreePath(((DefaultTreeModel)_tree.getModel()).getPathToRoot(parent));
		_tree.setSelectionPath(current);
		((BehaviorNode)parent.getUserObject()).removeSGraphName(node.getUserObject().toString());
		((DefaultTreeModel)_tree.getModel()).removeNodeFromParent(node);

		if ( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String name = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(name);
				_outStandingBehaviors.addElement(name);
			}
			((DefaultTreeModel)_tree.getModel()).nodeChanged(parent);
		}
		valueChanged();
	}

	public void addStrategyEdge()
	{
		DefaultMutableTreeNode node = getSelectedNode();// parent
		String name = ((SGraphNode)node.getUserObject()).nextEdgeName();
		SEdgeNode behnode = new SEdgeNode(_editor,name);
		DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(behnode);// new child
		((DefaultTreeModel)_tree.getModel()).insertNodeInto(newNode, node,node.getChildCount());
		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();

		((DefaultTreeModel)_tree.getModel()).nodeChanged(node);
		TreePath current = new TreePath(((DefaultTreeModel)_tree.getModel()).getPathToRoot(newNode));
		_tree.setSelectionPath(current);
		
		if ( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String naime = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(naime);
				_outStandingBehaviors.addElement(naime);
			}
		}
	}
	public void removeStrategyEdge()
	{
		DefaultMutableTreeNode node = getSelectedNode(); // At the strategy edge level
	
		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); // At the strategy graph level
		TreePath current = new TreePath(((DefaultTreeModel)_tree.getModel()).getPathToRoot(parent));
		_tree.setSelectionPath(current);
		parent = (DefaultMutableTreeNode) parent.getParent(); // At the behavior file level

		((DefaultTreeModel)_tree.getModel()).removeNodeFromParent(node);

		if ( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String name = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(name);
				_outStandingBehaviors.addElement(name);
			}
			((DefaultTreeModel)_tree.getModel()).nodeChanged(parent);
		}
		valueChanged();
	}

// Defining strategy edges by specifying source and destination and constraints BELOW
	public void addFrom(String name)
	{
		DefaultMutableTreeNode node = getSelectedNode(); // edge level
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addFrom(name);
		showStrategyEdgeDetails(edge);
		((DefaultTreeModel)_tree.getModel()).nodeChanged(node);

		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); // graph level
		parent = (DefaultMutableTreeNode) parent.getParent();// behavior level
		if ( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String naime = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(naime);
				_outStandingBehaviors.addElement(naime);
			}
			((DefaultTreeModel)_tree.getModel()).nodeChanged(parent);
		}
	}
	
	public void addTo(String name)
	{
		DefaultMutableTreeNode node = getSelectedNode(); // edge level
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addTo(name);
		showStrategyEdgeDetails(edge);
		((DefaultTreeModel)_tree.getModel()).nodeChanged(node);

		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); // graph level
		parent = (DefaultMutableTreeNode) parent.getParent(); // behavior level
		if ( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String naime = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(naime);
				_outStandingBehaviors.addElement(naime);
			}
			((DefaultTreeModel)_tree.getModel()).nodeChanged(parent);
		}
	}
	
	public void addBypassV(String name)
	{
		DefaultMutableTreeNode node = getSelectedNode(); // edge level
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addBypassV(name);
		showStrategyEdgeDetails(edge);

		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); //graph level
		parent = (DefaultMutableTreeNode) parent.getParent(); // behavior level
		if( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String naime = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(naime);
				_outStandingBehaviors.addElement(naime);
			}
			((DefaultTreeModel)_tree.getModel()).nodeChanged(parent);
		}
	}
	
	public void addOnlyThroughV(String name)
	{
		DefaultMutableTreeNode node = getSelectedNode(); //edge level
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addOnlyThroughV(name);
		showStrategyEdgeDetails(edge);

		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); // graph level
		parent = (DefaultMutableTreeNode) parent.getParent(); //behavior level
		if ( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String naime = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(naime);
				_outStandingBehaviors.addElement(naime);
			}
			((DefaultTreeModel)_tree.getModel()).nodeChanged(parent);
		}
	}

	public void addBypassE(String from,String name,String to)
	{
		DefaultMutableTreeNode node = getSelectedNode(); //edge level
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addBypassE(from,name,to);
		showStrategyEdgeDetails(edge);

		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); // graph level
		parent = (DefaultMutableTreeNode) parent.getParent(); // behavior level
		if( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String naime = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(naime);
				_outStandingBehaviors.addElement(naime);
			}
			((DefaultTreeModel)_tree.getModel()).nodeChanged(parent);
		}
	}
	
	public void addOnlyThroughE(String from,String name,String to)
	{
		DefaultMutableTreeNode node = getSelectedNode(); // edge level
		SEdgeNode edge = (SEdgeNode)node.getUserObject();
		edge.addOnlyThroughE(from,name,to);
		showStrategyEdgeDetails(edge);

		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); // graph level
		parent = (DefaultMutableTreeNode) parent.getParent(); // behavior level
		if( ((BehaviorNode)parent.getUserObject()).needsSaving(true) )
		{
			if( ((BehaviorNode)parent.getUserObject()).needsSaving() )
			{
				String naime = ((BehaviorNode)parent.getUserObject()).toString();
				_outStandingBehaviors.removeElement(naime);
				_outStandingBehaviors.addElement(naime);
			}
			((DefaultTreeModel)_tree.getModel()).nodeChanged(parent);
		}
	}
// Defined strategy edges by specifying source and destination and constraints ABOVE

	public void nodeNameChanged(DefaultMutableTreeNode node)
	{// This method is called when strategy graph name is modified
		((DefaultTreeModel)_tree.getModel()).nodeChanged(node); 
	}
	public boolean shouldTravPopup()
	{
		DefaultMutableTreeNode selection = getSelectedNode();
		if(selection == null)
			return false;
		
		Object o = selection.getUserObject();
		if(o instanceof SEdgeNode)
			return true;
		return false;
	}
	
	public void valueChanged(TreeSelectionEvent event)
	{	
		DefaultMutableTreeNode selection = getSelectedNode();
		if(selection == null)
		{
			_editor.getmenu().setMenuforNoBeh();
			_travText.setText("Select something in left panel");
			return;
		}

		Object o = selection.getUserObject();
		if(o instanceof SGraphNode)
			showStrategyGraphDetails(selection);
		else
		if(o instanceof SEdgeNode)
			showStrategyEdgeDetails((SEdgeNode)o);
		else
			showBehaviorDetails(selection);

		((Behdata)o).setMenu();
	}

	private void valueChanged()
	{	
		DefaultMutableTreeNode selection = getSelectedNode();
		if(selection == null)
		{
			_editor.getmenu().setMenuforNoBeh();
			_travText.setText("Select something in left panel");
			return;
		}

		Object o = selection.getUserObject();
		if(o instanceof SGraphNode)
			showStrategyGraphDetails(selection);
		else
		if(o instanceof SEdgeNode)
			showStrategyEdgeDetails((SEdgeNode)o);
		else
			showBehaviorDetails(selection);

		((Behdata)o).setMenu();
	}

	public void showBehaviorDetails(DefaultMutableTreeNode selection)
	{
		int i = selection.getChildCount();
		String desc=new String();
		if(i>0)
			desc= "Behavior file contains "+i+" strategy graph(s).";
		else
			desc= ((BehaviorNode)selection.getUserObject()).getDescription();
		_travText.setForeground(Color.black);
		_travText.setText(desc);
	}

	private void showStrategyGraphDetails(DefaultMutableTreeNode selection)
	{
		Enumeration enum = selection.children();
		_travText.setForeground(Color.black);
		_travText.setText("{\n");
		while(enum.hasMoreElements())
		{
			DefaultMutableTreeNode edgeNode = (DefaultMutableTreeNode)enum.nextElement();
			SEdgeNode edge = (SEdgeNode)edgeNode.getUserObject();
			_travText.append(edge.getDescription()+"\n");
		}
		_travText.append("}\n");
	}

	public void showStrategyEdgeDetails(SEdgeNode edge)
	{
		Color fg;
		switch(edge.get_status())
		{
		case SEdgeNode.PARTIAL	:fg = Color.lightGray;break;
		case SEdgeNode.VALID	:fg = Color.black;break;
		case SEdgeNode.INVALID	:fg = Color.red;break;
		default: fg=Color.black;
		}
		_travText.setForeground(fg);
		_travText.setText(edge.getDescription());
	}



	public void doPreview()
	{
		DefaultMutableTreeNode node = getSelectedNode();
		if(node.getChildCount()<1)
		{
			String title = "Error";
			String message = "Strategy graph has 0(zero) edges defined";
			ActionShowMessage asm= new ActionShowMessage(_editor,title,message);
			asm.doIt();
			return;
		}

		Enumeration enum = node.children();
		String stratGraphString=new String();
		while(enum.hasMoreElements())
		{
			DefaultMutableTreeNode edgeNode = (DefaultMutableTreeNode)enum.nextElement();
			SEdgeNode edge = (SEdgeNode)edgeNode.getUserObject();

			int status = edge.get_status();
			if(status!=SEdgeNode.VALID)
			{
				String title = "Error";
				String message = "There is at least one invalid or incomplete strategy edge in the graph";
				ActionShowMessage asm= new ActionShowMessage(_editor,title,message);
				asm.doIt();
				return;
			}

			if(!edge.checkIntegrity())
			{
				String title = "Mismatch Error";
				String message = "There is at least one strategy edge in the graph which uses different classname or edgename";
				ActionShowMessage asm= new ActionShowMessage(_editor,title,message);
				asm.doIt();
				return;
			}
			String stratDefinition = edge.getDescription();

			stratGraphString +=stratDefinition;
			stratGraphString +="\n";
		}

		StrategyGraph sg = StrategyGraph.parse("{ "+stratGraphString+" }");
		if (sg==null) {System.out.println("something wrong"); return;}
		sg = sg.toGraph(sg.getFirst());
		UGraph ug = _editor.curDocument().toExactGraph();
		boolean result = ug.computeSubgraph(sg);
		if(!result)
		{
			String title = "No Path found";
			String message = "There is no such path";
			ActionShowMessage asm= new ActionShowMessage(_editor,title,message);
			asm.doIt();
			_editor.curDocument().net().clearTags();
		}
		_editor.curDocument().damageAll();
	}

} /* end class StrategyPanel */

