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

   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 java.util.*;
import java.io.*;

/**
 * This class implements a recursive stucture based on TreeMap.
 * Each node contains a set of couples (GenericComponent,child node)
 * The key to navigate through the tree is the GenericComponent name.
 * We can now use the same name in the branch
 * for different components.
 */
///Node_class
public class Node extends TreeMap implements Serializable {
    // We fetch an entry using a String key.
    // The GenericComponent which is not a key is put in a Couple
    // with the child node.
    
                      
    /**
     * Adds a new branch if <it>element</it> is not already in the node,
     * else erases the former node.(ie two functions: addition and modification)
     */
    public void add(GenericComponent element,Node child) {
    	String name = element.getName();
    	Integer id2 = new Integer(element.ID);
    	Couple couple = new Couple(element,child);
    	
    	if (!containsKey(name)) {
            TreeMap t = new TreeMap();
            t.put(id2,couple);
    	    put(name,t);
    	} else {
    	    TreeMap t = (TreeMap)get(name);
    	    t.put(id2,couple);
    	}
    }
    
    /*public void duplicateNode(Node parentNode) {
        String name;
        TreeMap treeMap;
        Integer id2;
        Couple couple,newCouple;
        Node newNode;
        GenericComponent newGC = null;
        
        for (Iterator i=(parentNode.keySet()).iterator();i.hasNext();) {
            name = (String)i.next();
            treeMap = (TreeMap)(parentNode.get(name));
            
            if (!containsKey(name)) {
                TreeMap t = new TreeMap();
                
                for (Iterator i2=(treeMap.keySet()).iterator();i2.hasNext();) {
                    id2 = (Integer)i2.next();
                    couple = (Couple)(treeMap.get(id2));

                    if (couple.object instanceof Task) {
                        newGC = new Task((Task) couple.object);
                    }
                    
                    if (couple.object instanceof BooleanConnector) {
                        newGC = new BooleanConnector((BooleanConnector) couple.object);
                    }

                    if (couple.object instanceof Link) {
                        Link link = (Link) couple.object;
                        newGC = new Link(link,-1,link.lc1,link.lc2,link.parentName);
                        newGC.ID = link.ID;
                        name = newGC.name;
                    }
                    
                    newNode = new Node();
                    newNode.duplicateNode(couple.node);

                    newCouple = new Couple(newGC,newNode);
                    
                    t.put(id2,newCouple);
                }
                
                put(name,t);
            }
        }
        
        printNode();
    }*/
    
    public void duplicateNode(Node parentNode) {
        if (parentNode == null) return;
        
        String name;
        TreeMap treeMap;
        Integer id2;
        Couple couple;
        Node newNode;
        GenericComponent newGC = null;

        // create a duplicated set of tasks and / or boolean connectors first
        for (Iterator i=(parentNode.keySet()).iterator();i.hasNext();) {
            name = (String)i.next();
            treeMap = (TreeMap)(parentNode.get(name));
            
            for (Iterator i2=(treeMap.keySet()).iterator();i2.hasNext();) {
                id2 = (Integer)i2.next();
                couple = (Couple)(treeMap.get(id2));

                if (couple.object instanceof Task) {
                    newGC = new Task((Task) couple.object);

                    newNode = new Node();
                    newNode.duplicateNode(couple.node);
    
                    add(newGC,newNode);
                }
                
                if (couple.object instanceof BooleanConnector) {
                    newGC = new BooleanConnector((BooleanConnector) couple.object);

                    newNode = new Node();
                    newNode.duplicateNode(couple.node);
    
                    add(newGC,newNode);
                }
            }
        }
        
        // then create a duplicated set of links
        LinkableComponent newlc1,newlc2;

        int linkIndex = GenericComponent.getIndex(Link.REF_STRING);

        for (Iterator i=(parentNode.keySet()).iterator();i.hasNext();) {
            name = (String)i.next();
            treeMap = (TreeMap)(parentNode.get(name));
            
            for (Iterator i2=(treeMap.keySet()).iterator();i2.hasNext();) {
                id2 = (Integer)i2.next();
                couple = (Couple)(treeMap.get(id2));

                if (couple.object instanceof Link) {
                    Link link = (Link) couple.object;
                    
                    newlc1 = findLinkableComponent(link.lc1);
                    newlc2 = findLinkableComponent(link.lc2);
                    
                    newGC = new Link(link,linkIndex++,newlc1,newlc2,link.parentName);
                    newGC.setName(link.getName());
                    newGC.ID = link.ID;

                    newNode = new Node();
                    newNode.duplicateNode(couple.node);
    
                    add(newGC,newNode);
                }
            }
        }
                
        //printNode();
    }

