package de.dhbwstuttgart.syntaxtree;

import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.*;

import java.util.Iterator;
import java.util.Objects;

public abstract class AbstractASTWalker implements ASTVisitor {
    @Override
    public void visit(Constructor cons) {
        visitMethod(cons);
    }

    @Override
    public void visit(SourceFile sourceFile) {
        for (ClassOrInterface cl : sourceFile.getClasses()) {
            cl.accept(this);
        }
    }

    @Override
    public void visit(ArgumentList argumentList) {
        for (Expression expr : argumentList.getArguments()) {
            expr.accept(this);
        }
    }

    @Override
    public void visit(GenericTypeVar genericTypeVar) {

    }

    @Override
    public void visit(FormalParameter formalParameter) {
        formalParameter.getType().accept((ASTVisitor) this);
    }

    @Override
    public void visit(LiteralPattern literalPattern) {

    }

    @Override
    public void visit(GenericDeclarationList genericTypeVars) {
        Iterator<GenericTypeVar> genericIterator = genericTypeVars.iterator();
        if (genericIterator.hasNext()) {
            while (genericIterator.hasNext()) {
                genericIterator.next().accept(this);
            }
        }
    }

    @Override
    public void visit(Field field) {
        field.getType().accept(this);
    }

    @Override
    public void visit(Method method) {
        visitMethod(method);
    }

    private void visitMethod(Method method) {
        method.getReturnType().accept(this);
        method.getParameterList().accept(this);
        if (method.block != null)
            method.block.accept(this);
    }

    @Override
    public void visit(ParameterList formalParameters) {
        Iterator<Pattern> it = formalParameters.getFormalparalist().iterator();
        if (it.hasNext()) {
            while (it.hasNext()) {
                it.next().accept(this);
            }
        }
    }

    @Override
    public void visit(ClassOrInterface classOrInterface) {
        classOrInterface.getGenerics().accept(this);
        for (Field f : classOrInterface.getFieldDecl()) {
            f.accept(this);
        }
        for (Constructor c : classOrInterface.getConstructors()) {
            c.accept(this);
        }
        for (Method m : classOrInterface.getMethods()) {
            m.accept(this);
        }
    }

    @Override
    public void visit(RefType refType) {
        Iterator<RefTypeOrTPHOrWildcardOrGeneric> genericIterator = refType.getParaList().iterator();
        if (genericIterator.hasNext()) {
            while (genericIterator.hasNext()) {
                genericIterator.next().accept(this);
            }
        }
    }

    @Override
    public void visit(SuperWildcardType superWildcardType) {
        superWildcardType.getInnerType().accept(this);
    }

    @Override
    public void visit(TypePlaceholder typePlaceholder) {
    }

    @Override
    public void visit(ExtendsWildcardType extendsWildcardType) {
        extendsWildcardType.getInnerType().accept(this);
    }

    @Override
    public void visit(GenericRefType genericRefType) {
    }

    @Override
    public void visit(LambdaExpression lambdaExpression) {
        lambdaExpression.params.accept(this);
        lambdaExpression.methodBody.accept(this);
    }

    @Override
    public void visit(Assign assign) {
        assign.lefSide.accept(this);
        assign.rightSide.accept(this);
        assign.rightSide.getType().accept((ASTVisitor) this);
    }

    @Override
    public void visit(BinaryExpr binary) {

    }

    @Override
    public void visit(BoolExpression logical) {

    }

    @Override
    public void visit(Block block) {
        for (Statement stmt : block.getStatements()) {
            stmt.accept(this);
        }
    }

    @Override
    public void visit(CastExpr castExpr) {

    }

    @Override
    public void visit(EmptyStmt emptyStmt) {

    }

    @Override
    public void visit(FieldVar fieldVar) {
        fieldVar.receiver.accept(this);
    }

    @Override
    public void visit(ForStmt forStmt) {
        forStmt.block.accept(this);
    }

    @Override
    public void visit(ForEachStmt forEachStmt) {
        forEachStmt.block.accept(this);
    }

    @Override
    public void visit(IfStmt ifStmt) {
        ifStmt.then_block.accept(this);
        if (!Objects.isNull(ifStmt.else_block))
            ifStmt.else_block.accept(this);
    }

    @Override
    public void visit(InstanceOf instanceOf) {

    }

    @Override
    public void visit(LocalVar localVar) {

    }

    @Override
    public void visit(LocalVarDecl localVarDecl) {
        localVarDecl.getType().accept(this);
    }

    @Override
    public void visit(MethodCall methodCall) {
        methodCall.receiver.accept(this);
        methodCall.getArgumentList().accept(this);
        methodCall.getArgumentList().getArguments().forEach(a -> a.getType().accept((ASTVisitor) this));
    }

    @Override
    public void visit(NewClass methodCall) {
        visit((MethodCall) methodCall);
    }

    @Override
    public void visit(NewArray newArray) {

    }

    @Override
    public void visit(ExpressionReceiver receiver) {
        receiver.expr.accept(this);
    }

    @Override
    public void visit(UnaryExpr unaryExpr) {
        unaryExpr.expr.accept(this);
    }

    @Override
    public void visit(Return aReturn) {
        aReturn.retexpr.accept(this);
        aReturn.getType().accept((ASTVisitor) this);
    }

    @Override
    public void visit(ReturnVoid aReturn) {

    }

    @Override
    public void visit(Break aBreak) {
        aBreak.accept(this);
    }

    @Override
    public void visit(Continue aContinue) {
        aContinue.accept(this);
    }

    @Override
    public void visit(StaticClassName staticClassName) {

    }

    @Override
    public void visit(Super aSuper) {

    }

    @Override
    public void visit(This aThis) {

    }

    @Override
    public void visit(WhileStmt whileStmt) {
        whileStmt.loopBlock.accept(this);
    }

    @Override
    public void visit(DoStmt whileStmt) {
        whileStmt.loopBlock.accept(this);
    }

    @Override
    public void visit(Literal literal) {

    }

    @Override
    public void visit(Throw aThrow) {

    }

    @Override
    public void visit(AssignToField assignLeftSide) {
        assignLeftSide.field.accept(this);
    }

    @Override
    public void visit(AssignToLocal assignLeftSide) {
        assignLeftSide.localVar.accept(this);
    }

    @Override
    public void visit(SuperCall superCall) {
        this.visit((MethodCall) superCall);
    }

    @Override
    public void visit(ThisCall thisCall) {

    }

    @Override
    public void visit(Switch switchStmt) {
        switchStmt.getSwitch().accept(this);
        switchStmt.getBlocks().stream().forEach((switchBlock) -> {
            switchBlock.accept(this);
        });
    }

    @Override
    public void visit(SwitchBlock switchBlock) {
        switchBlock.getLabels().stream().forEach((label) -> {
            label.accept(this);
        });
        switchBlock.getStatements().stream().forEach((stmt) -> {
            stmt.accept(this);
        });
    }

    @Override
    public void visit(SwitchLabel switchLabel) {

    }

    @Override
    public void visit(Yield aYield) {
    }

    @Override
    public void visit(ExpressionPattern aPattern) {

    }

    @Override
    public void visit(RecordPattern aRecordPattern) {

    }

    @Override
    public void visit(GuardedPattern aGuardedPattern) {

    }

    @Override
    public void visit(Ternary ternary) {
        ternary.cond.accept(this);
        ternary.iftrue.accept(this);
        ternary.iffalse.accept(this);
    }
}
