/*
 * Use to encapsulate the operations involved with both graph package and DomainService
 * such as  cloneNode, cloneGraph
 */

package graphbrowser;

import jowgraphservice.*;
import graph.*;

import java.awt.*;
import java.util.*;
import java.net.*;

public class GraphEngine {
  private static  JOWGraphServiceInterface domainService;
  public static   GraphApplet   graphApplet=null;
  public static   GraphWindow   graphWindow=null;


  //// shared value of EditMode among all the content
  private static  int WorkingMode;

  public static final int NORMALMODE        =100;
  public static final int PASTEGRAPHMODE    =200;
  public static final int PASTECONTENTMODE  =300;
  public static final int MOVEGRAPHMODE     =400;
  public static final int MOVECONTENTMODE   =500;
  public static final int CUTGRAPHMODE      =600;
  public static final int CUTCONTENTMODE    =700;

  //// how to work on Cut/Copy/Paste
  private static Object data=null;
  private static int ClipBoardMode;

  public final static int COPY=0;

  public final static int CLONE_REFERENCE=1;
  public final static int CLONE_ONELAYER=2;
  public final static int CLONE_ONELAYER_REFERENCE=3;

  public final static int CLONE_RECURSION=4;
  public final static int CLONE_RECURSION_REFERENCE=5;
  public final static int MOVE=10;

  public final static int CUT=20;



  public GraphEngine(GraphApplet ga) { //GraphWindow gw) {
    graphApplet = ga;
    domainService = ga.getDomainService();
    WorkingMode = NORMALMODE;
  }

  public void setContents(Object trans,GraphWindow owner,int workingmode) {
    // change owner

    this.data=trans;
    graphWindow=owner;
    this.WorkingMode=workingmode;
    //if (owner instanceof GraphWindow) {
        setWorkingMode(workingmode);
    //}
  }

  public Object getContents(Object owner) {
    return data;
  }

  public int getClipBoardMode(){
    return ClipBoardMode;
  }

  public boolean isSpecialWorking() {
    if (WorkingMode== this.NORMALMODE) return false;
        else return true;
  }

  public int getWorkingMode() {
    return this.WorkingMode;
  }

  public void setWorkingMode(int workmode)
  {  WorkingMode = workmode;
     System.out.println("set work mode");
     if (workmode == NORMALMODE) {
             graphApplet.menuEnabled(true);

     }
       else graphApplet.menuEnabled(false);
  }



  public Node cloneNodeByReference(Graph targetGraph, Node sourcenode, boolean pasteORmove) {
     // clone the node
     Node newnode = targetGraph.addNode();

     // set the rectangle for node
     Graphics g;
     int x,y;
     g = graphWindow.graphpanel.getGraphics ();
     FontMetrics fm = g.getFontMetrics ();
     String label=sourcenode.getLabel();
     x = fm.stringWidth(label);
     y= fm.getAscent();

     newnode.setLabel(label,x,y);
     newnode.setPosition(sourcenode.getPosition());
     newnode.setContentType(sourcenode.getContentType());
     newnode.setContent(sourcenode.getContent());

     if (pasteORmove)  {        ///// if move
        sourcenode.setValid(false);
        sourcenode.setForwarded(true);
        sourcenode.setForwardURL(newnode.getUID());
        newnode.setBackwardURL(sourcenode.getUID());
     }
     return newnode;
  }

  public Node cloneNodeByContent(Graph targetGraph, Node sourcenode, int mode, boolean pasteORMove) {
     return   cloneNodeByContent(targetGraph, sourcenode, new Hashtable(),mode, pasteORMove);

  }

