package accrue.analysis.ctldep;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import polyglot.visit.DataFlow.Item;
import accrue.analysis.interprocanalysis.EdgeIdentifier;
import accrue.analysis.interprocvarcontext.AbstractValue;

public class CDAbsVal extends Item implements AbstractValue<CDAbsVal> {
    public static final CDAbsVal STARTPROC = new CDAbsVal(null,
            Collections.<EdgeIdentifier> emptySet());

    public static final CDAbsVal BOTTOM = new CDAbsVal(null,
            Collections.<EdgeIdentifier> emptySet()) {
        @Override
        public boolean isBottom() {
            return true;
        }
    };

    protected final Set<EdgeIdentifier> controlDeps;
    protected CDAbsVal parent;
    
    public void setParent(CDAbsVal parent) {
        this.parent = parent;
    }
    public CDAbsVal(CDAbsVal parent, Set<EdgeIdentifier> controlDeps) {
        this.controlDeps = Collections.unmodifiableSet(controlDeps);
        this.parent = parent;
    }

    public CDAbsVal(CDAbsVal parent, EdgeIdentifier ctlDep) {
        this.controlDeps = Collections.singleton(ctlDep);
        this.parent = parent;
    }

    @Override
    public boolean isBottom() {
        return false;
    }

    @Override
    public boolean leq(CDAbsVal o) {
        return o.controlDeps().containsAll(controlDeps);
    }

    @Override
    public CDAbsVal upperBound(CDAbsVal o) {
        if (this == o || o == null)
            return this;
        Set<EdgeIdentifier> controlDeps;
        if (!this.controlDeps.equals(o.controlDeps)) {
            controlDeps = new LinkedHashSet<EdgeIdentifier>(this.controlDeps);
            controlDeps.addAll(o.controlDeps);
        } else
            controlDeps = this.controlDeps;
        CDAbsVal parent = this.parent;
        if (this == BOTTOM)
            parent = o.parent;
        else if (!parent.equals(o.parent)) {
            // one parent must be control dependent other
            CDAbsVal next = o.parent;
            // is parent an ancestor of o.parent?
            while(!next.equals(parent)) {
                if (next != next.parent)
                    next = next.parent;
                else {
                    // no.
                    parent = o.parent;
                    break;
                }
            }                
        }
        return new CDAbsVal(parent, controlDeps);
    }

    @Override
    public CDAbsVal widen(CDAbsVal o) {
        return upperBound(o);
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof CDAbsVal) {
            CDAbsVal that = (CDAbsVal) o;
            return that.controlDeps.equals(controlDeps);
        } else
            return false;
    }

    @Override
    public int hashCode() {
        return controlDeps.hashCode();
    }

    public static CDAbsVal upperBound(List<CDAbsVal> vals) {

        CDAbsVal join = BOTTOM;
        for (CDAbsVal n : vals) {
            join = join.upperBound(n);
        }
        return join;
    }

    public Set<EdgeIdentifier> controlDeps() {
        return controlDeps;
    }

    public CDAbsVal popControlDeps() {        
        return parent;
    }

}
//public class CDAbsVal extends Item implements AbstractValue<CDAbsVal> {
//    public static final CDAbsVal STARTPROC = new CDAbsVal(null,
//            Collections.<EdgeIdentifier> emptySet());
//
//    public static final CDAbsVal BOTTOM = new CDAbsVal(null,
//            Collections.<EdgeIdentifier> emptySet());
//
//    protected final Set<EdgeIdentifier> controlDeps;
//    protected CDAbsVal parent;
//    
//    public void setParent(CDAbsVal parent) {
//        this.parent = parent;
//    }
//    public CDAbsVal(CDAbsVal parent, Set<EdgeIdentifier> controlDeps) {
//        this.controlDeps = Collections.unmodifiableSet(controlDeps);
//        this.parent = parent;
//    }
//
//    public CDAbsVal(CDAbsVal parent, EdgeIdentifier ctlDep) {
//        this.controlDeps = Collections.singleton(ctlDep);
//        this.parent = parent;
//    }
//
//    @Override
//    public boolean isBottom() {
//        return false;
//    }
//
//    @Override
//    public boolean leq(CDAbsVal o) {
//        return o.controlDeps().containsAll(controlDeps);
//    }
//
//    @Override
//    public CDAbsVal upperBound(CDAbsVal o) {
//        if (this == o || o == null)
//            return this;
//        if (this != BOTTOM && o != BOTTOM && !parent.equals(o.parent)) {
//            throw new InternalCompilerError("Expected identical parents.");
//        }
//        Set<EdgeIdentifier> controlDeps;
//        if (!this.controlDeps.equals(o.controlDeps)) {
//            controlDeps = new LinkedHashSet<EdgeIdentifier>(this.controlDeps);
//            controlDeps.addAll(o.controlDeps);
//        } else
//            controlDeps = this.controlDeps;
//        CDAbsVal parent = this.parent;
//        if (this == BOTTOM)
//            parent = o.parent;
//        return new CDAbsVal(parent, controlDeps);
//    }
//
//    @Override
//    public CDAbsVal widen(CDAbsVal o) {
//        return upperBound(o);
//    }
//
//    @Override
//    public boolean equals(Object o) {
//        if (o instanceof CDAbsVal) {
//            CDAbsVal that = (CDAbsVal) o;
//            return that.controlDeps.equals(controlDeps);
//        } else
//            return false;
//    }
//
//    @Override
//    public int hashCode() {
//        return controlDeps.hashCode();
//    }
//
//    public static CDAbsVal upperBound(List<CDAbsVal> vals) {
//        CDAbsVal join = BOTTOM;
//        for (CDAbsVal n : vals) {
//            join = join.upperBound(n);
//        }
//        return join;
//    }
//
//    public Set<EdgeIdentifier> controlDeps() {
//        return controlDeps;
//    }
//
//    public CDAbsVal popControlDeps() {
//        return parent;
//    }
//
//}
