/*
 * LTS.java
 *
 * Created on 23 July 2007, 23:13
 */

package assignment1;

import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import java.util.*;
import java.lang.*;
import java.io.*;

/**
 *
 * @author Davd
 */
public class LTS {
   
    protected String fileName;
    protected LTSNode startNode;
    protected ArrayList<LTSTransition> transitionList;
    
    /** Creates a new instance of LTS */
    public LTS() {
        transitionList = new ArrayList<LTSTransition>();
    }
    
    /** Sets the filename in use. This should only really be used by the load function - if you
     * want to save as a different file name use the save(string) function.
     */
    public void setFileName(String filename) {
        fileName = filename;
    }
    
    /** Returns the start node for this LTS
     */
    public LTSNode getStartNode() {
        return startNode;
    }
    
    /** Sets the start node for this LTS
     */
    public void setStartNode(LTSNode start) {
        startNode = start;
    }
    
    /** Returns true if the specified transition exists in this LTS.
     */
    public boolean transitionExists(LTSTransition ltst) {
        //return transitionList.contains(ltst);
        boolean found = false;
        for (int i = 0; i < transitionList.size(); i++) {
            if (transitionList.get(i).equals(ltst))
                found = true;
        }
        return found;
    }
    
    /** Adds the specified transition to this LTS
     */
    public void addTransition(LTSTransition ltst) {
        transitionList.add(ltst);
    }
    
    /** Removes the specified transition from this LTS.
     */
    public void removeTransition(LTSTransition ltst) {
        transitionList.remove(ltst);
    }
        
    public void killTransitions() {
        transitionList.clear();
    }
    
    /** Returns a list of all transitions in this LTS.
     */
    public Collection<LTSTransition> getTransitions() {
        return (Collection<LTSTransition>)transitionList.clone();
    }
    
    public Collection<LTSNode> getNodes() {
        Collection<LTSTransition> tl = getTransitions();
        ArrayList<LTSNode> output = new ArrayList<LTSNode>();
        
        HashSet<LTSNode> hs = new HashSet<LTSNode>();
        
        Iterator it = tl.iterator();
        
        while (it.hasNext()) {
            LTSTransition lt = (LTSTransition) it.next();
            
            if (lt.getNode1() != null)
                hs.add(lt.getNode1());
            if (lt.getNode2() != null)
                hs.add(lt.getNode2());
        }
        
        ArrayList<LTSNode> nl = new ArrayList<LTSNode>();
        
        nl.addAll(hs);
        
        return nl;
    }
    
    /** Loads an LTS from a text file.
     */
    public static LTS load(String filename) throws IOException {
        System.out.println("LTS: Attempting to load file \"" + filename +"\"");
        
        LTS theLTS = new LTS();
        theLTS.setFileName(filename);
        
        FileInputStream fin;		

        fin = new FileInputStream (filename);
        BufferedReader breader = new BufferedReader(new InputStreamReader(fin));            
        
        String startNodeName = "";
        
        String curlin = breader.readLine();
        
        while (curlin != null) {
            if (curlin.startsWith("#")) {
                // Its a comment, do nothing
            }
            else {
                if (startNodeName == "") {
                    startNodeName = curlin;
                    theLTS.setStartNode(new LTSNode(startNodeName));
                }
                else {
                    String node1;
                    String node2;
                    String name;

                    String[] stringList = curlin.split("/");

                    node1 = stringList[0];
                    if (stringList.length > 1) {
                        name = stringList[1];
                        node2 = stringList[2];

                        theLTS.addTransition(new LTSTransition(new LTSNode(node1),new LTSNode(node2),name));
                    }
                    else {
                        theLTS.addTransition(new LTSTransition(new LTSNode(node1),null,""));
                    }
                }
            }
            
            curlin = breader.readLine();
        }



        fin.close();		

        
        return theLTS;
    }
    
    /** Saves the LTS to the current file ("Save")
     */
    public void save() throws IOException {
        save(getFileName());
    }
    