    public void duplicateNode_WithNewIDs(Node parentNode) {
        if (parentNode == null) return;
        
        // treemap to store (oldID2, newID2) of linkable component for links to find
        //   its corresponding components later
        TreeMap idTreeMap = new TreeMap();
        Integer oldID2, newID2;
        
        String name;
        TreeMap treeMap;
        Integer id2;
        Couple couple;
        Node newNode;
        GenericComponent newGC = null;

        // create a duplicated set of tasks and / or boolean connectors first
        for (Iterator i=(parentNode.keySet()).iterator();i.hasNext();) {
            name = (String) i.next();
            treeMap = (TreeMap) parentNode.get(name);
            
            for (Iterator i2=(treeMap.keySet()).iterator();i2.hasNext();) {
                id2 = (Integer) i2.next();
                couple = (Couple)(treeMap.get(id2));

                if (couple.object instanceof Task) {
                    newGC = new Task((Task) couple.object);
                    newGC.setID();
                    
                    idTreeMap.put(id2,new Integer(newGC.ID));

                    newNode = new Node();
                    newNode.duplicateNode_WithNewIDs(couple.node);
    
                    add(newGC,newNode);
                }
                
                if (couple.object instanceof BooleanConnector) {
                    newGC = new BooleanConnector((BooleanConnector) couple.object);
                    newGC.setID();

                    idTreeMap.put(id2,new Integer(newGC.ID));

                    newNode = new Node();
                    newNode.duplicateNode_WithNewIDs(couple.node);
    
                    add(newGC,newNode);
                }
            }
        }
        
        //printNode();
        
        // then create a duplicated set of links
        LinkableComponent newlc1,newlc2;

        int linkIndex = GenericComponent.getIndex(Link.REF_STRING);

        for (Iterator i=(parentNode.keySet()).iterator();i.hasNext();) {
            name = (String)i.next();
            treeMap = (TreeMap)(parentNode.get(name));
            
            for (Iterator i2=(treeMap.keySet()).iterator();i2.hasNext();) {
                id2 = (Integer)i2.next();
                couple = (Couple)(treeMap.get(id2));

                if (couple.object instanceof Link) {
                    Link link = (Link) couple.object;
                    
                    //TMUtility.display("duplicateNode_WithNewIDs : link",link.getName());

                    //TMUtility.display("duplicateNode_WithNewIDs : lc1",link.lc1.getName());
                    //TMUtility.display("duplicateNode_WithNewIDs : lc2",link.lc2.getName());

                    newlc1 = findLinkableComponent(link.lc1,idTreeMap);
                    newlc2 = findLinkableComponent(link.lc2,idTreeMap);
                    
                    //TMUtility.display("duplicateNode_WithNewIDs : newlc1",newlc1.getName());
                    //TMUtility.display("duplicateNode_WithNewIDs : newlc2",newlc2.getName());
                    
                    newGC = new Link(link,linkIndex++,newlc1,newlc2,link.parentName);
                    newGC.setID();

                    newNode = new Node();
                    newNode.duplicateNode_WithNewIDs(couple.node);
    
                    add(newGC,newNode);
                }
            }
        }
                
        //printNode();
    }
    
    private Integer searchID(Integer oldID2, TreeMap idTreeMap) {
        Integer result = null;
        
        if (oldID2 == null) return result;
        
        if (idTreeMap.containsKey(oldID2)) result = (Integer) idTreeMap.get(oldID2);
        
        return result;
    }

    public LinkableComponent findLinkableComponent(LinkableComponent lc, TreeMap idTreeMap) {
        LinkableComponent result = null;
        
        String name = lc.name;
        
        Integer oldID2 = new Integer(lc.ID);
        Integer id2 = searchID(oldID2,idTreeMap);
        
        //TMUtility.display("findLinkableComponent : name",name);
        //TMUtility.display("findLinkableComponent : id2",id2);
        
        GenericComponent tmpGC = getComponent(name,id2);
        
        if (tmpGC instanceof LinkableComponent) result = (LinkableComponent) tmpGC;
        
        return result;
    }
    
