// Mike Maxim
// Statement translator

package edu.cmu.cs.l3.translate;

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

public class TranslateStatement extends Translate {

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

    public IRStatement getStatement() {
        return m_stm;
    }

    public void visitResponse(String type, Object obj) {

        int pos=0;

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

        try {
            if (type.equals("ASAssignStatement")) {
                visitAssign(obj);
            }
            else if (type.equals("ASStatementList")) {
                visitStmList(obj);
            }
            else if (type.equals("ASIfStatement")) {
                visitIf(obj);
            }
            else if (type.equals("ASWhileStatement")) {
                visitWhile(obj);
            }
            else if (type.equals("ASForStatement")) {
                visitFor(obj);
            }
            else if (type.equals("ASBreakStatement")) {
                visitBreak(obj);
            }
            else if (type.equals("ASContinueStatement")) {
                visitContinue(obj);
            }
            else if (type.equals("ASBlankStatement")) {
                visitBlank(obj);
            }
            else if (type.equals("ASBlock")) {
                visitBlock(obj);
            }
            else if (type.equals("ASReturnStatement")) {
                visitReturn(obj);
            }
            else if (type.equals("ASExpressionStatement")) {
                visitExpStm(obj);
            }
            else {
                System.out.println("INTERNAL COMPILER ERROR: Skipping: " + type);
            }
        } catch (TranslateException e) {

            m_errorMsg.error(pos,"TRANSLATE: " + e.getMessage());
            System.exit(-1);
        }

    }

    private void visitExpStm(Object p) {

        ASExpressionStatement ap = (ASExpressionStatement) p;

        TranslateExpression etran = chooseTranslator(ap.getExpression());
        ap.getExpression().visit(etran);

        m_stm = etran.asStatement();
    }

    private void visitReturn(Object o) {

        ASReturnStatement ao = (ASReturnStatement) o;

        if (ao.hasReturnExpression()) {
            TranslateExpression etran = chooseTranslator(ao.getReturnExpression());
            ao.getReturnExpression().visit(etran);

            m_stm = new IRSeq(new IRMove(new IRTemp(m_curframe.getReturnTemp()),etran.asExpression()),
                    new IRJump(new IRLabel(m_curframe.getReturnLabel())));
        }
        else {
            m_stm = new IRSeq(new IRMove(new IRTemp(m_curframe.getReturnTemp()), new IRConst(0)),
                              new IRJump(new IRLabel(m_curframe.getReturnLabel())));
        }
    }

    private void visitBlock(Object b) {

        ASBlock block = (ASBlock) b;

        TranslateDeclaration dtran = new TranslateDeclaration(m_curframe,m_errorMsg,m_table);
        TranslateStatement stran = new TranslateStatement(m_curframe,m_errorMsg,m_table);

        m_table.beginScope();
        if (block.getDeclaration() != null) {
            block.getDeclaration().visit(dtran);
        }
        if (block.getStatement() != null) {
            block.getStatement().visit(stran);
        }
        m_table.endScope();

        if (block.getDeclaration() != null && block.getStatement() != null) {
            m_stm = new IRSeq(dtran.getDeclaration(), stran.getStatement());
        }
        else if (block.getDeclaration() != null) {
            m_stm = dtran.getDeclaration();
        }
        else if (block.getStatement() != null) {
            m_stm = stran.getStatement();
        }
        else {
            m_stm = new IRLabel(new Label());
        }
    }

    private void visitBlank(Object b) {
	m_stm = new IRLabel(new Label());
    }

    private void visitFor(Object f) {

	ASForStatement af = (ASForStatement) f;

	TranslateExpression testtran;
	TranslateStatement bodytran,worktran,inittran;

	Label tdl = new Label();
	Label tbl = new Label();
	Label ttl = new Label();
	Label til = new Label();

	IRLabel donelabel = new IRLabel(tdl);
	IRLabel bodylabel = new IRLabel(tbl);
	IRLabel testlabel = new IRLabel(ttl);
	IRLabel inclabel = new IRLabel(til);

        testtran = chooseTranslator(af.getEndTest());

        inittran = new TranslateStatement(m_curframe,m_errorMsg, m_table);
	bodytran = new TranslateStatement(m_curframe,m_errorMsg, m_table);
	worktran = new TranslateStatement(m_curframe,m_errorMsg,m_table);

	m_donestack.push(tdl); m_teststack.push(til);

	af.getAssignment().visit(inittran);
	af.getEndTest().visit(testtran);
	af.getForWorkStatement().visit(worktran);
	af.getBody().visit(bodytran);

	m_donestack.pop(); m_teststack.pop();

	IRSeq incseq = new IRSeq(inclabel,worktran.getStatement());
	IRSeq workseq = new IRSeq(bodytran.getStatement(), incseq);
	IRSeq jworkseq = new IRSeq(workseq, new IRJump(testlabel));
	IRSeq bodyseq = new IRSeq(bodylabel,jworkseq);
	IRSeq ifseq = new IRSeq(testtran.asConditional(tbl,tdl), bodyseq);
	IRSeq testseq = new IRSeq(testlabel, ifseq);
	IRSeq forseq = new IRSeq(new IRSeq(inittran.getStatement(), testseq), donelabel);

	m_stm = forseq;
    }

