package edu.neu.ccs.demeterf.demfgen;

import edu.neu.ccs.demeterf.demfgen.classes.*;
import edu.neu.ccs.demeterf.*;
import edu.neu.ccs.demeterf.demfgen.ClassHier.InhrtPair;
import edu.neu.ccs.demeterf.lib.Cons;
import edu.neu.ccs.demeterf.lib.Empty;
import edu.neu.ccs.demeterf.lib.List;
import edu.neu.ccs.demeterf.lib.ident;
import edu.neu.ccs.demeterf.util.Util;
import edu.neu.ccs.demeterf.demfgen.traversals.Travs;
import edu.neu.ccs.demeterf.demfgen.TypeCollect.UseCollect;

/** Generate a JavaCC Parser for the given CDs (included) */
public class ParseGen{
    static void p(String s){{ System.err.print(s); }}
    public static void genParser(List<CDFile> CD, String dir, List<InhrtPair> inhrt, PackageDef basepkg){
        LookDef look = IncludeCDs.maxLookAhead(CD);
        String prser = 
            "options{ STATIC = false; "+(look.isLook()?"LOOKAHEAD = "+look.look()+";":"")+" }\n"+
            "PARSER_BEGIN(TheParser)\n"+
            basepkg.parserPackage()+
            IncludeCDs.allImports(CD)+Diff.d.parserImport+"\n"+
            "  public class TheParser{\n"+Diff.d.parserBody+"  }\n"+
            "PARSER_END(TheParser)\n\n"+Diff.d.parserPreamble+"\n";

        UseCollect uses = new UseCollect();
        CombStr func = new CombStr(uses,inhrt);

        List<TypeDef> allTypes = IncludeCDs.allTypes(CD);

        prser += Travs.TheFactory.makeParseGenTrav(func).traverse(allTypes, new YesGen());
        
        UseCollect done = new UseCollect();
        while(!uses.isEmpty()){
            TypeUse use = uses.top();
            uses.pop();
            if(!done.has(UseCollect.comp(use))){
                done.add(use);
                prser += toJJProd(DemFGenMain.instantiate(use, allTypes),uses,true,inhrt);
            }
        }
        prser += Preamble.lexer;
        Util.writeFile("theparser", ".jj", Preamble.header+prser, DemFGenMain.pkgdir(dir,basepkg,true), "");
    }
    static String toJJProd(TypeDef td, TypeCollect<TypeUse> tuc, boolean gen, List<InhrtPair> inhrt){
        if(gen || td.typeParams().isEmpty()){
            JJProd func = new JJProd(tuc,inhrt);
            return Travs.TheFactory.makeJJGenTrav(func).traverse(td, new YesGen());
        }
        return "";
    }
    static String strip(String s){
        int f = s.indexOf('<'),
        e = s.lastIndexOf('>');
        return s.substring(f+1, e);
    }

    //  Parsing un-escapes strings... so we need to re-escape them for 
    //   the parser generation (javacc)
    public static String escape(char c){
        switch(c){
        case '\n':return "\\n";  case '\t':return "\\t";
        case '\b':return "\\b";  case '\r':return "\\r";
        case '\f':return "\\f";  case '\\':return "\\\\";
        case '\'':return "\\\'"; case '\"':return "\\\"";
        default: return ""+c;
        }
    }
    public static String escape(String s){
        char str[] = s.toCharArray();
        StringBuffer ret = new StringBuffer("");
        for(char c:str)ret.append(escape(c));
        return ret.toString();
    }
    // Left for reference
    static char unescapeChar(String s) { 
        char c = s.charAt(0); 
        if (c == '\\'){ 
            switch (s.charAt(1)) { 
            case 'n': return '\n';  case 't': return '\t'; 
            case 'b': return '\b';  case 'r': return '\r'; 
            case 'f': return '\f';  case '\\': return '\\'; 
            case '\'': return '\''; case '\"': return '\"'; 
            default: return (char)Integer.parseInt(s.substring(1, s.length()), 8);
            }
        }
        return c; 
    }

    /** This one gets us to the TypeDefs, and combines the resulting parse descriptions into
     *    a single file.  Have to be sure to pass the generic use collection to be sure we get
     *    all the type expansions for parse methods.
     *    
     *  Also, we can inline this one separately if needed. */
    public static class CombStr extends FC{
        TypeCollect<TypeUse> genUses;
        List<InhrtPair> inhrt;

        CombStr(TypeCollect<TypeUse> gu, List<InhrtPair> i){ genUses = gu; inhrt = i; }
        public String combine(TypeDef td){ return ParseGen.toJJProd(td, genUses, false, inhrt); }
        public String combine(List<?> e){ return ""; }
        public String combine(EmptyList e){ return ""; }
        public String combine(Cons<?> c, String f, String r){ return f+"\n"+r; }
        public String combine(ConsList c, String f, String r){ return f+"\n"+r; }
    }

    public static class JJProd extends SumGen{
        TypeCollect<TypeUse> genUses;
        List<InhrtPair> inhrt;
        JJProd(TypeCollect<TypeUse> gu, List<InhrtPair> i){ super(i); genUses = gu; }

