package edu.cmu.cs.l3.translate;

import edu.cmu.cs.l3.absyn.*;
import edu.cmu.cs.l3.symbol.Symbol;
import edu.cmu.cs.l3.errormsg.*;
import edu.cmu.cs.l3.temp.Temp;
import edu.cmu.cs.l3.frame.*;
import java.util.*;
import edu.cmu.cs.l3.tree.*;
import edu.cmu.cs.l3.semant.*;
import edu.cmu.cs.l3.general.*;
import edu.cmu.cs.l3.temp.*;

class TranslateExpressionEx
    extends TranslateExpression {

    public TranslateExpressionEx(Frame curframe, ErrorMsg e,
                                 TranslateEnvironment t) {
        super(curframe, e, t);
    }

    public IRExpression asExpression() {
        return m_exp;
    }

    public IRStatement asStatement() {
        return new IRStatementExpression(m_exp);
    }

    public IRStatement asConditional(Label t, Label f) {

        if (m_exp instanceof IRConst) {
            if ( ( (IRConst) m_exp).getValue() == 0)
                return new IRJump(new IRLabel(f));
            else if ( ( (IRConst) m_exp).getValue() == 1)
                return new IRJump(new IRLabel(t));
        }

        return new IRCJump(IRCJump.NE, m_exp,
                           (IRExpression)new IRConst(0), new IRLabel(t),
                           new IRLabel(f));
    }

    public void visitResponse(String type, Object obj) {

        try {
            if (type.equals("ASIntConstant")) {
                visitIntExp(obj);
            }
            else if (type.equals("ASOpExpression")) {
                visitOpExp(obj);
            }
            else if (type.equals("ASLvalExpression")) {
                visitVarExp(obj);
            }
            else if (type.equals("ASPointerExpression")) {
                visitPointerExp(obj);
            }
            else if (type.equals("ASProcedureExpression")) {
                visitProcExp(obj);
            }
            else if (type.equals("ASNullConstant")) {
                visitNullConstant(obj);
            }
            else {
                System.out.println("INTERNAL COMPILER ERROR: Skipping: " + type);
            }
        }
        catch (TranslateException e) {
            System.out.println(e.getMessage());
            System.exit( -1);
        }
    }

    private void visitNullConstant(Object n) {
        ASNullConstant an = (ASNullConstant) n;
        IRExpression ntemp = new IRTemp(m_curframe.allocLocalTemp());
        m_exp = new IRESeq(new IRSeq(new IRComment("Null Deposit"),
                                     new IRMove(ntemp, m_memtran.getNullTemp())), ntemp);
    }

    private void visitProcExp(Object p) {

        ASProcedureExpression ap = (ASProcedureExpression) p;
        List params = ap.getParameters();
        IRExpressionList paramlist = new IRExpressionList();
        int i;

        for (i = 0; i < params.size(); i++) {
            ASExpression exp = (ASExpression) params.get(i);
            TranslateExpression etran = chooseTranslator(exp);
            exp.visit(etran);
            paramlist = paramlist.append(etran.asExpression());
        }

        m_exp = new IRCall(new IRName(ap.getName().toString()), paramlist);
    }

    private void visitPointerExp(Object o) throws TranslateException {

        ASPointerExpression ao = (ASPointerExpression) o;
        IRExpression init = null;
        TranslateExpression tran = chooseTranslator(ao);

        switch (ao.getOperation()) {
            case ASPointerExpression.AMP:
                ASLval var = ((ASLvalExpression)ao.getOperand()).getVariable();
                TranslateVariable vtran = new TranslateVariable(m_curframe, m_errorMsg, m_table);
                var.visit(vtran);
                if (var instanceof ASDerefLval)
                    m_exp = m_memtran.copyFatPointer(m_curframe, vtran.getFatPointer());
                else
                    m_exp = m_memtran.addressOf(m_curframe, vtran.getVariable());
                break;
            case ASPointerExpression.OFFSET:
                ao.getOperand().visit(tran);
                m_exp = m_memtran.pointerInfo(m_curframe, tran.asExpression(),
                                              OFFSET);
                break;
            case ASPointerExpression.SIZE:
                ao.getOperand().visit(tran);
                m_exp = m_memtran.pointerInfo(m_curframe, tran.asExpression(),
                                              SIZE);
                break;
            case ASPointerExpression.INC:
                ao.getOperand().visit(tran);
                m_exp = m_memtran.pointerIncrement(m_curframe, tran.asExpression());
                break;
            case ASPointerExpression.DEC:
                ao.getOperand().visit(tran);
                m_exp = m_memtran.pointerDecrement(m_curframe, tran.asExpression());
                break;
            case ASPointerExpression.ALLOC:

                TranslateExpression ntran = chooseTranslator(ao.getNumber());

                ao.getNumber().visit(ntran);

                IRTemp numels = new IRTemp(m_curframe.allocLocalTemp());
                IRStatement movetemp = new IRMove(numels, ntran.asExpression());
                IRExpression allocsize = numels;

                IRExpression mem = m_memtran.allocateHeapMemory(m_curframe,
                    allocsize, true);
                IRTemp memtmp = new IRTemp(m_curframe.allocLocalTemp());
                IRTemp intmp = new IRTemp(m_curframe.allocLocalTemp());

                IRStatement memmove = new IRMove(memtmp, mem);
                IRLabel doalloc = new IRLabel(new Label());
                IRLabel failalloc = new IRLabel(new Label());
                IRStatement negerror = new IRCJump(IRCJump.GE, numels, new IRConst(0),
                        doalloc, failalloc);
                int pos = ao.getPosition();
                IRExpressionList params = new IRExpressionList(Translator.getFileLabel(m_errorMsg.getFile(pos)));
                params = params.append(new IRConst(m_errorMsg.getLine(pos)));
                params = params.append(Translator.getErrorLabel(Translator.NEGALLOC));
                IRStatement error = new IRStatementExpression(
                    new IRCall(new IRName(Globals.extFunctionName("_l3_error")), params));
                IRLabel succ = new IRLabel(new Label());

                if (ao.getType().getTypeInfo().getSemantType()instanceof
                    TyRecord ||
                    ao.getType().getTypeInfo().getSemantType()instanceof
                    TyPointer) {

                    IRLabel tlabel = new IRLabel(new Label());
                    IRLabel flabel = new IRLabel(new Label());
                    IRLabel toplabel = new IRLabel(new Label());
                    IRTemp counter = new IRTemp(m_curframe.allocLocalTemp());

                    init = allocInitExp(ao.getType().getTypeInfo().getSemantType());
                    IRStatement inmove = new IRMove(intmp, init);
                    IRStatement initstm = new IRSeq(new IRMove(counter,
                        new IRConst(0)), new IRSeq(
                        toplabel, new IRSeq(
                        new IRCJump(IRCJump.LT, counter, numels, tlabel, flabel),
                        new IRSeq(new IRSeq(tlabel, new IRSeq(
                        new IRMove(m_memtran.unsafeArrayAccess(m_curframe,
                        memtmp, counter), init),
                        new IRSeq(new IRMove(counter,
                                             new IRBinop(IRBinop.PLUS, counter,
                        new IRConst(1))), new IRJump(toplabel)))), flabel))));

                    m_exp = new IRESeq(new IRSeq(new IRSeq(new IRSeq(new IRSeq(movetemp, negerror),
                        new IRSeq(doalloc,
                                                 new IRSeq(memmove,
                        new IRSeq(inmove, new IRSeq(initstm, new IRJump(succ)))))),
    new IRSeq(failalloc, error)), succ), memtmp);
                }
                else
                    m_exp = new IRESeq(
                        new IRSeq(new IRSeq(new IRSeq(new IRSeq(movetemp, negerror),
                        new IRSeq(doalloc, new IRSeq(memmove, new IRJump(succ)))),
                        new IRSeq(failalloc, error)), succ), memtmp);

                break;
        }
    }

    private void visitIntExp(Object i) {

        ASIntConstant ai = (ASIntConstant) i;

        m_exp = new IRConst(ai.getValue());
    }

    private void visitOpExp(Object i) {

        ASOpExpression ai = (ASOpExpression) i;

        TranslateExpression l = chooseTranslator(ai.getLeft());
        TranslateExpression r = null;

        if (ai.getLeft() != null) {
            ai.getLeft().visit(l);
        }

        if (ai.getRight() != null) {
            r = chooseTranslator(ai.getRight());
            ai.getRight().visit(r);
        }

        switch (ai.getOperator()) {
            case ASOpExpression.PADD:
                m_exp = m_memtran.pointerAddition(m_curframe, l.asExpression(),
                                                  r.asExpression(), true);
                break;
            case ASOpExpression.PMINUS:
                m_exp = m_memtran.pointerAddition(m_curframe, l.asExpression(),
                                                  r.asExpression(), false);
                break;
            case ASOpExpression.PEQ:
                m_exp = m_memtran.pointerEquality(m_curframe, l.asExpression(),
                                                  r.asExpression(), 1);
                break;
            case ASOpExpression.PNEQ:
                m_exp = m_memtran.pointerEquality(m_curframe, l.asExpression(),
                                                  r.asExpression(), 0);
                break;
            case ASOpExpression.PLUS:
                m_exp = new IRBinop(IRBinop.PLUS, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.MINUS:
                m_exp = new IRBinop(IRBinop.MINUS, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.MUL:
                m_exp = new IRBinop(IRBinop.MUL, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.DIV:
                m_exp = translateDivision(l.asExpression(), r.asExpression(),
                                          IRBinop.DIV, ai.getPosition());
                break;
            case ASOpExpression.MOD:
                m_exp = translateDivision(l.asExpression(), r.asExpression(),
                                          IRBinop.MOD, ai.getPosition());
                break;
            case ASOpExpression.NEG:
                m_exp = new IRBinop(IRBinop.MINUS, new IRConst(0),
                                    l.asExpression());
                break;
            case ASOpExpression.BAND:
                m_exp = new IRBinop(IRBinop.BAND, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.BOR:
                m_exp = new IRBinop(IRBinop.BOR, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.BXOR:
                m_exp = new IRBinop(IRBinop.BXOR, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.BFLIP:
                m_exp = new IRBinop(IRBinop.BXOR, l.asExpression(),
                                    new IRConst(0xffffffff));
                break;
            case ASOpExpression.LSHIFT:
                m_exp = new IRBinop(IRBinop.LSHIFT, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.RSHIFT:
                m_exp = new IRBinop(IRBinop.RSHIFT, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.AND:
                m_exp = new IRBinop(IRBinop.AND, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.OR:
                m_exp = new IRBinop(IRBinop.OR, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.XOR:
                m_exp = new IRBinop(IRBinop.XOR, l.asExpression(),
                                    r.asExpression());
                break;
            case ASOpExpression.NOT:
                m_exp = new IRBinop(IRBinop.XOR, l.asExpression(),
                                    new IRConst(1));
                break;
            default:
                System.out.println("WARNING: Unable to process: " +
                                   ai.getOperator());
        }

    }

    private void visitVarExp(Object v) {

        ASLvalExpression av = (ASLvalExpression) v;

        TranslateVariable varTranslator = new TranslateVariable(m_curframe,
            m_errorMsg, m_table);
        av.getVariable().visit(varTranslator);

        m_exp = varTranslator.getVariable();
    }

    private IRExpression translateDivision(IRExpression ileft, IRExpression iright, int op, int pos) {

        IRLabel iszero = new IRLabel(new Label()); IRLabel nonzero = new IRLabel(new Label());
        IRLabel succ = new IRLabel(new Label());

        IRTemp left = new IRTemp(m_curframe.allocLocalTemp()),
            right = new IRTemp(m_curframe.allocLocalTemp());
        IRStatement leftmove = new IRMove(left, ileft);
        IRStatement rightmove = new IRMove(right, iright);
        IRStatement zerotest = new IRCJump(IRCJump.NE, right, new IRConst(0), nonzero, iszero);
        IRTemp res = new IRTemp(m_curframe.allocLocalTemp());
        IRExpression oper = new IRBinop(op, left, right);

        IRExpressionList params = new IRExpressionList(Translator.getFileLabel(m_errorMsg.getFile(pos)));
        params = params.append(new IRConst(m_errorMsg.getLine(pos)));
        params = params.append(Translator.getErrorLabel(Translator.DIVZERO));
        IRExpression error = new IRCall(new IRName(Globals.extFunctionName("_l3_error")), params);

        IRStatement dotest = new IRSeq(new IRSeq(zerotest, new IRSeq(
            new IRSeq(iszero, new IRSeq(new IRMove(res, error), new IRJump(succ))),
            new IRSeq(nonzero, new IRMove(res, oper)))), succ);

        return new IRESeq(new IRSeq(new IRSeq(leftmove, rightmove), dotest), res);
    }

    private IRExpression m_exp;
}