package accrue.analysis.preciseExceptions;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import polyglot.types.ProcedureInstance;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.util.InternalCompilerError;

public abstract class PreciseExLibrarySignature {

    public static final PreciseExLibrarySignature NORM_TERMINATION_ONLY =
            new ConstantSignature(Collections.EMPTY_SET, true, true);
    public static final PreciseExLibrarySignature NO_TERMINATION =
            new ConstantSignature(Collections.EMPTY_SET, true, false);

    public static PreciseExLibrarySignature ExactExceptions(String ex1) {
        Set<String> throwableTypeNames = Collections.singleton(ex1);
        return memoize(throwableTypeNames, true);
    }

    public static PreciseExLibrarySignature NonexactExceptions(String ex1) {
        Set<String> throwableTypeNames = Collections.singleton(ex1);
        return memoize(throwableTypeNames, false);
    }

    private static Map<Set<String>, ConstantSignature> exactSigs =
            new HashMap<Set<String>, ConstantSignature>();
    private static Map<Set<String>, ConstantSignature> nonexactSigs =
            new HashMap<Set<String>, ConstantSignature>();

    private static ConstantSignature memoize(Set<String> throwableTypeNames,
            boolean isExactTypes) {
        if (isExactTypes) {
            ConstantSignature sig = exactSigs.get(throwableTypeNames);
            if (sig == null) {
                sig = new ConstantSignature(throwableTypeNames, true, true);
                exactSigs.put(throwableTypeNames, sig);
            }
            return sig;
        }
        else {
            ConstantSignature sig = nonexactSigs.get(throwableTypeNames);
            if (sig == null) {
                sig = new ConstantSignature(throwableTypeNames, false, true);
                nonexactSigs.put(throwableTypeNames, sig);
            }
            return sig;
        }
    }

    public abstract PreciseExAbsVal process(ProcedureInstance pi);

}

class ConstantSignature extends PreciseExLibrarySignature {
    private final Set<String> throwableTypeNames;
    private final boolean isExactTypes;
    private final boolean canTerminateNormally;
    private PreciseExAbsVal dfi = null;

    ConstantSignature(Set<String> throwableTypeNames, boolean isExactTypes,
            boolean canTerminateNormally) {
        this.throwableTypeNames = throwableTypeNames;
        this.isExactTypes = isExactTypes;
        this.canTerminateNormally = canTerminateNormally;
    }

    @Override
    public PreciseExAbsVal process(ProcedureInstance pi) {
        if (this.dfi == null) {
            TypeSystem ts = pi.typeSystem();
            Set<ThrowableType> t = new LinkedHashSet();
            for (String s : this.throwableTypeNames) {
                Type type;
                try {
                    type = ts.typeForName(s);
                }
                catch (SemanticException e) {
                    try {
                        type = ts.typeForName("java.lang." + s);
                    }
                    catch (SemanticException e1) {
                        throw new InternalCompilerError(e1);
                    }
                }
                t.add(new ThrowableType(type, this.isExactTypes));
            }
            this.dfi = new PreciseExAbsVal(t, canTerminateNormally);
        }
        return this.dfi;
    }
}
