package edu.neu.ccs.demeterf.demfgen;

import edu.neu.ccs.demeterf.Fields;
import edu.neu.ccs.demeterf.demfgen.ClassGen.ArityPair;
import edu.neu.ccs.demeterf.demfgen.ClassGen.ToList;
import edu.neu.ccs.demeterf.demfgen.ClassGen.ToTDList;
import edu.neu.ccs.demeterf.demfgen.classes.*;
import edu.neu.ccs.demeterf.demfgen.traversals.Travs;
import edu.neu.ccs.demeterf.lib.List;
import edu.neu.ccs.demeterf.lib.ident;
import edu.neu.ccs.demeterf.util.Util;

/** Does a quick type check over the TypeDefs to be sure type parameters are used with
 *    the correct arity, and uses/definitions exist */
public class TypeCheck extends StaticTP{
    public static class Env{
        String curr;
        List<ArityPair> env;
        Env(List<ArityPair> e){ this("ROOT",e); }
        Env(String c, List<ArityPair> e){ curr = c; env = e; }
        Env push(List<ArityPair> l){ return new Env(curr,env.push(l)); }
        Env enter(TypeDef d){ return new Env(d.name()+d.typeParams(), env); }
        boolean containsAny(List<ArityPair> ps){ return env.containsAny(ps); }
        boolean contains(ArityPair p){ return env.contains(p); }
    }
    static ToList TOLIST = new ToList();
    static List<ArityPair> paramsToList(TypeDefParams p){
        return Travs.TheFactory.makeParamArityTrav(TOLIST).traverse(p);
    }
    static ToTDList TOTDLIST = new ToTDList();
    static List<ArityPair> defsToList(TypeDefList tl){
        return Travs.TheFactory.makeTypeArityTrav(TOTDLIST).traverse(tl);
    }
    public Env update(TypeDef t, Fields.any f, Env env){
        List<ArityPair> pars = paramsToList(t.typeParams());
        if(env.containsAny(pars) && Util.warningOn)
            //throw new TE("Type param shadows another type in def. "+t.name());
            ClassGen.p(" ** WARNING: Type param shadows another type in def: "+t.name()+"\n");
        return env.push(pars).enter(t);
    }
    public TypeUse combine(TypeUse tus, ident n, TypeUseParams p, Env env){
        ArityPair search = new ArityPair(""+n,0);
        if(!env.contains(search))
            throw new TE("Unbound Type: "+n+"\n   * In definition of: "+env.curr);

        ArityPair found = env.env.find(search);
        if(found.arity != p.length())
            throw new TE("Wrong Number of Type Params for: "+n+"\n  "+
                    "   * Expected "+found.arity+" found "+p.length()+"\n");
        return tus;
    }
    public TypeDef combine(TypeDef t){ return t; }
    
}