package de.dhbwstuttgart.strucTypes4.parserFWJ;

import de.dhbwstuttgart.strucTypes4.grammar.FWJBaseListener;
import de.dhbwstuttgart.strucTypes4.grammar.FWJParser;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import de.dhbwstuttgart.strucTypes4.syntaxtree.*;
import de.dhbwstuttgart.strucTypes4.syntaxtree.Class;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

public class SourceFileWalker extends FWJBaseListener {


    SourceFile sourceFile = null;
    private List<Field> felder = new ArrayList<>();
    private List<Method> methods = new ArrayList<>();
    private List<Class> klassen = new ArrayList<>();

    private List<Argument> methodenArgs = new ArrayList<>();



    // Rein Informell
    @Override
    public void enterClassX(FWJParser.ClassXContext ctx) {
        super.enterClassX(ctx);
        //System.out.println(ctx.getText());

    }

    // Feldparser
    @Override
    public void exitFeld(FWJParser.FeldContext ctx) {
        super.exitFeld(ctx);
        //ToDo momentan nur typ und var
        felder.add(new Field(ctx.bezeichner(0).getText() , ctx.bezeichner(1).getText()));
    }

    // MethodenParser
    @Override
    public void exitMethodDecl(FWJParser.MethodDeclContext ctx) {
        super.exitMethodDecl(ctx);
        // Methode Implementieren


        List<String> argList = new ArrayList<>();

        ParseTreeWalker ptl = new ParseTreeWalker();

        //for (ctx.methodDeclArgs().getToken())
        Method method = new Method(ctx.bezeichner().getText() ,"returnType:", methodenArgs , ExpressAdapter.adapt(ctx.returnExpr().expr()));
        methods.add(method);
        methodenArgs = new ArrayList<>();
    }

    // Parser für Argumente der Methode
    @Override
    public void exitMethodDeclArgs(FWJParser.MethodDeclArgsContext ctx) {
        super.exitMethodDeclArgs(ctx);

        ParseTreeWalker walker = new ParseTreeWalker();
        walker.walk(new FWJBaseListener(){
            @Override
            public void exitBezeichner(FWJParser.BezeichnerContext ctx) {
                super.exitBezeichner(ctx);
                methodenArgs.add(new Argument(ctx.getText()));
            }
        } , ctx);
    }



    // Parser der Return exprression


    /*
    @Override
    public void exitReturnExpr(FWJParser.ReturnExprContext ctx) {
        super.exitReturnExpr(ctx);
        ExpressionAbstract exp = ExpressAdapter.adapt(ctx.expr());
    }
    */

    @Override
    public void exitClassX(FWJParser.ClassXContext ctx) {
        super.exitClassX(ctx);
        // Klasse in SourceFile schreiben
        // Variablen löschen
        Class cl = new Class(ctx.bezeichner().getText(),felder,methods);
        felder = new ArrayList<>();
        methods = new ArrayList<>();
        klassen.add(cl);
    }


    @Override
    public void exitSourceFile(FWJParser.SourceFileContext ctx) {
        super.exitSourceFile(ctx);
        // Parsevorgang beendet
        sourceFile = new SourceFile(klassen);

    }


    public SourceFile getSourceFile() {
        return sourceFile;
    }
}



