package edu.neu.ccs.demeterf.demfgen;

import edu.neu.ccs.demeterf.*;
import edu.neu.ccs.demeterf.demfgen.classes.*;
import edu.neu.ccs.demeterf.lib.List;

import java.io.*;

/** Resolve the Included CD/BEH files recursively. Cycles are not broken, and
 *    repeate includes result in multiple definitions. */
public class IncludeCDs{
    public static List<CDFile> resolveCDs(IncludeList l, String par){
        return resolveCDs(l, par, new YesGen());
    }
    public static List<CDFile> resolveCDs(IncludeList l, String par, DoGen g){
        return Factory.newTraversal(new ReadCDs(par,g)).traverseIncludeList(l);
    }
    public static List<BehDef> resolveBEHs(IncludeList l, String par){
        return resolveBEHs(l, par, new YesGen());
    }
    public static List<BehDef> resolveBEHs(IncludeList l, String par, DoGen g){
        return Factory.newTraversal(new ReadBEHs(par,g)).traverseIncludeList(l);
    }
    /** Resolve relative include paths based on the containing CD file (name) */
    public static String localFile(String name, String inc){
        if(inc.charAt(0) == File.separatorChar)return inc;
        return name.substring(0, name.lastIndexOf(File.separatorChar)+1)+inc;
    }
    /** Collect all the types in a CD */
    public static List<TypeDef> allTypes(List<CDFile> l){
        return l.fold(new List.Fold<CDFile, List<TypeDef>>(){
            public List<TypeDef> fold(CDFile f, List<TypeDef> r){
                return r.append(f.getTypes().toList());
            }},List.<TypeDef>create());
    }
    /** Calculate the maximum look ahead from all transitive included CDs */
    public static LookDef maxLookAhead(List<CDFile> l){
        return l.fold(new List.Fold<CDFile, LookDef>(){
            public LookDef fold(CDFile f, LookDef r){
                if(!f.getLook().isLook())return r;
                if(!r.isLook())return f.getLook();
                return (f.getLook().look() > r.look())?f.getLook():r;
            }}, new NoLook());
    }
    /** Collect all the imports from all transitive included CDs */
    public static ImportList allImports(List<CDFile> l){
        return l.fold(new List.Fold<CDFile, ImportList>(){
            public ImportList fold(CDFile f, ImportList r){
                return f.getImports().append(r);
            }}, new ImportEmpty());
    }

    public static abstract class Read<X> extends ID{
        String parFile;
        DoGen gen;
        protected Read(String p, DoGen g){ parFile = p; gen = g; }
        protected abstract List<X> read(InputStream in, DoGen g, String par) throws Exception;
        protected abstract String type();
        protected DoGen combine(DoGen g){ return g; }
        protected List<X> combine(Include inc, DoGen g, String file){
            String loc = IncludeCDs.localFile(parFile,file);
            try{
                FileInputStream in = new FileInputStream(loc);
                return read(in,g.merge(gen), loc);
            }catch(FileNotFoundException fe){
                throw new RTFileNotFound(" ** In \""+parFile+"\"\n   Included "+type()+" File Not Found : \""+loc+"\"");
            }catch(Exception e){
                throw new RTParseException(" ** "+type()+" File: \""+loc+"\"\n"+e.getMessage());
            }
        }
        protected List<X> combine(ConsList c, List<X> f, List<X> r){ return r.push(f); }
        protected List<X> combine(EmptyList e){ return List.<X>create(); }
    }
    static class ReadCDs extends Read<CDFile>{
        ReadCDs(String p, DoGen g){ super(p,g); }
        protected String type(){ return "CD"; }
        protected List<CDFile> read(InputStream in, DoGen g, String file) throws Exception{
            CDFile m = mergeGen(CDFile.parse(in),g);
            List<CDFile> incl = IncludeCDs.resolveCDs(m.getIncl(),file,g);
            ImportList imp = IncludeCDs.allImports(incl);
            return incl.push(m.updateImports(imp.append(m.getImports())));
        }
        static CDFile mergeGen(CDFile m, final DoGen g){
            if(g.doGen() && g.doParse())return m;
            return Factory.newTraversal(new TP(){
                DoGen combine(DoGen gen){ return gen.merge(g); }
            }).<List<CDFile>>traverseList_CDFile_(List.create(m)).top();
        }
    }
    static class ReadBEHs extends Read<BehDef>{
        ReadBEHs(String p, DoGen g){ super(p,g); }
        protected String type(){ return "BEH"; }
        protected List<BehDef> read(InputStream in, DoGen g, String file) throws Exception{
            BehFile b = BehFile.parse(in);
            return IncludeCDs.resolveBEHs(b.getIncl(),file,g).push(b.getBehs().toList());
        }
    }

}