  // includes recursion
  public Node cloneNodeByContent(Graph targetGraph, Node sourcenode, Hashtable UIDstore, int mode, boolean pasteORmove) {

     // clone the node
     Node newnode = cloneNodeByReference(targetGraph,sourcenode,pasteORmove);

    // clone the content of the node

      MIME_Type type = newnode.getContentType();
      if (type == null) return newnode;   // temporary slove the null type problem
      if ( type.getType() != MIME_Type.GRAPH ) {
        graphWindow.showMessage("Clone non-graph content.");

        if ( (mode==GraphEngine.CLONE_RECURSION_REFERENCE) || (mode==GraphEngine.CLONE_ONELAYER_REFERENCE) ){
            newnode.setContent( sourcenode.getContent() );
            return newnode;
        }
        // get source content
        ContentObject sourceContent =null, targetContent =null ;
        try  {
            sourceContent = domainService.openGraphContent ( sourcenode.getContent() ,JOWPermissionImpl.READ);

            // clone the content
            targetContent = new ContentObject ( targetGraph );
            targetContent.setType ( sourceContent.getType() );
            targetContent.setContent ( sourceContent.getContent() );

            /*
            if (pasteORmove)  {        ///// if move
                sourceContent.setValid(false);
                sourceContent.setForwarded(true);
                sourceContent.setForwardURL(targetContent.getUID());
                targetContent.setBackwardURL(sourceContent.getUID());
            }*/

            // save and close the contents
            domainService.closeGraphContent(sourceContent,false);  // close with no save
            domainService.closeGraphContent( targetContent,true ); // close with save

            newnode.setContent( targetContent.getUID() );

            graphWindow.showMessage("Clone the non-Graph Content: ("+ newnode.getLabel()+ ") Finished");

        } catch (JOWException e) {
            graphWindow.showError("Open Graph Content Fail."+e.getMessage());
        } // end catch
      return newnode;
      }

      else
        if ( type.getType() == MIME_Type.GRAPH) {
        //  if graph; get graph
        Graph sourceGraph=null,newGraph=null;
        try {
            sourceGraph = (Graph) domainService.openGraph( sourcenode.getContent(),JOWPermissionImpl.READ );

            //build a new graph and copy all the content
            newGraph = cloneGraph(sourceGraph, UIDstore, mode,pasteORmove);

            // save and close to release the write lock
            domainService.closeGraph(sourceGraph,false);

            if (newGraph != null) domainService.closeGraph(newGraph,true);
               else {System.out.println("new Graph is Null");}
            newnode.setContent( newGraph.getUID() );

            graphWindow.showMessage("Clone the Graph Content Finished");
        } catch (JOWException e) {
            graphWindow.showError("Operation Fail:"+e.getMessage());
           }
      }
    return newnode;
  }

