/*
   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.*;
import java.awt.Point;


// determine the source and destination components used by PromoteManager & DemoteManager
public class SourceDestinationManager {
    // source LinkableComponent (outside BottomUp)
    private static TreeMap sourceLinkableComponentTreeMap = new TreeMap();        // id2 (key), lc (value)

    // destination LinkableComponent (outside BottomUp)
    private static TreeMap destinationLinkableComponentTreeMap = new TreeMap();   // id2 (key), lc (value)
    
    
    // source LinkableComponent in the decomposition (inside BottomUp)
    private static TreeMap sourceDecompositionTreeMap = new TreeMap();        // id2 (key), lc (value)
    
    // destination LinkableComponent in the decomposition (inside BottomUp)
    private static TreeMap destinationDecompositionTreeMap = new TreeMap();   // id2 (key), lc (value)


    // source LinkableComponent in the grand parent (parent of the task enclosing BottomUp)
    private static TreeMap sourceGrandParentTreeMap = new TreeMap();        // id2 (key), lc (value)
    
    // destination LinkableComponent in the grand parent (parent of the task enclosing BottomUp)
    private static TreeMap destinationGrandParentTreeMap = new TreeMap();   // id2 (key), lc (value)

    
    // pasted source LinkableComponent from sourceDecompositionTreeMap
    private static TreeMap pastedSourceTreeMap = new TreeMap();        // id2 (key), lc (value)
    
    // pasted destination LinkableComponent from destinationDecompositionTreeMap
    private static TreeMap pastedDestinationTreeMap = new TreeMap();   // id2 (key), lc (value)
    
    
    private static void init() {
        sourceLinkableComponentTreeMap.clear();
        destinationLinkableComponentTreeMap.clear();
        
        sourceDecompositionTreeMap.clear();
        destinationDecompositionTreeMap.clear();
    }
    
    private static void initGrandParent() {
        sourceGrandParentTreeMap.clear();
        destinationGrandParentTreeMap.clear();

        pastedSourceTreeMap.clear();
        pastedDestinationTreeMap.clear();
    }
    
    public static void findSourceAndDestinationComponentsForPromote() {
        init();
        
        if (!BottomUp.isMultiComponentsSelected()) return;
        
        if (BottomUp.isLinksOnly()) return;
        
        BottomUp.updateRegisters();
        
        Link [] linkList = BottomUp.getExcludedLinks();

        for (int i=0; i<linkList.length; i++) {
            Link link = linkList[i];
            //TMUtility.displayLink("findSourceAndDestinationComponents",link);
            
            LinkableComponent lc1 = link.lc1;
            LinkableComponent lc2 = link.lc2;
            
            boolean lc1ExistFlag = BottomUp.isComponentExistInShiftRegister(lc1);
            boolean lc2ExistFlag = BottomUp.isComponentExistInShiftRegister(lc2);
            
            if ((!lc1ExistFlag) && (lc2ExistFlag)) addSource(lc1);
            if ((lc1ExistFlag) && (!lc2ExistFlag)) addDestination(lc2);
        }
    
        findSourceAndDestinationComponentsInDecomposition();
        
        //displaySourceAndDestinationForPromote();
    }

    public static void findSourceAndDestinationComponentsForDemote() {
        init();
        
        if (!BottomUp.isMultiComponentsSelected()) return;
        
        if (BottomUp.isLinksOnly()) return;
        
        BottomUp.updateRegisters();
        
        findSourceAndDestinationComponentsInDecomposition();
    }

    public static void findSourceAndDestinationInGrandParent(GenericComponent gc) {
        initGrandParent();
        
        if (!(gc instanceof LinkableComponent)) return;
        
        LinkableComponent lc = (LinkableComponent) gc;
        
        Vector linkList = lc.getLinks();
        
        for (int i=0; i<linkList.size(); i++) {
            Link link = (Link) linkList.get(i);
            
            LinkableComponent lc1 = link.lc1;
            LinkableComponent lc2 = link.lc2;
            
            if (!lc.equals(lc1)) addSourceInGrandParent(lc1);
            if (!lc.equals(lc2)) addDestinationInGrandParent(lc2);
        }
        
        //displaySourceAndDestinationForDemote();
    }

    private static void addSourceInGrandParent(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        sourceGrandParentTreeMap.put(id2,lc);
    }
    
    private static void addDestinationInGrandParent(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        destinationGrandParentTreeMap.put(id2,lc);
    }

    // find the middle point of the components in decomposition
    public static Point findMiddlePointInDecomposition() {
        LinkableComponent [] lcList = BottomUp.getLinkableComponents();
        
        // find the frame's dimension which containing all the components in decomposition
        // (minX, minY) = top-left corner of the frame
        // (maxX, maxY) = bottom-right corner of the frame
        int minX = -1;
        int minY = -1;
        int maxX = -1;
        int maxY = -1;
        
        for (int i=0; i<lcList.length; i++) {
            LinkableComponent lc = lcList[i];
            
            int x1 = lc.getX();
            int y1 = lc.getY();
            int x2 = lc.getX() + lc.getWidth();
            int y2 = lc.getY() + lc.getHeight();
            
            if (minX < 0) {
                minX = x1;
                minY = y1;
                maxX = x2;
                maxY = y2;
            }
            
            if (x1 < minX) minX = x1;
            if (y1 < minY) minY = y1;
            if (maxX < x2) maxX = x2;
            if (maxY < y2) maxY = y2;
        }
        
        // find the middle point of the frame
        double middleXForFrame = minX + (maxX - minX) / 2;
        double middleYForFrame = minY + (maxY - minY) / 2;
        
        Point result = new Point((int) middleXForFrame, (int) middleYForFrame);
        
        return result;
    }
    
    // find source-only & destination-only components in decomposition
    private static void findSourceAndDestinationComponentsInDecomposition() {
        LinkableComponent [] lcList = BottomUp.getLinkableComponents();
        
        // by default, if without links, all linkableComponent are both source & destination
        // thus add them all to both treeMap initially
        for (int i=0; i<lcList.length; i++) {
            LinkableComponent lc = lcList[i];
            
            addSourceInDecomposition(lc);
            addDestinationInDecomposition(lc);
        }
        
        // remove the components which are not source-only or destination-only
        // in the treeMap from the links
        Link [] linkList = BottomUp.getLinks();
        
        for (int i=0; i<linkList.length; i++) {
            Link link = linkList[i];
            
            LinkableComponent lc1 = link.lc1;  // source component
            LinkableComponent lc2 = link.lc2;  // destination component

            // lc1 = source => not destination-only component
            removeDestinationInDecomposition(lc1);
            
            // lc2 = destination => not source-only component
            removeSourceInDecomposition(lc2);
        }
    }
    
    private static void addSourceInDecomposition(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        sourceDecompositionTreeMap.put(id2,lc);
    }
    
    private static void addDestinationInDecomposition(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        destinationDecompositionTreeMap.put(id2,lc);
    }

    private static void removeSourceInDecomposition(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        if (sourceDecompositionTreeMap.containsKey(id2))
            sourceDecompositionTreeMap.remove(id2);
    }
    
    private static void removeDestinationInDecomposition(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        if (destinationDecompositionTreeMap.containsKey(id2))
            destinationDecompositionTreeMap.remove(id2);
    }
    
    private static void addSource(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        sourceLinkableComponentTreeMap.put(id2,lc);
    }
    
    private static void addDestination(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        destinationLinkableComponentTreeMap.put(id2,lc);
    }

    private static void addPastedSource(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        pastedSourceTreeMap.put(id2,lc);
    }
    
    private static void addPastedDestination(LinkableComponent lc) {
        if (lc == null) return;
        
        Integer id2 = new Integer(lc.ID);
        pastedDestinationTreeMap.put(id2,lc);
    }

    public static void findPastedSourceAndDestination() {
        Set keys = sourceDecompositionTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            
            LinkableComponent lc = PasteManager.findPastedComponentFromOldID(id2);
            addPastedSource(lc);
        }

        keys = destinationDecompositionTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            
            LinkableComponent lc = PasteManager.findPastedComponentFromOldID(id2);
            addPastedDestination(lc);
        }
        
        //displayPastedComponents();
    }
    
    public static void joinSourceAndDestinationLinksInGrandParentIfOK(String grandParentName) {
        if (sourceGrandParentTreeMap.size() >= 1) {
            if (pastedSourceTreeMap.size() == 1) joinSourceLinkInGrandParent(grandParentName);
        }
        
        if (destinationGrandParentTreeMap.size() >= 1) {
            if (pastedDestinationTreeMap.size() == 1) joinDestinationLinkInGrandParent(grandParentName);
        }
    }
    
    private static void joinSourceLinkInGrandParent(String grandParentName) {
        // get the pasted source linkable component
        Integer destinationID = (Integer) pastedSourceTreeMap.firstKey();
        LinkableComponent destination = (LinkableComponent) pastedSourceTreeMap.get(destinationID);
        
        Set keys = sourceGrandParentTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent source = (LinkableComponent) sourceGrandParentTreeMap.get(id2);
            
            // add the link from the source to destination
            NewManager.newLink(source,destination,grandParentName);
        }
    }

    private static void joinDestinationLinkInGrandParent(String grandParentName) {
        // get the pasted destination linkable component
        Integer sourceID = (Integer) pastedDestinationTreeMap.firstKey();
        LinkableComponent source = (LinkableComponent) pastedDestinationTreeMap.get(sourceID);
        
        Set keys = destinationGrandParentTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent destination = (LinkableComponent) destinationGrandParentTreeMap.get(id2);
            
            // add the link from the source to destination
            NewManager.newLink(source,destination,grandParentName);
        }
    }

    public static void joinSourceAndDestinationLinksIfOK(Task newTask, String parentName) {
        if (sourceLinkableComponentTreeMap.size() == 1) {
            if (sourceDecompositionTreeMap.size() == 1) joinSourceLink(newTask,parentName);
        }
        
        if (destinationLinkableComponentTreeMap.size() == 1) {
            if (destinationDecompositionTreeMap.size() == 1) joinDestinationLink(newTask,parentName);
        }
    }
    
    public static boolean isFindPositionOK(Point middlePoint) {
        boolean result = false;
        
        if (sourceLinkableComponentTreeMap.size() != 1) return result;
        if (sourceDecompositionTreeMap.size() != 1) return result;
        
        if (destinationLinkableComponentTreeMap.size() != 1) return result;
        if (destinationDecompositionTreeMap.size() != 1) return result;
        
        result = true;
        
        // get the source linkable component
        Integer id2 = (Integer) sourceLinkableComponentTreeMap.firstKey();
        LinkableComponent source = (LinkableComponent) sourceLinkableComponentTreeMap.get(id2);
        
        // get the destination linkable component
        id2 = (Integer) destinationLinkableComponentTreeMap.firstKey();
        LinkableComponent destination = (LinkableComponent) destinationLinkableComponentTreeMap.get(id2);
        
        // adjust middlePoint to be the middle between the source & destination components
        double x1 = source.getX() + source.getWidth() / 2;
        double y1 = source.getY() + source.getHeight() / 2;
        
        double x2 = destination.getX() + destination.getWidth() / 2;
        double y2 = destination.getY() + destination.getHeight() / 2;

        double x = (x1 + x2) / 2;
        double y = (y1 + y2) / 2;
        
        middlePoint.setLocation(x,y);
        
        return result;
    }

    private static void findMiddlePointSource(Point middlePointSource) {
        double x = 0;
        double y = 0;
        
        Set keys = sourceGrandParentTreeMap.keySet();
        int num = 0;
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) sourceGrandParentTreeMap.get(id2);
            
            double middleX = lc.getX() + lc.getWidth() / 2;
            double middleY = lc.getY() + lc.getHeight() / 2;
            
            x += middleX;
            y += middleY;
            
            num++;
        }

        // find the average of the middle points
        x = x / num;
        y = y / num;
        
        middlePointSource.setLocation(x,y);
    }
    
    private static void findMiddlePointDestination(Point middlePointDestination) {
        double x = 0;
        double y = 0;
        
        Set keys = destinationGrandParentTreeMap.keySet();
        int num = 0;
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) destinationGrandParentTreeMap.get(id2);
            
            double middleX = lc.getX() + lc.getWidth() / 2;
            double middleY = lc.getY() + lc.getHeight() / 2;
            
            x += middleX;
            y += middleY;
            
            num++;
        }

        // find the average of the middle points
        x = x / num;
        y = y / num;
        
        middlePointDestination.setLocation(x,y);
    }
    
    public static boolean isFindPositionInGrandParentOK(Point middlePoint) {
        boolean result = false;
        
        //TMUtility.display("isFindPositionInGrandParentOK","");
        //displayGrandParent();
        //displayPastedComponents();
        
        if (sourceGrandParentTreeMap.size() == 0) return result;
        if (pastedSourceTreeMap.size() != 1) return result;
        
        if (destinationGrandParentTreeMap.size() == 0) return result;
        if (pastedDestinationTreeMap.size() != 1) return result;
        
        result = true;
        
        // get the average of the source linkable components in grand parent
        Point middlePointSource = new Point();
        findMiddlePointSource(middlePointSource);
        
        // get the average of the destination linkable components in grand parent
        Point middlePointDestination = new Point();
        findMiddlePointDestination(middlePointDestination);
        
        double x = (middlePointSource.getX() + middlePointDestination.getX()) / 2;
        double y = (middlePointSource.getY() + middlePointDestination.getY()) / 2;
        
        // adjust middlePoint to be the middle between the source & destination components
        middlePoint.setLocation(x,y);
        
        //TMUtility.display("isFindPositionInGrandParentOK : x",x);
        //TMUtility.display("isFindPositionInGrandParentOK : y",y);
        
        return result;
    }

    private static void joinSourceLink(Task newTask, String parentName) {
        // get the source linkable component
        Integer id2 = (Integer) sourceLinkableComponentTreeMap.firstKey();
        LinkableComponent source = (LinkableComponent) sourceLinkableComponentTreeMap.get(id2);
        
        // add the link from the source to newTask
        NewManager.newLink(source,newTask,parentName);
    }

    private static void joinDestinationLink(Task newTask, String parentName) {
        // get the destination linkable component
        Integer id2 = (Integer) destinationLinkableComponentTreeMap.firstKey();
        LinkableComponent destination = (LinkableComponent) destinationLinkableComponentTreeMap.get(id2);
        
        // add the link from the newTask to destination
        NewManager.newLink(newTask,destination,parentName);
    }

    private static void displayCurrent() {
        Set keys = sourceLinkableComponentTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) sourceLinkableComponentTreeMap.get(id2);
            
            TMUtility.displayLinkableComponent("Source",lc);
        }
        
        keys = destinationLinkableComponentTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) destinationLinkableComponentTreeMap.get(id2);
            
            TMUtility.displayLinkableComponent("Destination",lc);
        }
    }

    private static void displayDecomposition() {
        Set keys = sourceDecompositionTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) sourceDecompositionTreeMap.get(id2);
            
            TMUtility.displayLinkableComponent("Source (Decomposition)",lc);
        }

        keys = destinationDecompositionTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) destinationDecompositionTreeMap.get(id2);
            
            TMUtility.displayLinkableComponent("Destination (Decomposition)",lc);
        }
    }
    
    private static void displaySourceAndDestinationForPromote() {
        TMUtility.display("displaySourceAndDestinationForPromote","");

        // outside BottomUp
        displayCurrent();
        
        // inside BottomUp
        displayDecomposition();
    }

   private static void displayGrandParent() {
        Set keys = sourceGrandParentTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) sourceGrandParentTreeMap.get(id2);
            
            TMUtility.displayLinkableComponent("Source (Grand Parent)",lc);
        }

        keys = destinationGrandParentTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) destinationGrandParentTreeMap.get(id2);
            
            TMUtility.displayLinkableComponent("Destination (Grand Parent)",lc);
        }
    }
    
   private static void displayPastedComponents() {
        Set keys = pastedSourceTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) pastedSourceTreeMap.get(id2);
            
            TMUtility.displayLinkableComponent("Pasted Source",lc);
        }

        keys = pastedDestinationTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) pastedDestinationTreeMap.get(id2);
            
            TMUtility.displayLinkableComponent("Pasted Destination",lc);
        }
    }
    
    private static void displaySourceAndDestinationForDemote() {
        TMUtility.display("displaySourceAndDestinationForDemote","");

        displayDecomposition();
        
        displayGrandParent();
    }
    
}