        void addGenUse(TypeUse tu){ genUses.add(tu); }

        public String combine(TypeUse tu, ident n, String p, DoGen gen){
            if(gen.doParse() && !tu.getTparams().isEmpty())
                addGenUse(tu);
            return ""+n+p;
        }

        public String combine(Impl imp){ return ""; }

        public String combine(EmptyToken e){ return ""; }
        public String combine(RealToken e, String s){ return "\""+ParseGen.escape(s)+"\""; }

        public String combine(Bound b){ return ""; }
        public String combine(NameDef d, ident id, String b){ return ""+id; }
        public String combine(NameEmpty e){ return ">"; }
        public String combine(NameCons c, String n, String r){ return ","+n+r; }
        public String combine(NENameList tl, String n, String e){ return "<"+n+e; }
        public String combine(DefParams p, String s){ return s; }
        public String combine(EmptyDefParams e){ return ""; }   
    }

    static class SumGen extends ProductGen{
        SumGen(List<InhrtPair> i){ super(i); }
        public String combine(TypeUseEmpty e){ return ">"; }
        public String combine(TypeUseCons c, ident n, String r){ return combine(c,""+n,r); }
        public String combine(TypeUseCons c, String n, String r){ return ","+n+r; }
        public String combine(NETypeUseList tl, String n, String e){ return "<"+n+e; }
        public String combine(UseParams p, String s){ return s; }
        public String combine(EmptyUseParams e){ return ""; }

        public List<String> combine(SubtypeEmpty e){ return List.create(); }

        // Make the TypeChecker Happy...
        public List<String> combine(NESubtypeList c, String tu, List<String> r){ return comb(c,tu,r); }
        public List<String> combine(SubtypeCons c, String tu, List<String> r){ return comb(c,tu,r); }
        public List<String> comb(ConsList c, String tu, List<String> r){
            return r.push("sup = "+methName(tu,"")+"()");
        }

        public DoGen update(ClassDef td, Fields.any f){ return td.getGen(); }
        public String combine(TypeDef td, DoGen g, ident n, String par, List<String> stl){
            if(g.doParse())
                return subtypeParse(n,par,stl);
            return "";
        }
    }

    static class ProductGen extends FC{
        List<InhrtPair> inhrt;
        ProductGen(List<InhrtPair> i){ inhrt = i; }

        public static String typeMethod(String s){
            return s.replaceAll("[,<)>(]","\\"+Diff.d.genericSep);
        }
        public StrLTrip.StrTrip combine(Field f, ident n, String t){
            return new StrLTrip.StrTrip(t+" "+n, n+" = "+methName(t,"")+"()",""+n);
        }
        public String combine(AddToken at, String tok){ return "\""+ParseGen.escape(tok)+"\""; }
        public String combine(Syntax other){ return ""; }
        public String combine(TheEOF eof){ return "<EOF>"; }

        public StrLTrip combine(FieldList l, StrLTrip.StrTrip tr, StrLTrip tl)
        { return tl.add(tr.f, tr.a, tr.t); }
        public StrLTrip combine(FieldList l, String tok, StrLTrip tl)
        { return tok.length()==0?tl:tl.add(tok); }

        public StrLTrip combine(List<FieldOrSyntax> l, StrLTrip.StrTrip tr, StrLTrip tl)
        { return tl.add(tr.f, tr.a, tr.t); }
        public StrLTrip combine(List<FieldOrSyntax> l, String tok, StrLTrip tl)
        { return tok.length()==0?tl:tl.add(tok); }
        public StrLTrip combine(Empty<FieldOrSyntax> e){ return new StrLTrip(); }

        public StrLTrip combine(FieldEmpty e){ return new StrLTrip(); }
        public EmptyList combine(EmptyList e){ return e; }
        public DoGen combine(DoGen g){ return g; }

        String methName(String n, String par){
            return "parse_"+typeMethod(n+par);
        }
        String methSig(ident n, String par){
            return "public "+n+ClassGen.unlocal(par)+" "+methName(n.toString(), par)+"():{\n";
        }
        String subtypeParse(ident n, String par, List<String> stl){
            return (methSig(n,par)+
                    "    "+n+par+" sup = null;"+"\n}{\n"+
                    "( "+stl.toString(" { return sup; } | \n  ","  ")+
            " { return sup; } )\n}\n");
        }

        public String combine(ClassDef td, DoGen g, ident n, String par, List<String> stl, StrLTrip fs){
            if(g.doParse()){
                StrLTrip sup = Factory.newTraversalCtx(this)
                .traverseList_FieldOrSyntax_(ClassHier.superFieldsAndSyntax(td.getTparams().toList(), inhrt, ""+n),g);
                StrLTrip all = fs.append(sup);

                if(stl.isEmpty())
                    return (methSig(n,par)+
                            all.fieldDefs("")+"}{\n"+
                            all.assigns.toString("\n","    ")+"\n"+
                            "    { return new "+n+par+"("+all.fNames.toString(",","")+
                    "); }\n}\n");
                return subtypeParse(n,par,stl);
            }
            return "";
        }
    }
}