package edu.cmu.cs.l3.semant;

import edu.cmu.cs.l3.absyn.*;
import edu.cmu.cs.l3.general.*;
import java.util.*;

public class TyPrinter
    implements Visitor {

    public TyPrinter() {
    }

    public void visitResponse(String type, Object obj) {

        ASTypeInfo tyinfo = null;

        if (type.equals("ASSimpleVariable")) {
            visitSimpleVar(obj);
        }
        else if (type.equals("ASIntConstant")) {
            visitIntConstant(obj);
        }
        else if (type.equals("ASVariableExpression")) {
            visitVarExp(obj);
        }
        else if (type.equals("ASBoolConstant")) {
            visitBoolConstant(obj);
        }
        else if (type.equals("ASDeclarationVariable")) {
            visitDecVar(obj);
        }
        else if (type.equals("ASDeclarationType")) {
            visitDecType(obj);
        }
        else if (type.equals("ASDeclarationList")) {
            visitDecList(obj);
        }
        else if (type.equals("ASAssignStatement")) {
            visitAssign(obj);
        }
        else if (type.equals("ASStatementList")) {
            visitStmList(obj);
        }
        else if (type.equals("ASIfStatement")) {
            visitIf(obj);
        }
        else if (type.equals("ASBlock")) {
            visitBlock(obj);
        }
        else if (type.equals("ASFieldVariable")) {
            visitFieldVariable(obj);
        }
        else if (type.equals("ASOpExpression")) {
            visitOpExp(obj);
        }
        else if (type.equals("ASPointerExpression")) {
            visitPointerExp(obj);
        }
        else if (type.equals("ASPrimitiveType")) {
            visitPrimitiveType(obj);
        }
        else if (type.equals("ASRecordType")) {
            visitRecordType(obj);
        }
        else if (type.equals("ASExpressionStatement")) {
            visitStmExp(obj);
        }
        else if (type.equals("ASMemVariable")) {
            visitMemVar(obj);
        }
        else if (type.equals("ASNullConstant")) {
            visitNull(obj);
        }
        else if (type.equals("ASUserType")) {
            visitUserType(obj);
        }
        else if (type.equals("ASPointerType")) {
            visitPointerType(obj);
        }
        else if (type.equals("ASReturnStatement")) {
            visitReturn(obj);
        }
        else if (type.equals("ASForStatement")) {
            visitFor(obj);
        }
        else if (type.equals("ASWhileStatement")) {
            visitWhile(obj);
        }
        else if (type.equals("ASDeclarationProc")) {
            visitProc(obj);
        }
        else if (type.equals("ASProcedureExpression")) {
            visitProcExp(obj);
        }
        else if (type.equals("ASExpressionStatement")) {
            visitStmExp(obj);
        }
        else if (type.equals("ASBreakStatement") ||
                 type.equals("ASContinueStatement")) {

        }
        else {
            System.out.println(
                "Severe Internal Compiler Warning: Visit passing unknown visitable!: " +
                type);
        }

        if (obj instanceof ASStatement) {
            tyinfo = ( (ASStatement) obj).getTypeInfo();
        }
        else if (obj instanceof ASExpression) {
            tyinfo = ( (ASExpression) obj).getTypeInfo();
        }
        else if (obj instanceof ASLval) {
            tyinfo = ( (ASLval) obj).getTypeInfo();
        }
        else if (obj instanceof ASDeclaration) {
            tyinfo = ( (ASDeclaration) obj).getTypeInfo();
        }
        else if (obj instanceof ASType) {
            tyinfo = ( (ASType) obj).getTypeInfo();
        }

        System.out.print("[ABSYN:" + type);
        if (tyinfo.getSemantType() != null) {
            System.out.print(" FINAL TYPE:" + tyinfo.getSemantType().getName());
        }
        System.out.print("]\n");
    }

    private void visitProcExp(Object p) {

        ASProcedureExpression ap = (ASProcedureExpression) p;
        int i;

        for (i = 0; i < ap.getParameters().size(); i++) {
            ( (ASExpression) ap.getParameters().get(i)).visit(this);
        }
    }

    private void visitStmExp(Object s) {
        ASExpressionStatement as = (ASExpressionStatement) s;
        as.getExpression().visit(this);
    }

    private void visitProc(Object pp) {
        ASDeclarationProc ap = (ASDeclarationProc) pp;

        if (ap.getBody() != null)
            ap.getBody().visit(this);
    }

    public void visitWhile(Object w) {
        ASWhileStatement aw = (ASWhileStatement) w;
        aw.getTest().visit(this);
        aw.getBody().visit(this);
    }

    public void visitFor(Object f) {
        ASForStatement af = (ASForStatement) f;
        af.getAssignment().visit(this);
        af.getEndTest().visit(this);
        af.getForWorkStatement().visit(this);
        af.getBody().visit(this);
    }

    private void visitReturn(Object r) {

        ASReturnStatement ar = (ASReturnStatement) r;

        if (ar.hasReturnExpression()) {
            ar.getReturnExpression().visit(this);
        }
    }

    private void visitPointerType(Object p) {
        ASPointerType ap = (ASPointerType) p;
        ap.getUnderType().visit(this);
    }

    public void visitUserType(Object t) {
    }

    public void visitNull(Object n) {
    }

    public void visitMemVar(Object v) {
        ASDerefLval av = (ASDerefLval) v;
        av.getVariableExpression().visit(this);
    }

    public void visitBlankStm(Object b) {
    }

    public void visitSimpleVar(Object v) {
    }

    public void visitIntConstant(Object i) {
    }

    public void visitBlock(Object b) {
        ASBlock ab = (ASBlock) b;
        if (ab.getDeclaration() != null) {
            ab.getDeclaration().visit(this);
        }
        if (ab.getStatement() != null) {
            ab.getStatement().visit(this);
        }
    }

    public void visitFieldVariable(Object v) {
        ASFieldLval av = (ASFieldLval) v;
        av.getRecord().visit(this);
    }

    public void visitPointerExp(Object e) {
        ASPointerExpression ae = (ASPointerExpression) e;
        if (ae.getOperation() == ASPointerExpression.AMP ||
            ae.getOperation() == ASPointerExpression.OFFSET ||
            ae.getOperation() == ASPointerExpression.SIZE) {
            ae.getOperand().visit(this);
        }
        else {
            ae.getType().visit(this);
            ae.getNumber().visit(this);
            if (ae.getInit() != null) {
                ae.getInit().visit(this);
            }
        }
    }

    public void visitPrimitiveType(Object t) {
    }

    public void visitRecordType(Object t) {
        ASRecordType at = (ASRecordType) t;
        List els = at.getFields();
        int i;
        for (i = 0; i < els.size(); i++) {
            ( (ASType) ( (Pair) els.get(i)).getRight()).visit(this);
        }
    }

    public void visitOpExp(Object e) {
        ASOpExpression ai = (ASOpExpression) e;
        ai.getLeft().visit(this);
        if (ai.getRight() != null) {
            ai.getRight().visit(this);
        }
    }

    public void visitVarExp(Object v) {
        ASLvalExpression av = (ASLvalExpression) v;
        av.getVariable().visit(this);
    }

    public void visitBoolConstant(Object e) {
    }

    public void visitDecVar(Object v) {
        ASDeclarationVariable d = (ASDeclarationVariable) v;
        d.getTypeInfo().getASType().visit(this);
        if (d.getInitExpression() != null) {
            d.getInitExpression().visit(this);
        }
    }

    public void visitDecType(Object v) {
        ASDeclarationType d = (ASDeclarationType) v;
        d.getType().visit(this);
    }

    public void visitDecList(Object dl) {
        int i;
        ASDeclarationList adl = (ASDeclarationList) dl;
        if (adl != null) {
            for (i = 0; i < adl.getSize(); i++) {
                adl.getDeclaration(i).visit(this);
            }
        }
    }

    public void visitAssign(Object s) {
        ASAssignStatement as = (ASAssignStatement) s;
        as.getVariable().visit(this);
        as.getExpression().visit(this);
    }

    public void visitStmList(Object ss) {
        int i;
        ASStatementList adl = (ASStatementList) ss;
        if (adl != null) {
            for (i = 0; i < adl.getSize(); i++) {
                adl.getStatement(i).visit(this);
            }
        }
    }

    public void visitIf(Object i) {
        ASIfStatement ifstm = (ASIfStatement) i;
        ifstm.getTest().visit(this);
        ifstm.getThenClause().visit(this);
        if (ifstm.getElseClause() != null) {
            ifstm.getElseClause().visit(this);
        }
    }

}