    private LinkableComponent findLinkableComponent(LinkableComponent lc) {
        LinkableComponent result = null;
        
        String name = lc.name;
        Integer id2 = new Integer(lc.ID);
        
        GenericComponent tmpGC = getComponent(name,id2);
        
        if (tmpGC instanceof LinkableComponent) result = (LinkableComponent) tmpGC;
        
        return result;
    }
    
    private void printLinkDetails(Link link) {
        System.out.print("--- ("+link.lc1.getName()+","+link.lc2.getName()+")");
    }
    
    public void printNode() {
        String name;
        
        Integer id2,objectID;
        Couple tmpCouple;
        
        System.out.print("printNode : {");
        for (Iterator i=(keySet()).iterator();i.hasNext();) {
             name = (String) i.next();
             System.out.print(name+" : [");
             
             for (Iterator itID = getIDIterator(name);itID.hasNext();) {
                id2 = (Integer)(itID.next());
                
                tmpCouple = getCouple(name,id2);
                objectID = new Integer(tmpCouple.object.ID);
                
                if (id2.equals(objectID))
                    System.out.print(id2+", ");
                else
                    System.out.print(id2+"("+objectID+"), ");
                    
                if (tmpCouple.object instanceof Link) printLinkDetails((Link) tmpCouple.object);
             }
            
             System.out.print("]; ");
        }
        System.out.println("}");
    }
    
    public void print2ndNode() {
        for (Iterator i=(keySet()).iterator();i.hasNext();) {
             String name = (String) i.next();
             
             for (Iterator itID = getIDIterator(name);itID.hasNext();) {
                 Integer id2 = (Integer) itID.next();
                 Couple tmpCouple = getCouple(name,id2);
                 Node node = tmpCouple.node;
                
                 if (node.size() > 0) node.printNode();
             }
        }
    }
    
    public void print3rdNode() {
        for (Iterator i=(keySet()).iterator();i.hasNext();) {
             String name = (String) i.next();
             
             for (Iterator itID = getIDIterator(name);itID.hasNext();) {
                 Integer id2 = (Integer) itID.next();
                 Couple tmpCouple = getCouple(name,id2);
                 Node node = tmpCouple.node;
                
                 if (node.size() > 0) node.print2ndNode();
             }
        }
    }
    
    /**
     * Add a leaf. But erase the branch if there is one associated with
     * the key (A branch can be lost).
     */
    public void add(GenericComponent element) {
    	String name = element.getName();
    	Integer id2 = new Integer(element.ID);
    	Couple couple = new Couple(element,new Node());
    	
    	if (!containsKey(name)) {
            TreeMap t = new TreeMap();
            t.put(id2,couple);
    	    put(name,t);
    	} else {
    	    TreeMap t = (TreeMap)get(name);
    	    t.put(id2,couple);
    	}
    }
    
    public void putCouple(Couple couple) {
    	add(couple.object,couple.node);
    }

    /**
     * Modify the node of name to point to node if <it>name</it> is a key.
     */
    public boolean modify(String name,Node node) {
    	boolean returnValue = false;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		Couple couple = (Couple)(t.get(t.firstKey()));
    		
    	    GenericComponent obj = couple.object;
    	    
    	    add(obj,node);
    	    
    	    returnValue=true;
    	}
    	
