package de.dhbwstuttgart.strucTypes4.parserFWJ;

import de.dhbwstuttgart.strucTypes4.algo.*;
import control.Help;
import de.dhbwstuttgart.strucTypes4.grammar.FWJLexer;
import de.dhbwstuttgart.strucTypes4.grammar.FWJParser;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTreeWalker;


import de.dhbwstuttgart.strucTypes4.syntaxtree.*;
import de.dhbwstuttgart.strucTypes4.syntaxtree.Class;
import de.dhbwstuttgart.strucTypes4.typeVars.TypeVarAbstract;

import java.util.*;

/**
 * Created by sebastian on 07.04.17.
 */

public class Parser {

    public SourceFile parse(String quellcode) {

        ANTLRInputStream inputStream = new ANTLRInputStream(quellcode);
        FWJLexer lexer = new FWJLexer(inputStream);


        CommonTokenStream tokens = new CommonTokenStream(lexer);
        FWJParser parser = new FWJParser(tokens);
        ParserRuleContext tree = parser.sourceFile();
        ParseTreeWalker walker = new ParseTreeWalker();

        SourceFileWalker sourceFileWalker = new SourceFileWalker();

        walker.walk(sourceFileWalker, tree);

        return sourceFileWalker.getSourceFile();
    }



    // Quellcode wird geparst. Evtl. bekannte Klassen müssen über die Assumptions mitgegeben werden
    public Class parseCode(String quellcode , Map<String,TypeVarAbstract> ass  ) {
        SourceFile sf = parse(quellcode);
        TI typeinference = new TI(ass,sf.getClassList().get(0));
        return typeinference.getResultClassTypedWithInterfaces();
    }
}



// - ----------- Ab hier der Type AlgoRythmus


class TI {

    Class resultClassTypedWithInterfaces;

    public TI (Map<String,TypeVarAbstract> ass,  Class aClass) {

        Type type = new Type(ass,aClass);

        // Ergebnis:
        System.out.println("--------------- Ergebnis nach TPH: -------------------------" );
        System.out.println(aClass.toCodeTPH());
        System.out.println(Help.toStringN(type.resultConstraintSet));
        System.out.println("---------------------------------------------------------------");


        // Ergebnis:
        System.out.println("--------------- Ergebnis nach Typen: -------------------------" );
        System.out.println(aClass.toCodeTyped(aClass.typeVarStore));
        System.out.println(Help.toStringN(type.resultConstraintSet));
        System.out.println(aClass.typeVarStore.toString());
        System.out.println("---------------------------------------------------------------");


        // Ergebnis nach Construct = -> Eine Klasse mit Interfaces und Constraints
        // construct (cl t + interfaces )  +
        System.out.println("--------------- Ergebnis nach Construct: -------------------------" );
        Construct2 construct2 = new Construct2();
        Class aClass2 = construct2.construct(aClass,type.resultConstraintSet);
        System.out.println(Help.toStringN(construct2.neueConstraits));
        System.out.println(Help.toStringN(aClass2.getInterfaces()));
        System.out.println(aClass2.toCodeTyped(aClass2.typeVarStore));
        System.out.println(aClass2.getTypeVarStore().toString());
        System.out.println("--------------------------------------------------------------" );


       // Hier würde noch Solve stehen.

        // Ergebnis
        resultClassTypedWithInterfaces = aClass2;


    }




    public Class getResultClassTypedWithInterfaces() {
        return resultClassTypedWithInterfaces;
    }
}




class Type {

    Class resultClassTyped;
    List<ConstraintAbstract> resultConstraintSet = new ArrayList<>();

    public Type(Map<String,TypeVarAbstract> ass, Class aClass)  {

        // 1. Erstellen der Assumptions für die Klasse
        Map<String, TypeVarAbstract> fass_mass =  aClass.makeAssumptions();

        // ToDo Vereinigen der Assumptions
        fass_mass.putAll(ass);



        // 2. Erstellen der classTyped
        // Eigentlich die Kombination aus Assumptions, Class TPH und ConstriantSet
        resultClassTyped = aClass;


        // 3. Erstellen des Constraint Sets
        for (Method method : aClass.getMethoden()) {

            // AssAll     Ass und fass und mass und this:A
            // ToDo this:a
            Map<String,TypeVarAbstract> assMethod = method.makeAssumptions(aClass.typeVarStore);

            assMethod.putAll(fass_mass);
            resultConstraintSet.addAll(method.typeExpr(assMethod , aClass.typeVarStore));

        }
    }
}



