using System;
using compile;
using type;
using Type = type.Type;

using edu.neu.ccs.demeterf;
using edu.neu.ccs.demeterf.demfgen.lib;

// TypeChecking
public class TypeCheck : ID{
    public class TE:Exception{ public TE(String s):base(s){}}
    static Type intT = new Int();
    static Type boolT = new Bool();
    
    Type combine(S s){ return intT; }

    Op combine(Op o){ return o; }
    Type combine(C c, ArithOp op, Int l, Int r){ return intT; }
    Type combine(C c, CompOp op, Int l, Int r){ return boolT; }
    Type combine(C c, BoolOp op, Bool l, Bool r){ return boolT; }
    Type combine(C c, Op op, Type l, Type r){
        throw new TE("Op ("+op.Print()+") doesn\'t match Args: "+
                     l.Print()+" and "+r.Print());
    }

    Type combine(I ife, Bool c, Int t, Int e){ return t; }
    Type combine(I ife, Bool c, Bool t, Bool e){ return t; }
    Type combine(I ife, Bool c, Type t, Type e){
        throw new TE("If Branches Don\'t Match: "+t.Print()+" != "+e.Print());
    }
     
    Type combine(L l, ident id, Type e, E body, List<Pair> te){
        return check(body, te.push(new Pair(id,e)));
    }
    
    class find : List<Pair>.Pred{
        ident id;
        public find(ident i){ id = i; }
        public override bool huh(Pair p){ return p.id.Equals(id); }
    }
    Type combine(V v, ident id, List<Pair> te){ return te.find(new find(id)).t; }

    // Cache the Traversal... and a few static methods for recursion
    // Ok... the Naming/Lookup of classes is a little difficult.
    //   It goes: "full.name.space.Class,Assembly.Field"
    static Traversal trav =
        new Traversal(new TypeCheck(),Control.bypass("L.body"));
    public static Type check(E e){ return check(e, List<Pair>.create()); }
    static Type check(E e, List<Pair> te){ return trav.traverse<Type>(e, te); }
}