package deadlock.analyser.detection;

import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.stringtemplate.v4.misc.STNoSuchAttributeException;

import deadlock.analyser.factory.Contract;
import deadlock.analyser.factory.GroupName;
import deadlock.analyser.factory.MainMethodContract;
import deadlock.analyser.factory.MethodContract;
import deadlock.analyser.factory.MethodInterface;
import deadlock.analyser.factory.IRecord;
import com.gzoumix.semisolver.term.Term;


// a BigLam is a quadruple <methodName, methodContract, Lam w, Lam wPrime>
// and it contains the set of variable bTilde (new fresh name created in the method) and the set of variable aTilde (formal parameter of a method)

public class BigLam {

    String methodName;
    Term methodContract;
    Set<GroupName> bTilde;
    Set<GroupName> aTilde;

    VarSubstitution lastBFresh;


    Lam w;
    Lam wPrime;

 // constructor from methodName and methodContract
    public BigLam(String method, MainMethodContract methodContractInferred){
        this.methodName = method;
        this.methodContract = methodContractInferred;

        this.lastBFresh = new VarSubstitution();

        this.w = new Lam();
        this.wPrime = new Lam();

        this.bTilde = new TreeSet<>();
        this.aTilde = new TreeSet<>();
    }

    // constructor from methodName and methodContract
    public BigLam(String method, Term methodContractInferred){
        //initialize the lam for the method MethodName, and use this MethodContract obtained form inference algorithm to produce
        //bTilde

        this.methodName = method;
        this.methodContract = methodContractInferred;

        this.lastBFresh = new VarSubstitution();

        this.w = new Lam();
        this.wPrime = new Lam();


        if(methodContractInferred instanceof MethodContract){
            MethodInterface methodInterface = ((MethodContract) methodContractInferred).getMethodInterface();
            Contract contractP = ((MethodContract) methodContractInferred).getContractPresent();
            Contract contractF = ((MethodContract) methodContractInferred).getContractPresent();

            IRecord _this = methodInterface.getThis();
            IRecord ret =  methodInterface.getResult();


            List<IRecord> args = methodInterface.getParameters();

            Set<GroupName> bTildeTemp = contractP.fn();
            bTildeTemp.addAll(contractF.fn());

            this.bTilde = bTildeTemp;
            this.bTilde.addAll(ret.fn());
            this.bTilde.removeAll(_this.fn());
            this.aTilde = new TreeSet<>();
            this.aTilde.addAll(_this.fn());
            for(IRecord t : args){
                this.bTilde.removeAll(t.fn());
                this.aTilde.addAll(t.fn());
            }
        }
        else {
            this.bTilde = new TreeSet<>();
            this.aTilde = new TreeSet<>();
        }

    }

    //Getter and Setter for Lam
    public Lam getFirst(){
        return this.w;
    }

    public Lam getSecond(){
        return this.wPrime;
    }

    public void setFirst(Lam l){
        Lam l2 = l.minimize();
        this.w = l2;
    }

    public void setSecond(Lam l){
        Lam l2 = l.minimize();
        this.wPrime = l2;
    }

    //Getter for aTilde and bTilde
    public Set<GroupName> getbTilde(){
        return this.bTilde;
    }

    public Set<GroupName> getaTilde(){
        return this.aTilde;
    }

    //Getter for lastBFresh used for saturation
    public VarSubstitution getLastBFresh(){
        return this.lastBFresh;
    }

    public void setLastBFresh(VarSubstitution sub){
        this.lastBFresh = sub;
    }


    //Getter for methodName and methodContract
    public String getMethodName(){
        return this.methodName;
    }

    public Term getMethodContract(){
        return this.methodContract;
    }

    //Return the freeVariable of a BigLamp
    public Set<GroupName> fv(){
        Set<GroupName> fv = new TreeSet<>();
        fv.addAll(this.w.fv());
        fv.addAll(this.wPrime.fv());
        return fv;
    }

    //check for Cycle
    public Boolean hasCycle(){
        //there is a Cycle when one of the two cycle types is present
        return w.hasCycle() || wPrime.hasCycle();
    }



    //check just for Get Cycle
    public Boolean hasCycleGet(){
        //it has cycle if any of the two lamps is cyclic
        return w.hasCycleGet() || wPrime.hasCycleGet();
    }



    //check just for Await Cycle
    public Boolean hasCycleAwait(){
      //it has cycle if any of the two lamps is cyclic
        return w.hasCycleAwait() || wPrime.hasCycleAwait();
    }

    //toString method
    public String toString(){
        return "< \n" + this.w.toString() + " , \n" + this.wPrime.toString() + ">";
    }

    //According to version2 of the fix point algorithm this method adds the missing dependencies from the transitive closure
    //of the dependencies of not new names and removes all dependencies with new names
    public void expandAndClean() {

        w.expandAndClean();
        wPrime.expandAndClean();
    }

    public Boolean hasReflexiveState() {
        // TODO Auto-generated method stub
        return w.hasReflexiveState() || wPrime.hasReflexiveState();
    }

    public LinkedList<State> getReflexiveStates(){
        LinkedList<State> res = new LinkedList<>();
        res.addAll(w.getReflexiveStates());
        res.addAll(wPrime.getReflexiveStates());
        return res;
    }


}
