/*
   Tamot --- Task Modelling Editor
   Copyright (C) 2000 Commonwealth Scientific and Industrial Research Organisation
   (CSIRO), Australia. All rights reserved. 

   The Software, owned by CSIRO Australia, has been developed by the CSIRO 
   Intelligent Interactive Technology group. 
   For more information, please see http://www.cmis.csiro.au/iit/.

   Redistribution in source and binary forms, with or without modification, are not permitted.

   The Software is a beta-test version for internal research and evaluation purposes 
   by the Licensee. The Software is still at a development stage. 
   It has not undergone complete testing and may have inherent errors,
   bugs or deficiencies that can affect the operation of the Software. 

   We encourage your feedback and suggestions. Any bugs / suggestions found please email to
   Shijian.Lu@cmis.csiro.au and Thomas.Lo@cmis.csiro.au. 

   After you have signed the Beta-Test Software License Agreement with the CSIRO,
   CSIRO grants you a non-exclusive, non-transferable license to use it only for your 
   personal, non-commercial and lawful end use. Implied licenses are negated. 

   Warranty Disclaimer : 

   CSIRO supplies the Software on an "as is" basis and makes no warranties that use of
   the Software by the Licensee will not infringe any third party’s 
   Intellectual Property Rights nor that the Software will be of merchantable quality,
   or suitable for a particular purpose. CSIRO excludes all terms, conditions and
   warranties implied by custom, the general law or statute in relation to the
   Software.
*/

package taskModellingTool;

import javax.swing.*;
import java.util.*;
import javax.swing.tree.*;


public class CutManager {
    private static boolean checkCutComponentIsLink() {
        boolean result = false;

        if (Manager.currentComponent instanceof Link) {
            MessageDialog.errorDialog(MainFrame.mainFrame,"Operation not allowed",
                "Cannot cut a link.");
            result = true;
        }
        
        return result;
    }
    
    private static boolean checkCutMultiComponentsAreLink() {
        boolean result = false;

        if (BottomUp.isLinksOnly()) {
            MessageDialog.errorDialog(MainFrame.mainFrame,"Operation not allowed",
                "Cannot cut links only.");
            result = true;
        }
        
        return result;
    }
    
    private static int numLinkExisting(GenericComponent gc) {
        int result = 0;

        if (gc instanceof LinkableComponent) {
             LinkableComponent lc = (LinkableComponent) gc;
             result = lc.linkList.size();
        }
        
        return result;
    }
    
    private static boolean askForMutlipleLinksDeletion(int numLinks) {
        String message = "The selected tasks / boolean connectors have "+numLinks+
            " unconnected links in the set.\n"+
            ((numLinks>1)?"They":"It") +" will be removed!";
        
        return MessageDialog.askIfYesWithWarning(MainFrame.mainFrame,"Warning",
            message,"Ok","Cancel");
    }
    
    private static boolean askForLinkDeletion(int numLinks) {
        String message = "This component has "+numLinks+" link"+
            ((numLinks>1)?"s.\nThey":".\nIt") +" will be removed with it.";
        
        return MessageDialog.askIfYesWithWarning(MainFrame.mainFrame,"Warning",
            message,"Ok","Cancel");
    }
    
    public static boolean multipleCut(boolean askFlag) {
        boolean result = false;
        
        // if link(s) only, cannot cut
        if (checkCutMultiComponentsAreLink()) return result;
        
        int numExcludedLinks = BottomUp.numLinkExcluded();
        if (numExcludedLinks > 0) {
            BottomUp.updateLinksDisplay();
            if (askFlag) {
                if (!askForMutlipleLinksDeletion(numExcludedLinks)) return result;
            }
        }
        
        TMClipboard.init();
        
        // get the LinkableComponent list
        LinkableComponent [] lcList = BottomUp.getLinkableComponents();

        for (int i=0; i<lcList.length; i++) {
            addLinkableComponentToClipboard(lcList[i]);
        }
        
        // get the Link list
        Link [] linkList = BottomUp.getLinks();

        for (int i=0; i<linkList.length; i++) {
            addLinkToClipboard(linkList[i]);
        }
        
        // cut the selected linkableComponents
        for (int i=0; i<lcList.length; i++) {
            cutLinkableComponent(lcList[i]);
        }
        
        //TMClipboard.displayContent();
        
        BottomUp.resetAllSelection();
        
        result = true;
        
        return result;
    }

    /**
     * Cut recursively the selected component
     */
    public static boolean cut() {
        boolean result = false;
        
        if (BottomUp.isMultiComponentsSelected()) {
            result = multipleCut(true);
            return result;
        }
        
        // if link, cannot cut
        if (checkCutComponentIsLink()) return result;
        
        // ask to permit to delete links linked to the cut task / BC
        int numLinks = numLinkExisting(Manager.currentComponent);
        if (numLinks > 0) {
            if (!askForLinkDeletion(numLinks)) return result;
        }
        
        if (!(Manager.currentComponent instanceof LinkableComponent)) return result;
        LinkableComponent lc = (LinkableComponent) Manager.currentComponent;
        
        TMClipboard.init();
        
        addLinkableComponentToClipboard(lc);
        cutLinkableComponent(lc);

        //TMClipboard.displayContent();
        
        result = true;
        
        return result;
    }
    
