/*
   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;


// REMOVE LEVEL
public class DemoteManager {
    
    // select a single task with decomposition to remove level
    public static void demote() {
        if (!isSelectDecompositionInTask_To_BottomUp_OK()) return;
        
        // here needs to select multiple components in decomposition to remove level
        // if not multiple components selected, error message
        if (!BottomUp.isMultiComponentsSelected()) {
            MessageDialog.errorDialog(MainFrame.mainFrame,"Operation not allowed",
                "Multiple components not selected.");
            
            return;
        }
        
        if (!PromoteManager.pointToParentTask()) return;
        
        String parentName = BottomUp.lastParentName;
        
        if (isRootLevel(parentName)) return;
        
        SourceDestinationManager.findSourceAndDestinationComponentsForDemote();
        
        // cut multiple components selected to clipboard
        if (!CutManager.multipleCut(false)) return;
        
        if (TMClipboard.isEmpty()) return;
            
        GenericComponent [] gcList = Manager.skeleton.getComponents(parentName);

        for (int i=0; i<gcList.length; i++) {
            GenericComponent gc = gcList[i];
            
            Integer id2 = new Integer(gc.ID);
            
            String grandParentName = Manager.skeleton.getParent(parentName,id2);
            String grandGrandParentName = Manager.skeleton.getFirstParent(grandParentName);
            TreePath grandParentTreePath = Manager.fetchTreePath(grandParentName,Manager.tree);
            
            //TMUtility.display("demote : parentName",parentName);
            //TMUtility.display("demote : grandParentName",grandParentName);
            //TMUtility.display("demote : grandGrandParentName",grandGrandParentName);
            
            SourceDestinationManager.findSourceAndDestinationInGrandParent(gc);
        
            Point middlePointOfParent = findMiddlePointOfParent(gc);
                
            // delete parent task of name parentName
            DeleteManager.deleteGenericComponent(gc);
            
            // paste from the clipboard to the parent task's level
            PasteManager.multiplePaste(-1,-1,true,grandParentTreePath,grandParentName,grandGrandParentName);
            
            SourceDestinationManager.findPastedSourceAndDestination();
            
            adjustTaskPositions(grandParentName,middlePointOfParent);
            
            SourceDestinationManager.joinSourceAndDestinationLinksInGrandParentIfOK(grandParentName);
        }
    }
    
    private static boolean isSelectDecompositionInTask_To_BottomUp_OK() {
        boolean result = false;
        
        if (BottomUp.isMultiComponentsSelected()) return result;

        if (Manager.currentComponent == null) {
            //TMUtility.display("isSelectDecompositionInTask_To_BottomUp_OK","Task not selected !");
            return result;
        }
        
        if (!(Manager.currentComponent instanceof Task)) return result;
        
        // get the details of the selected task
        Task task = (Task) Manager.currentComponent;
        
        //TMUtility.displayLinkableComponent("isSelectDecompositionInTask_To_BottomUp_OK",task);
        
        Integer id2 = new Integer(task.ID);
        String name = task.getName();
        
        String parentName = Manager.skeleton.getParent(name,id2);
        
        // include all the components in the decomposition of the selected task to BottomUp
        Node currentNode = Manager.skeleton.toChildNode(name,parentName,id2);
        if (currentNode == null) return result;
        
        BottomUp.resetAllSelection();

        GenericComponent [] gcChildrenList = currentNode.getComponentList();
        if (gcChildrenList == null) return result;
        
        result = true;
        
        for (int i=0; i<gcChildrenList.length; i++) {
            GenericComponent gcChild = gcChildrenList[i];

            //TMUtility.displayGenericComponent("isSelectDecompositionInTask_To_BottomUp_OK",gcChild);
            
            BottomUp.select(gcChild,name);
        }

        return result;
    }

    // can't remove level at the root level
    private static boolean isRootLevel(String parentName) {
        boolean result = false;
        
        //TMUtility.display("isRootLevel : parentName",parentName);
        
        if (parentName.equals(Manager.ROOT_STRING)) {
            JOptionPane.showMessageDialog(MainFrame.mainFrame,
                "It is not allowed to remove at the root level.\n\n",
                "Error - Remove at Root Level", JOptionPane.ERROR_MESSAGE);
            
            result = true;
        }
        
        return result;
    }

    private static Point findMiddlePointOfParent(GenericComponent gc) {
        int minX = gc.getX();
        int minY = gc.getY();
        int maxX = gc.getX() + gc.getWidth();
        int maxY = gc.getY() + gc.getHeight();
        
        // 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;
    }
    
    private static void backupXYForPastedComponents(TreeMap pastedTreeMap, TreeMap backupXYTreeMap) {
        Set keys = pastedTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) pastedTreeMap.get(id2);
            
            int backupX = lc.getX();
            int backupY = lc.getY();
            
            Point point = new Point(backupX,backupY);

            backupXYTreeMap.put(id2,point);
        }
    }

    private static void restoreXYForPastedComponents(TreeMap pastedTreeMap, TreeMap backupXYTreeMap) {
        Set keys = pastedTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) pastedTreeMap.get(id2);
            
            if (backupXYTreeMap.containsKey(id2)) {
                Point backup = (Point) backupXYTreeMap.get(id2);
                
                int backupX = (int) backup.getX();
                int backupY = (int) backup.getY();

                setLocation(lc,backupX,backupY);
            }
        }
    }
    
    private static Point findOffset(TreeMap pastedTreeMap, Point middlePoint) {
        // find the frame's dimension which containing all the pasted components
        // (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;
        
        Set keys = pastedTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) pastedTreeMap.get(id2);
            
            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;
        }
        
        //TMUtility.display("findOffset minX",minX);
        //TMUtility.display("findOffset minY",minY);
        //TMUtility.display("findOffset maxX",maxX);
        //TMUtility.display("findOffset maxY",maxY);
        
        // find the middle point of the frame
        double middleXForFrame = minX + (maxX - minX) / 2;
        double middleYForFrame = minY + (maxY - minY) / 2;
        
        // find the offset distance using Point as the storage
        double offsetX = middlePoint.getX() - middleXForFrame;
        double offsetY = middlePoint.getY() - middleYForFrame;
        
        // adjust the offset if the coordinates of at least one of the pasted component is -ve
        final int MIN_OFFSET = 10;  // minimum offset from the left-edge & the top-edge of the window
        keys = pastedTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) pastedTreeMap.get(id2);
            
            // shift the pasted component with the specified offset distance
            double x = lc.getX() + offsetX;
            double y = lc.getY() + offsetY;
            
            if (x < MIN_OFFSET) offsetX = MIN_OFFSET - lc.getX();
            if (y < MIN_OFFSET) offsetY = MIN_OFFSET - lc.getY();
        }
        
        Point result = new Point((int) offsetX, (int) offsetY);
        
        //TMUtility.display("findOffset offsetX",offsetX);
        //TMUtility.display("findOffset offsetY",offsetY);
        
        return result;
    }
    
    private static boolean isPutPastedComponentsInMiddleOK(TreeMap pastedTreeMap, Point offset) {
        boolean result = true;
        
        Set keys = pastedTreeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) pastedTreeMap.get(id2);
            
            // shift the pasted component with the specified offset distance
            double x = lc.getX() + offset.getX();
            double y = lc.getY() + offset.getY();
            
            if ((x < 0) || (y < 0)) {
                result = false;
                break;
            }
            
            setLocation(lc, (int) x, (int) y);
        }
        
        return result;
    }
    
    private static void setLocation(LinkableComponent lc, int x, int y) {
        lc.setLocation(x,y);
        
        lc.backupX = lc.getX();
        lc.backupY = lc.getY();
    }
    
    // adjust the position of the newly pasted tasks to be between the source and the destination tasks
    // if the positions are originally empty
    private static void adjustTaskPositions(String grandParentName, Point middlePointOfParent) {
        // (1) find the middle point between the source and the destination tasks
        Point middlePoint = new Point();
        
        if (!SourceDestinationManager.isFindPositionInGrandParentOK(middlePoint)) {
            //TMUtility.display("adjustTaskPositions","Try to put in middle of parent.");

            // try to put the pasted components in the middle of parent if not obstructed
            middlePoint = middlePointOfParent;
            
            //return;
        }
        
        // (2) get the pasted treemap
        // id2 (key), lc (value) --- the pasted component
        TreeMap pastedTreeMap = PasteManager.getPastedTreeMap();
 
        // (3) backup the x,y coordinates of the pasted lc in pastedTreeMap
        // id (key), Point (value)
        TreeMap backupXYTreeMap = new TreeMap();
        backupXYForPastedComponents(pastedTreeMap,backupXYTreeMap);
        
        // (4) find offset x & y distance for the pasted components to shift to
        Point offset = findOffset(pastedTreeMap,middlePoint);
        
        // (5) put the pasted components whose frame's middle is at the middlePoint
        boolean okFlag = isPutPastedComponentsInMiddleOK(pastedTreeMap,offset);
        
        // (6) if the new position intersects with the other component,
        // restore the newTask to its old position
        ComponentInternalFrame frame = (ComponentInternalFrame) Manager.desktop.fetchFrame(grandParentName);
        if (frame == null) return;
        if (frame.gpanel == null) return;
        
        if (MultipleComponents.intersection(pastedTreeMap,frame.gpanel) || (!okFlag)) {
            //TMUtility.display("adjustTaskPosition","intersected --- restored to original position.");
            restoreXYForPastedComponents(pastedTreeMap,backupXYTreeMap);
        }
    }
    
}