package accrue.analysis.preciseExceptions;

import accrue.analysis.interprocanalysis.Ordered;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class PreciseExAbsVal implements Ordered<PreciseExAbsVal> {
    private final Set<ThrowableType> throwTypes;
    private final boolean canTerminateNormally;
    public static final PreciseExAbsVal EMPTY =
            new PreciseExAbsVal(Collections.EMPTY_SET, false);

    public PreciseExAbsVal(Set<ThrowableType> terminationKinds,
            boolean canTerminateNormally) {
        this.throwTypes = terminationKinds;
        this.canTerminateNormally = canTerminateNormally;
    }

    @Override
    public boolean leq(PreciseExAbsVal o) {
        return this.equals(o);
        //throw new InternalCompilerError("FooBar");
    }

    @Override
    public PreciseExAbsVal upperBound(PreciseExAbsVal o) {
        if (o == null) return this;
        Set<ThrowableType> tt = new HashSet<ThrowableType>(this.throwTypes());
        tt.addAll(o.throwTypes());
        return new PreciseExAbsVal(tt, this.canTerminateNormally()
                || o.canTerminateNormally());
    }

    @Override
    public String toString() {
        return throwTypes.toString();
    }

    public Set<ThrowableType> throwTypes() {
        return throwTypes;
    }

    public boolean canTerminateNormally() {
        return canTerminateNormally;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (canTerminateNormally ? 1231 : 1237);
        result =
                prime * result
                        + ((throwTypes == null) ? 0 : throwTypes.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!(obj instanceof PreciseExAbsVal)) return false;
        PreciseExAbsVal other = (PreciseExAbsVal) obj;
        if (canTerminateNormally != other.canTerminateNormally) return false;
        if (throwTypes == null) {
            if (other.throwTypes != null) return false;
        }
        else if (!throwTypes.equals(other.throwTypes)) return false;
        return true;
    }

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

}