    	return returnValue;
    }
    
    public boolean modify(String name,Node node,Integer id2) {
    	boolean returnValue = false;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);

    		if (t.containsKey(id2)) {
	    		Couple couple = (Couple)t.get(id2);
    		
	    	    GenericComponent obj = couple.object;
    	    
    		    add(obj,node);
    	    
    		    returnValue=true;
    		}
    	}
    	
    	return returnValue;
    }
    
    /**
     * Modify the component if <it>name</it> is a key.
     */
    public boolean modify(GenericComponent c) {
    	boolean returnValue = false;
    	
    	String name = c.getName();
    	Integer id2 = new Integer(c.ID);
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		
    		if (t.containsKey(id2)) {
	    		Couple couple = (Couple)t.get(id2);
	
	    		Node node = couple.node;
	    		
	    		add(c,node);
	    	    
	    	    returnValue=true;
    		}
    	}
    	
    	return returnValue;
    }
    
    /**
     * Remove an entry from the node
     */
    public Couple removeCouple(String name) {
    	Couple result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		result = (Couple)(t.get(t.firstKey()));

    		remove(name);
    	}
    	
    	return result;
    }
        
    public Couple removeCouple(String name,Integer id2) {
    	Couple result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);

    		if (t.containsKey(id2)) {
	    		result = (Couple)t.get(id2);

	    		t.remove(id2);
	    		
	    		if (t.isEmpty())
	    			remove(name);
    		}
    	}
    	
    	return result;
    }

    // e.g. if index = 2, return the ID of the 2nd duplicated task
    public Integer getID(String name,int index) {
        Integer result = null;
        
        if (containsKey(name)) {
            TreeMap t = (TreeMap)get(name);

            Set keys = t.keySet();                    // get all IDs of name
            int i = 1;
            for (Iterator it = keys.iterator();it.hasNext();) {
                result = (Integer)it.next();
                   
                i++;
                if (i > index) break;
            }
        }
        
        return result;
    }
    
    public boolean hasOtherDuplicatedTask(String name) {
    	boolean returnValue = false;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);

    		Set keys = t.keySet();					// get all IDs of name

    	    if (keys.size() > 1) returnValue = true;
    	}
    	
    	return returnValue;
    }
    
    /**
     * Returns true if <it>name</it> exists and if it is a leaf.
     */
    public boolean hasLeaf(String name) {
    	boolean returnValue = false;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		Couple couple = (Couple)(t.get(t.firstKey()));

    		returnValue = (couple.node.isEmpty());
    	}
    	
    	return returnValue;
    }
    
    public boolean hasLeaf(String name,Integer id2) {
    	boolean returnValue = false;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		
    		if (t.containsKey(id2)) {
    			Couple couple = (Couple)t.get(id2);

	    		returnValue = (couple.node.isEmpty());
    		}
    	}
    	
    	return returnValue;
    }
    
    /**
     * Returns true if <it>element</it> exists and if it is a leaf.
     */
    public boolean hasLeaf(GenericComponent element) {
    	boolean returnValue = false;
    	
    	String name = element.getName();
    	Integer id2 = new Integer(element.ID);
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		
    		if (t.containsKey(id2)) {
    			Couple couple = (Couple)(t.get(id2));

    			returnValue = (couple.node.isEmpty());
    		}
    	}
    	
    	return returnValue;
    }
    
    /**
     * Returns true if <it>name</it> exists and if it is a branch.
     */
    public boolean hasBranch(String name) {
    	boolean returnValue = false;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		Couple couple = (Couple)(t.get(t.firstKey()));

    		returnValue = (!(couple.node.isEmpty()));
    	}
    	
    	return returnValue;
    }
    
    public boolean hasBranch(String name,Integer id2) {
    	boolean returnValue = false;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);

    		if (t.containsKey(id2)) {
    			Couple couple = (Couple)t.get(id2);

	    		returnValue = (!(couple.node.isEmpty()));
    		}
    	}
    	
    	return returnValue;
    }
    
    /**
     * Returns true if <it>element</it> exists and if it is a branch.
     */
    public boolean hasBranch(GenericComponent element) {
    	boolean returnValue = false;
    	
    	String name = element.getName();
    	Integer id2 = new Integer(element.ID);
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		
    		if (t.containsKey(id2)) {
    			Couple couple = (Couple)(t.get(id2));

    			returnValue = (!(couple.node.isEmpty()));
    		}
    	}
    	
    	return returnValue;
    }
    
    public GenericComponent [] getComponentList(String name) {
        GenericComponent [] result = null;
        
        if (containsKey(name)) {
            TreeMap t = (TreeMap) get(name);
            
            result = new GenericComponent[t.size()];
        
            Set keys = t.keySet();
            int index = 0;
            for (Iterator it = keys.iterator();it.hasNext();) {
                Integer id2 = (Integer) it.next();
                Couple couple = (Couple) t.get(id2);
                result[index++] = couple.object;
            }
        }
        
        return result;
    }
    
    public GenericComponent [] getComponentList() {
        GenericComponent [] result = null;
        
        result = new GenericComponent[getNumCouples()];
        
        Set keys = keySet();
        int index = 0;
        
        for (Iterator it = keys.iterator();it.hasNext();) {
            String name = (String) it.next();
            TreeMap treemapName = (TreeMap) get(name);
            
            Set keysName = treemapName.keySet();
            for (Iterator itName = keysName.iterator();itName.hasNext();) {
                Integer id2 = (Integer) itName.next();
                Couple couple = (Couple) treemapName.get(id2);
                result[index++] = couple.object;
            }
        }
        
        return result;
    }
    
    public int getNumCouples() {
        int result = 0;
        
        Set keys = keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
               String name = (String) it.next();
        
               result += getNumCouples(name);
        }
        
        return result;
    }
    
    /**
     * Returns the GenericComponent whose name is <it>name</it>.
     */
    public GenericComponent getComponent(String name) {
    	GenericComponent result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		Couple couple = (Couple)(t.get(t.firstKey()));

    		result = couple.object;
    	}
    	
    	return result;
    }
    
    public GenericComponent getComponent(String name,Integer id2) {
    	GenericComponent result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);

    		if (t.containsKey(id2)) {
    			Couple couple = (Couple) t.get(id2);

	    		result = couple.object;
    		}
    	}
    	
    	return result;
    }
    
    public boolean isLink(String name,Integer id2) {
        boolean result = false;
        
        if (containsKey(name)) {
            TreeMap t = (TreeMap)get(name);

            if (t.containsKey(id2)) {
                Couple couple = (Couple)t.get(id2);

                if (couple.object instanceof Link) result = true;
            }
        }
        
        return result;
    }
    
    /**
     * Returns the Node whose name is <it>name</it>.
     */
    public Node getChild(String name) {
    	Node result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap) get(name);
    		Couple couple = (Couple)(t.get(t.firstKey()));

    		result = couple.node;
    	}
    	
    	return result;
    }
    
    public Integer getChildID(String name) {
    	Integer result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		result = (Integer)t.firstKey();
    	}
    	
    	return result;
    }
    
    public Node getChild(String name,Integer id2) {
    	Node result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);

    		if (t.containsKey(id2)) {
    			Couple couple = (Couple)t.get(id2);

    			result = couple.node;
    		}
    	}
    	
    	return result;
    }
    
    public void setChild(String name,Integer id2,Node node) {
        if (containsKey(name)) {
            TreeMap t = (TreeMap)get(name);

            if (t.containsKey(id2)) {
                Couple couple = (Couple)t.get(id2);

                couple.node = node;
            }
        }
    }
    
    public boolean hasChild(String name,Integer id2) {
    	boolean result = false;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);

    		if (t.containsKey(id2)) {
    			result = true;
    		}
    	}
    	
    	return result;
    }
    
    /**
     * Returns the Couple whose name is <it>name</it>.
     */
    public Couple getCouple(String name) {
    	Couple result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		result = (Couple)(t.get(t.firstKey()));
    	}
    	
    	return result;
    }
    
    public Couple getCouple(String name,Integer id2) {
    	Couple result = null;
    	
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		
    		if (t.containsKey(id2)) {
    			result = (Couple)t.get(id2);
    		}
    	}
    	
    	return result;
    }
    
    public Iterator getIDIterator(String name) {
    	Iterator result = null;

    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		
    	    Set keys = t.keySet();					// get all IDs of name

    	    result = keys.iterator();
    	}
    	
    	return result;
    }

    // return position of id2 of its duplicated tasks in the same window where the first component starts from 1
    public int getPositionForDuplicatedTasks(String name,Integer id2) {
    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		
    	    Set keys = t.keySet();					// get all IDs of name

    	    Integer tempID2;
    	    int result = 0;
	        for (Iterator it = keys.iterator();it.hasNext();) {
    	       	tempID2 = (Integer)it.next();
    	       	
    	       	result++;
    	       	if (tempID2.equals(id2))
    	       		return result;
	        }
    	}
    	
    	return 0;
    }
    
    // return index for all tasks within the same window where the first component starts from 0
    public int getTreeIndex(String name,Integer id2) {
	    int result = 0;

	    if (containsKey(name)) {
	    	Set keys = keySet();
	    	
		    String tempName;
	        for (Iterator it = keys.iterator();it.hasNext();) {
		       	tempName = (String)it.next();
		       	
		       	result++;
		       	if (tempName.equals(name))
		       		break;
	        }
	        result--;
	        
	        result += getPositionForDuplicatedTasks(name,id2) - 1;   //first component starts from 0
    	}

    	if (result<0) result = 0;
    	
    	return result;
    }
    
    public int getNumCouples(String name) {
    	int result = 0;

    	if (containsKey(name)) {
    		TreeMap t = (TreeMap)get(name);
    		
    	    Set keys = t.keySet();					// get all IDs of name

    	    result = keys.size();
    	}
    	
    	return result;
    }
    
}