    private static void removeAllLinksConnectingToComponent(LinkableComponent lc, String parentName) {
        Vector links = lc.getLinks();
        int size = links.size();
        //System.out.println("No of links to " + name + " = " + size);
        Link link;
    
        while (!(links.isEmpty())) {
            link = (Link)links.lastElement();
            
            // delete the link from both components
            lc.deleteLink(link);

            // Delete a link from its parentNode + [skeleton] + [RTFDatabase] and from the tree
            DeleteManager.deleteLink(link,parentName);  
        }
    }
    
    private static void setGrandParentsElementary(String parentName) {
        GenericComponent [] tab = Manager.skeleton.getComponents(parentName);
        //System.out.println("Number of parent components ("+parentName+") = "+tab.length);
        for (int i = 0; i<tab.length; i++) {
            if (tab[i] instanceof CompositeComponent) {
                ((CompositeComponent)tab[i]).setComposite(false);
            }
        }
    }

    private static void addLinkToClipboard(Link link) {
        if (link == null) return;

        // get information about the component
        Integer id2 = new Integer(link.ID);
        String name = link.getName();

        String parentName = Manager.skeleton.toParentName(name,id2);
        
        // can't cut root
        TreePath treePath = Manager.fetchTreePath(name,parentName,Manager.tree,id2);
        if (Manager.isRoot(treePath)) return;
        
        // value to be put in the clipboard
        TMClipboard.addLink(link);
    }

    private static void addLinkableComponentToClipboard(LinkableComponent lc) {
        if (lc == null) return;

        // get information about the component
        Integer id2 = new Integer(lc.ID);
        String name = lc.getName();

        String parentName = Manager.skeleton.toParentName(name,id2);
        
        // can't cut root
        TreePath treePath = Manager.fetchTreePath(name,parentName,Manager.tree,id2);
        if (Manager.isRoot(treePath)) return;
        
        Node parentNode = Manager.skeleton.toParentNode(name,parentName);
                    
        // value to be put in the clipboard
        //TMClipboard.add(parentNode.getComponent(name,id2),Manager.skeleton.toChildNode(name,id2));
        TMClipboard.addLinkableComponent(lc,Manager.skeleton.toChildNode(name,id2));
    }
    
    private static void cutLinkableComponent(LinkableComponent lc) {
        if (lc == null) return;

        // get information about the component
        Integer id2 = new Integer(lc.ID);
        String name = lc.getName();

        //String parentName = treePathToString(truncTreePath(treePath));
        String parentName = Manager.skeleton.toParentName(name,id2);
        
        // can't cut root
        //TreePath treePath = tree.getSelectionPath();
        TreePath treePath = Manager.fetchTreePath(name,parentName,Manager.tree,id2);
        if (Manager.isRoot(treePath)) return;
        
        Node parentNode = Manager.skeleton.toParentNode(name,parentName);
                    
        //remove all the links connecting to the component
        removeAllLinksConnectingToComponent(lc,parentName);
            
        // delete all the frames of the component and all its children's frames
        // delete the component from its parentNode + [skeleton]
        //   as well as all its children's
        cutSubTree(name,parentName,parentNode,id2);
           
        // if parentNode is empty, set parentName as elementary task in all its grand-parent frames 
        if ( (parentNode.isEmpty()) && (!(Manager.skeleton.isRoot(parentNode))) )
            setGrandParentsElementary(parentName);
        
        // select the parent in the tree
        Manager.select(Manager.truncTreePath(treePath));
        
        // remove name in tree
        Manager.tree.removeInTree(treePath);
        
        // synchronize the deletion of node (name, id2) in other parent nodes (with the same parent name)
        synchronizeDeletedNode(name,parentName,id2);

        // synchronize the same duplicated tasks in tree
        Manager.tree.synchronizeTreeStructure(Manager.truncTreePath(treePath));
        
        // update display of parent frame
        Manager.updateParentFrame(treePath);

        // set type in graphicToolBar
        TMClipboard.setToolBar(lc);
                                
        //mark parent task as dirty TRUE
        Manager.markTaskDirty(parentName,true);
        
        FileManager.MODIFIED = true;
    }

    private static void synchronizeDeletedNode(String name,String parentName,Integer id2) {
        // verify that the node (name,parentName,id2) in skeleton is deleted
        if (Manager.skeleton.isInTree(name,parentName,id2)) return;
        
        // for other possible parentNodes of the same parentName, delete (name,parentName,id2)
        if (Manager.skeleton.nodeRegister.containsKey(parentName)) {
            TreeMap treemapGrandParent = (TreeMap) Manager.skeleton.nodeRegister.get(parentName);
            
            Set keysGrandParent = treemapGrandParent.keySet();
            String grandParentName;
            Node grandParentNode;
            for (Iterator itGrandParent = keysGrandParent.iterator();itGrandParent.hasNext();) {
                grandParentName = (String)(itGrandParent.next());
                grandParentNode = (Node) treemapGrandParent.get(grandParentName);
                
                if (grandParentNode.containsKey(parentName)) {
                    TreeMap treemapParent = (TreeMap) grandParentNode.get(parentName);
                    
                    Set keysParent = treemapParent.keySet();
                    Integer parentID;
                    Couple coupleParent;
                    Node parentNode;
                    for (Iterator itParent = keysParent.iterator();itParent.hasNext();) {
                        parentID = (Integer)itParent.next();
                        coupleParent = (Couple)treemapParent.get(parentID);

                        // array of parent nodes
                        parentNode = coupleParent.node;

                        //System.out.println("synchronizeDeletedNode : parentID = "+parentID);
                        //parentNode.printNode();
                        
                        if (parentNode.hasChild(name,id2)) {
                            parentNode.removeCouple(name,id2);
                        }
                    }
                }
            }
        }
    }
    