    /** Saves the LTS to the specified file ("Save As")
     */
    public void save(String filename) throws IOException {
        setFileName(filename);  // Just in case the file name is different.
        FileOutputStream fout;		


        fout = new FileOutputStream (getFileName());
        PrintStream ps = new PrintStream(fout);
        
        // Print the start node:
        ps.println (getStartNode().getName());
        
        // Print the transitions:
        for (int i = 0; i < transitionList.size(); i++) {
            ps.println(transitionList.get(i).toString());
        }
        
        fout.close();		

    }
    
    /** Compares the LTS with another.
     *@returns true if the two LTS are equal.
     */
    public boolean equals(LTS theLTS){
        // Ensure they have the same start nodes
        String lSN = theLTS.getStartNode().getName();
        String lSNN = getStartNode().getName();
        if (!lSN.equals(lSNN))
            return false;
        
        // If one has more transitions than the other they cant be equal.
        if (theLTS.getTransitions().size() != getTransitions().size())
            return false;
        
        // Ensure they have the same transitions
        for (int i = 0; i < transitionList.size();i++){
            LTSTransition t = transitionList.get(i);
            boolean te = theLTS.transitionExists(t);
            if (!te)
                return false;
        }
        return true;
    }
    
    /** Composes the given LTS with this one
     */
    public void compose(LTS theLTS, HashSet syncSet) {
        /* Here we must compose the given LTS with this one. We must do the following:
         */        
        
        //********** Abstraction: Incase we want to make this static
        LTS ltsA = this;
        LTS ltsB = theLTS;
        
        // Get a list of nodes in each LTS
        Collection<LTSNode> nA = ltsA.getNodes();
        Collection<LTSNode> nB = ltsB.getNodes();
        
        // Get a list of transitions in each LTS
        Collection<LTSTransition> tA = ltsA.getTransitions();
        Collection<LTSTransition> tB = ltsB.getTransitions();
        
        // Get start nodes
        LTSNode sA = ltsA.getStartNode();
        LTSNode sB = ltsB.getStartNode();
        
        //********** Abstraction: Incase we want to make this static
        // Clear out this LTS ready for reconstruction
        ltsA.killTransitions();
        
        // Do some transition composition!!!
        Iterator it = nB.iterator();
        Iterator itt = tA.iterator();
        while (itt.hasNext()){
            LTSTransition t = (LTSTransition)itt.next();
            if (t.getNode2() != null)
            while (it.hasNext()) {
                LTSNode i = (LTSNode)it.next();
                //if (t.getNode2() != null)
                    ltsA.addTransition(compTransition(t,i));
            }
        }
        Iterator ittt = nA.iterator();
        Iterator itttt = tB.iterator();
        while (itttt.hasNext()){
            LTSTransition t = (LTSTransition)itttt.next();
            while (ittt.hasNext()) {
                LTSNode i = (LTSNode)ittt.next();
               // if (t.getNode2() != null)
                    ltsA.addTransition(compTransition(t,i));
            }
        }
        
        // And lastly, do the start node.
        ltsA.setStartNode(new LTSNode("(" + sB.getName() + "," + sA.getName() + ")"));
    }
    
    /** Composes a transition with a node. Used internally for the composition functions.
     */
    protected static LTSTransition compTransition(LTSTransition lt, LTSNode ln){
        LTSNode tnA = lt.getNode1();
        LTSNode tnB = lt.getNode2();
        
        LTSNode nnA = new LTSNode("(" + tnA.getName() + "," + ln.getName() + ")");
        LTSNode nnB = new LTSNode("(" + tnB.getName() + "," + ln.getName() + ")");
        
        LTSTransition nt = new LTSTransition();
        
        nt.setNode1(nnA);
        nt.setNode2(nnB);
        
        nt.setName(lt.getName());
        
        return nt;
    }

    /** Composes two LTS and returns the result. Clones lts_a and the composes it with lts_b returning the result.
     * @returns The composition of lts_a and lts_b
     */
    public static LTS compose(LTS lts_a, LTS lts_b, HashSet syncSet) {
        try {
            LTS tempLTS = (LTS)lts_a.clone();
            tempLTS.compose(lts_b, syncSet);
            return tempLTS;
        }
        catch (Exception e) {
            return null;
        }

    }
    
    /** Returns the name of the file this transition was loaded from or saved to.
     */
    public String getFileName() {
        return fileName;
    }
    
    
}