    private void visitWhile(Object w) {

	ASWhileStatement aw = (ASWhileStatement) w;

	TranslateExpression testtran;
	TranslateStatement bodytran;

	Label donelabel = new Label();
	Label bodylabel = new Label();
	Label testlabel = new Label();

        testtran = chooseTranslator(aw.getTest());

	bodytran = new TranslateStatement(m_curframe,m_errorMsg, m_table);

	m_teststack.push(testlabel); m_donestack.push(donelabel);

	aw.getTest().visit(testtran);
	aw.getBody().visit(bodytran);

	m_teststack.pop(); m_donestack.pop();

	IRSeq bodyseq = new IRSeq(bodytran.getStatement(), new IRJump(new IRLabel(testlabel)));
	IRSeq blockseq = new IRSeq(new IRLabel(bodylabel), bodyseq);
	IRSeq ifseq = new IRSeq(testtran.asConditional(bodylabel,donelabel), blockseq);
	IRSeq mainseq = new IRSeq(ifseq, new IRLabel(donelabel));

	m_stm = new IRSeq(new IRLabel(testlabel), mainseq);

    }

    private void visitIf(Object i) {

	ASIfStatement ai = (ASIfStatement) i;
	TranslateExpression tctran=null;
	TranslateStatement thentran,elsetran;
	boolean haselse = false;
	Label tlabel = new Label();
	Label flabel = new Label();
	Label done = new Label();

        tctran = chooseTranslator(ai.getTest());

	thentran = new TranslateStatement(m_curframe,m_errorMsg, m_table);
	elsetran = new TranslateStatement(m_curframe,m_errorMsg, m_table);

	ai.getTest().visit(tctran);
	ai.getThenClause().visit(thentran);
	if (ai.getElseClause() != null) {
	    haselse = true;
	    ai.getElseClause().visit(elsetran);
	}

	if (!haselse) {
	    IRSeq thenseq = new IRSeq(new IRLabel(tlabel),thentran.getStatement());
	    IRLabel elseseq = new IRLabel(flabel);
	    IRSeq ifseq = new IRSeq(thenseq,elseseq);
	    m_stm = new IRSeq(tctran.asConditional(tlabel,flabel),ifseq);
	}
	else {
	    IRSeq thenseq = new IRSeq(new IRLabel(tlabel),thentran.getStatement());
	    IRSeq uberthenseq = new IRSeq(thenseq, new IRJump(new IRLabel(done)));
	    IRSeq elseseq = new IRSeq(new IRLabel(flabel),elsetran.getStatement());
	    IRSeq combseq = new IRSeq(uberthenseq,elseseq);
	    IRSeq doneseq = new IRSeq(combseq,new IRLabel(done));
	    m_stm = new IRSeq(tctran.asConditional(tlabel,flabel),doneseq);
	}
    }

    private void visitBreak(Object obj) throws TranslateException {
        if (m_donestack.empty()) {
            error(((ASBreakStatement)obj).getPosition(),"Break outside of Loop");
            return;
        }
	m_stm = new IRJump(new IRLabel((Label)m_donestack.peek()));
    }

    private void visitContinue(Object obj) throws TranslateException {
        if (m_teststack.empty()) {
            error(((ASContinueStatement)obj).getPosition(),"Continue outside of Loop");
            return;
        }
	m_stm = new IRJump(new IRLabel((Label)m_teststack.peek()));
    }

    private void visitAssign(Object s) throws TranslateException {

	ASAssignStatement as = (ASAssignStatement) s;

	TranslateExpression rhs = chooseTranslator(as.getExpression());
	TranslateVariable vhs = new TranslateVariable(m_curframe,m_errorMsg, m_table);
	as.getExpression().visit(rhs); as.getVariable().visit(vhs);

        IRTemp rhstemp = new IRTemp(m_curframe.allocLocalTemp());
        IRStatement rhsmove = new IRMove(rhstemp, rhs.asExpression());
        m_stm = new IRSeq(rhsmove, new IRMove(vhs.getVariable(), rhstemp));
    }

    private void visitStmList(Object s) {

	ASStatementList ss = (ASStatementList) s;

        if (ss.getSize() == 0) {
            m_stm = new IRLabel(new Label());
            return;
        }

	TranslateStatement h = new TranslateStatement(m_curframe,m_errorMsg, m_table);
	TranslateStatement t = new TranslateStatement(m_curframe,m_errorMsg, m_table);

	if (ss.getSize() > 1) {
	    ASStatementList tail = ss.removeFront();
	    ss.getStatement(0).visit(h);
	    tail.visit(t);
	    m_stm = new IRSeq(h.getStatement(), t.getStatement());
	}
	else {
	    ss.getStatement(0).visit(h);

	    m_stm = h.getStatement();
	}

    }

    private IRStatement m_stm;
    private static Stack m_donestack = new Stack(),m_teststack = new Stack();
}