  public Graph cloneGraph(Graph sourceGraph, Hashtable UIDstore, int mode, boolean pasteORmove) {
    Graph targetGraph=null;

    if ( UIDstore.containsKey(sourceGraph.getUID()) ) return (Graph) UIDstore.get(sourceGraph.getUID());

    try {
      graphWindow.showMessage("Clone the Graph");
      targetGraph=domainService.createGraph( sourceGraph.getUID() );

      // record the map of UID for recursion copy to prevent from a infinite circle
      UIDstore.put(sourceGraph.getUID(),targetGraph);


      // copy the content of the Graph
      targetGraph.setLabel(sourceGraph.getLabel());
      // copy the node
      Node sourcenode=null, targetnode=null;
      Link sourcelink=null, targetlink=null;

      Hashtable nodemap = new Hashtable();

      //graphWindow.showMessage("Clone the Nodes )");

      try {
      sourcenode = sourceGraph.getFirstNode();
      } catch (NullPointerException e) {
          System.out.println("Null Pointer1 !!!");
        }
      while( sourcenode != null) {

       switch (mode) {
         case GraphEngine.CLONE_ONELAYER:
            //content
          MIME_Type mt = null;
          try {

              mt = sourcenode.getContentType();
             if (mt==null) { System.out.println("NULL CONTENT!");
                             targetnode = cloneNodeByReference(targetGraph,sourcenode,pasteORmove);
                                 break; }
          } catch (NullPointerException e) {
          System.out.println("Null Pointer 22 !!!");
        }
            if (mt.getType()==MIME_Type.GRAPH) {
                // Node content is graph
                targetnode = cloneNodeByReference(targetGraph,sourcenode,pasteORmove);
             }
               else {
                // Node content is non-graph
                targetnode = cloneNodeByContent(targetGraph, sourcenode, UIDstore, GraphEngine.CLONE_ONELAYER,pasteORmove);
                }
            break;
         case GraphEngine.CLONE_ONELAYER_REFERENCE:
            targetnode=cloneNodeByReference(targetGraph,sourcenode,pasteORmove);
            break;

         case GraphEngine.CLONE_RECURSION:
            targetnode=cloneNodeByContent(targetGraph, sourcenode, UIDstore, GraphEngine.CLONE_RECURSION,pasteORmove);
            break;
         case GraphEngine.CLONE_RECURSION_REFERENCE:
            targetnode=cloneNodeByContent(targetGraph, sourcenode, UIDstore, GraphEngine.CLONE_RECURSION_REFERENCE,pasteORmove);
            break;
         default:break;
       }

       nodemap.put(sourcenode, targetnode);
            /*
            if (pasteORmove)  {        ///// if move
                sourcenode.setValid(false);
                sourcenode.setForwarded(true);
                sourcenode.setForwardURL(targetnode.getUID());
                targetnode.setBackwardURL(sourcenode.getUID());
            } */

       sourcenode = sourceGraph.getNextNode();
      }

      // copy the links
      graphWindow.showMessage("Clone the links");
      sourcelink = sourceGraph.getFirstLink();
      while (sourcelink !=null) {
        Node snode,tnode;
        snode = (Node) nodemap.get(sourcelink.getFrom());
        tnode = (Node) nodemap.get(sourcelink.getTo());
        targetlink = targetGraph.addLink(snode,tnode);

        sourcelink = sourceGraph.getNextLink();
      }

    } catch (JOWException e) {
        graphWindow.showError("Operation Fail:"+e.getMessage());
      }

            /*
            if (pasteORmove)  {        ///// if move
                sourceGraph.setValid(false);
                sourceGraph.setForwarded(true);
                sourceGraph.setForwardURL(targetGraph.getUID());
                targetGraph.setBackwardURL(sourceGraph.getUID());
            } */

  return targetGraph;
  }


  // add Forwarding mechanism
  public URL getContentURL(Node node) {
    // if node valid; directly get it
    //System.out.println("IN GETCONTENTURL");
    //graphWindow.showMessage("IN GETCONTENTURL");
    if (!node.getForwarded()) return node.getContent();
    // otherwise, need to get the forwarded Node URL
    URL newNodeURL = node.getForwardURL();
    // compute the URL of according Graph
    URL graphURL = getGraphURL(newNodeURL);
    if (graphURL == null) return null;
    // then get the Graph from the server
    Graph targetGraph=null;

    System.out.println("Forwarding from:"+node.getUID().toString()+"\n to:"+newNodeURL.toString());
    try {
        targetGraph = (Graph) domainService.openGraph( graphURL );
    } catch (JOWException e)
      {
        System.out.println("FATAL in forwarding. Open Graph Fail."+e.getMessage());
      }
    if (targetGraph == null) return null;

    // then find the matching Node
    Node targetNode = getMatchingNode(targetGraph, newNodeURL);
    if (targetNode==null) return null;
    URL targetContentURL = getContentURL(targetNode);       // Recursion for multi-level Forwarding
    //if (targetContentURL==null) return null;
    return targetContentURL;
    // then get Content URL

  }

  public  URL getGraphURL(URL nodeURL) {
    String str = nodeURL.toString();
    URL graphURL = null;
    try {
        str=str.substring(0,str.length()-4);
        str+="0000";
        System.out.println("!!!!! Graph URL from Node URL:"+str);
        graphURL = new URL(str);
    } catch (MalformedURLException e) {System.out.println("FATAL URL Error");}
    return graphURL;
  }

  public  Node getMatchingNode(Graph graph, URL nodeURL) {
    Node n;
    String urlstr = nodeURL.toString();
    n = graph.getFirstNode();
    while (n!=null) {
        if (urlstr.equals(n.getUID().toString())) return n;
        n = graph.getNextNode();
    }
    return null;
  }
}