    private static void cutDecomposition(String name,Node parentNode,Integer id2) {
        Node currentNode = parentNode.getChild(name,id2); 
        
        Set keysName = currentNode.keySet();
        String childName;
        Integer childID;
        TreeMap t;
        
        // get all childName & childID first before the recursive function
        // to prevent ConcurrentModificationException : 
        
        // find numChildName & numChildID
        //System.out.println("cutDecomposition : find numChildID");
        int numChildID = 0;
        for (Iterator itName = keysName.iterator();itName.hasNext();) {
            childName = (String) (itName.next());
            
            t = (TreeMap) currentNode.get(childName);
            Set keysID = t.keySet();                    // get all IDs of name
            for (Iterator itID = keysID.iterator();itID.hasNext();) {
                childID = (Integer)itID.next();
                numChildID++;
            }
        }

        // fill values to childNameArray & childIDArray
        //System.out.println("cutDecomposition : fill values to childNameArray & childIDArray");
        String [] childNameArray = new String[numChildID];
        Integer [] childIDArray = new Integer[numChildID];

        int j = 0;
        for (Iterator itName = keysName.iterator();itName.hasNext();) {
            childName = (String) (itName.next());
            
            t = (TreeMap) currentNode.get(childName);
            Set keysID = t.keySet();                    // get all IDs of name
            for (Iterator itID = keysID.iterator();itID.hasNext();) {
                childIDArray[j] = (Integer)itID.next();
                childNameArray[j] = childName;
                j++;
            }
        }
        
        // now the recursive function
        //System.out.println("cutDecomposition : now the recursive function");
        for (j = 0; j<numChildID; j++) {
            cutSubTree(childNameArray[j],name,currentNode,childIDArray[j]);
        }
    }
    
    private static void cutLeaf(String name,String parentName,Node parentNode,Integer id2) {
        Manager.deleteFrame(name); //look if a frame of this name exist
        
        parentNode.removeCouple(name,id2);
        
        if (!parentNode.containsKey(name))
            Manager.skeleton.removeFromRegister(name,parentName);
    }
    
    // delete all the frames of the component and all its children's frames
    // delete the component from its parentNode + [skeleton]
    //   as well as all its children's
    private static void cutSubTree(String name,String parentName,Node parentNode,Integer id2) {
        //System.out.println("cutSubTree : name = "+name+", parentName = "+parentName+", id2 = "+id2);
        if (parentNode.hasLeaf(name,id2)) {
            cutLeaf(name,parentName,parentNode,id2);
        } else {
            int numInstance = Manager.skeleton.getNumInstance(name);
            //System.out.println("cutSubTree : numInstance = "+numInstance);
            if (numInstance == 1)
                cutDecomposition(name,parentNode,id2);
            
            cutLeaf(name,parentName,parentNode,id2);
        }
    }
    
    //return a new node with new components with the same attributes and the
    //same name except for the links.
    /*private static Node duplicateNodeAndComponents(Node node,String parentName){
        Node n=new Node();
        String name;
        GenericComponent g;
        LinkedList links=new LinkedList();
        LinkedList linkedComponents=new LinkedList();
        LinkableComponent lc;
        for (Iterator i=node.keySet().iterator();i.hasNext();){
            name=(String)i.next();
            g=node.getComponent(name);
            //There is a better way to do it with introspection
            if (g instanceof Task) {
                n.add(new Task((Task)g));
                lc=(LinkableComponent)g;
                if (lc.linkList.size()>0) linkedComponents.add(g);
            }
            else if (g instanceof BooleanConnector) {
                n.add(new BooleanConnector((BooleanConnector)g));
                lc=(LinkableComponent)g;
                if (lc.linkList.size()>0) linkedComponents.add(g);
            }
            else if (g instanceof Link) links.add(g);
        }
        int length=links.size();
        if (length>0){
            Link link;
            LinkableComponent lc1,lc2;
            int i;
            int index=GenericComponent.getIndex(Link.REF_STRING);
            for (i=0;i<length;i++){
                link=(Link)links.removeFirst();
                lc1=(LinkableComponent)n.getComponent(link.lc1.getName());
                lc2=(LinkableComponent)n.getComponent(link.lc2.getName());
                link=new Link(link,index++,lc1,lc2,parentName);
                link.updateLink();
                n.add(link);
            }
        }
        return n;
    }*/

}