// Diese Idee hat nich funktioniert
/*
class ExprWalker2 extends FWJBaseListener {


    List<ExpressionAbstract> rtExpressionStore;

    List<ExpressionAbstract> rtExpression = new ArrayList<>();
    List<List<ExpressionAbstract>> mtCallExpressions = new ArrayList<>();


    @Override
    public void exitExprVar(FWJParser.ExprVarContext ctx) {
        super.exitExprVar(ctx);
        rtExpression.add(new ExprVar(ctx.bezeichner().getText()));
        System.out.println(rtExpression.toString());
    }


    @Override
    public void exitExprField(FWJParser.ExprFieldContext ctx) {
        super.exitExprField(ctx);
        rtExpression.add(new ExprField(ctx.bezeichner().getText()));
    }


    @Override
    public void enterExprMCallArgs(FWJParser.ExprMCallArgsContext ctx) {
        super.enterExprMCallArgs(ctx);
        rtExpressionStore = rtExpression;
        rtExpression = new ArrayList<>();
    }

    @Override
    public void exitExprMCallArgs(FWJParser.ExprMCallArgsContext ctx) {
        super.exitExprMCallArgs(ctx);
        List<ExpressionAbstract> l = rtExpression;
        mtCallExpressions.add(l);
        rtExpression = rtExpressionStore;
    }

    @Override
    public void exitExprMCall(FWJParser.ExprMCallContext ctx) {
        super.exitExprMCall(ctx);

        List<ExpressionAbstract> ex = new ArrayList<>();
        //wenn keine parameter
        if (mtCallExpressions.size() != 0) {
            ex = mtCallExpressions.get(mtCallExpressions.size() - 1);
        }
        rtExpression.add(new ExprMCall(ctx.bezeichner().getText(), ex));
    }


    @Override
    public void exitExprNew(FWJParser.ExprNewContext ctx) {
        super.exitExprNew(ctx);
        System.err.println("Fehler");
    }
}
*/

/*   Beispiel, wird nicht benötigt :

class ExprWalker extends FWJBaseListener{

    List<ExpressionAbstract> rtExpression = new ArrayList<>();

    @Override
    public void exitExpr(FWJParser.ExprContext ctx) {
        super.exitExpr(ctx);
        System.out.println("aussere klasse "+  ctx.getText());
    }

    @Override
    public void enterExprMCallArgs(FWJParser.ExprMCallArgsContext ctx) {
        super.enterExprMCallArgs(ctx);

        ParseTreeWalker walter = new ParseTreeWalker();
        walter.walk(new FWJBaseListener() {

            @Override
            public void exitExpr(FWJParser.ExprContext ctx) {
                super.exitExpr(ctx);
                System.out.println("Innere Klasse: " + ctx.getText());
            }
        },ctx );
    }
}
*/











// Parst die Expression und erstellt eine Rekursive Datenstruktur
class ExpressAdapter {

    public static ExpressionAbstract adapt(FWJParser.ExprContext ctx) {

        if (ctx.expr() != null ) {
            if (ctx.expr().size() == 1) {
                return adapt(ctx.expr(0));
            }
            else if (ctx.expr().size() == 2) {
                ExpressionAbstract receiver = adapt(ctx.expr(0));
                ExpressionAbstract caller = adapt(ctx.expr(1));
                Expression ex = new Expression(receiver,caller);
                return ex;
            }
            else if (ctx.exprVar() != null) {
                return new ExprVar(ctx.exprVar().getText());
            }
            else if (ctx.exprField() != null ) {
                return new ExprField(ctx.exprField().bezeichner().getText());
            }
            else if (ctx.exprMCall() != null) {
                List<ExpressionAbstract> args = new ArrayList<>();
                if (ctx.exprMCall().exprMCallArgs() != null ) {
                    if (ctx.exprMCall().exprMCallArgs().expr() != null) {
                        for (FWJParser.ExprContext exp : ctx.exprMCall().exprMCallArgs().expr()) {
                            args.add(adapt(exp));
                        }
                    }
                }
                return new ExprMCall(ctx.exprMCall().bezeichner().getText()  , args);
            }
            else if (ctx.exprNew() != null) {

                // Argumente
                List<ExpressionAbstract> args = new ArrayList<>();
                if (ctx.exprNew().exprMCallArgs() != null) {
                    if (ctx.exprNew().exprMCallArgs().expr() != null) {
                        for (FWJParser.ExprContext exp : ctx.exprNew().exprMCallArgs().expr()) {
                            args.add((adapt(exp)));
                        }
                    }
                }

                //Generics
                // Zugriff erfolgt äquivalent zu den Argumenten
                Map<String,String> generics = new HashMap<>();
                if (ctx.exprNew().generics() != null) {
                    if (ctx.exprNew().generics().generic() != null) {
                        for (FWJParser.GenericContext genericContext : ctx.exprNew().generics().generic()) {
                            generics.put(genericContext.bezeichner(0).getText() , genericContext.bezeichner(1).getText());
                        }
                    }
                }
                return new ExprNew(ctx.exprNew().bezeichner().getText() , generics, args);
             }
        }
        return null;
    }
}
