package edu.cmu.cs.l3.translate;

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

public class TranslateDeclaration extends Translate {

    public TranslateDeclaration(Frame frame, ErrorMsg errormsg, TranslateEnvironment env) {
	super(frame,errormsg,env);
    }

    public IRStatement getDeclaration() {
	return m_stm;
    }

    public void visitResponse(String type, Object obj){

        try {
            if (type.equals("ASDeclarationList")) {
                visitDecList(obj);
            }
            else if (type.equals("ASDeclarationVariable")) {
                visitDecVar(obj);
            }
            else if (type.equals("ASDeclarationType")) {
                // Do nothing
            }
            else if (type.equals("ASDeclarationProc")) {
                visitProc(obj);
            }
        } catch (TranslateException e) {
            System.out.println(e.getMessage());
            System.exit(-1);
        }
        catch (SymbolTableException e) {
            System.out.println(e.getMessage());
            System.exit(-1);
        }
    }

    private void visitProc(Object p) throws SymbolTableException {

        ASDeclarationProc ap = (ASDeclarationProc) p;
        Frame procframe = m_curframe.newFrame(ap.getParams().size(),1);
        IRStatement parammoves=null;
        String fname;
        int i;

        if (ap.getBody() == null) return;

        for (i = 0; i < ap.getParams().size(); i++) {
            Symbol pname = (Symbol)((Pair)ap.getParams().get(i)).getLeft();
            Ty pty = ((ASType)(((Pair)ap.getParams().get(i)).getRight())).getTypeInfo().getSemantType();

            IRExpression stackloc = procframe.getParam(i);
            IRTemp etmp = new IRTemp(procframe.allocLocalTemp());
            if (!pty.isEscaping()) {
                m_table.addEntry(pname, etmp);
                if (parammoves == null)
                    parammoves = new IRMove(etmp, stackloc);
                else
                    parammoves = new IRSeq(parammoves, new IRMove(etmp, stackloc));
            }
            else {
                IRExpression heaploc = m_memtran.allocateHeapMemory(procframe, new IRConst(1), false);
                if (parammoves == null)
                    parammoves =
                        new IRSeq(new IRMove(etmp, heaploc), new IRMove(
                        new IRMem(etmp), stackloc));
                else
                    parammoves =
                        new IRSeq(parammoves, new IRSeq(new IRMove(etmp, heaploc),
                        new IRMove(new IRMem(etmp), stackloc)));

                m_table.addEntry(pname, new IRMem(etmp));
            }
        }

        TranslateStatement stran = new TranslateStatement(procframe,m_errorMsg,m_table);

        ap.getBody().visit(stran);

        for (i = 0; i < ap.getParams().size(); i++) {
            Symbol pname = (Symbol)((Pair)ap.getParams().get(i)).getLeft();
            m_table.removeEntry(pname,true);
        }

        if (parammoves != null)
            m_stm = new IRSeq(parammoves, stran.getStatement());
        else
            m_stm = stran.getStatement();

       if ((fname = ap.getName().toString()).equals("main")) {
           m_stm = new IRSeq(m_stm,
                             new IRMove(new IRTemp(procframe.getReturnTemp()), new IRConst(0)));
           fname = Globals.extFunctionName("main");
       }
       Translator.addFragment(new FragmentCode(procframe,fname,m_stm));
    }

    private void visitDecVar(Object d) throws TranslateException, SymbolTableException {

	ASDeclarationVariable ad = (ASDeclarationVariable) d;
        Ty varty;
        IRExpression init;

        varty = ad.getTypeInfo().getSemantType();
        init = allocInitExp(varty);

        if (varty.isEscaping()) {
            IRExpression hspace = m_memtran.allocateHeapMemory(m_curframe,
                new IRConst(m_curframe.getWordSize()),
                false);
            IRExpression vtmp = new IRTemp(m_curframe.allocLocalTemp());

            m_table.addEntry(ad.getVariableName(), new IRMem(vtmp));
            if (varty instanceof TyPointer ||
                varty instanceof TyRecord)
                m_stm = new IRSeq(new IRMove(vtmp, hspace),
                              new IRMove(new IRMem(vtmp), init));
            else
                m_stm = new IRMove(vtmp, hspace);
        }
        else {
            IRTemp vtmp = new IRTemp(m_curframe.allocLocalTemp());
            m_table.addEntry(ad.getVariableName(), vtmp);
            if (varty instanceof TyPointer || varty instanceof TyRecord)
                m_stm = m_memtran.copyExpression(m_curframe, vtmp, init, varty);
            else
                m_stm = new IRLabel(new Label());
        }

    }

    private void visitDecList(Object l) {

	ASDeclarationList al = (ASDeclarationList) l;

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

	if (al.getSize() > 1) {
	    ASDeclarationList tail = al.removeFront();
	    al.getDeclaration(0).visit(h);
	    tail.visit(t);
	    m_stm = new IRSeq(h.getDeclaration(), t.getDeclaration());
	}
	else {
	    al.getDeclaration(0).visit(h);

	    m_stm = h.getDeclaration();
	}
    }

    private IRStatement m_stm;
}