package edu.cmu.cs.l3.x86;

import edu.cmu.cs.l3.frame.*;
import edu.cmu.cs.l3.temp.*;
import java.util.*;
import edu.cmu.cs.l3.tree.*;
import edu.cmu.cs.l3.assem.*;

public class FrameIntel extends Frame {

    public static final int EAX=0,EDX=1,ECX=2,EBX=3,ESI=4,EDI=5;

    public FrameIntel() {
	m_reglist = new Hashtable(5000);
        m_spilllist = new ArrayList();
	m_fp = new Temp();
        m_rl = new Label();

        int i;
        m_registers = new Temp[6];
        for (i = 0; i < 6; i++) {
            m_registers[i] = new Temp();
        }

        m_rv = m_registers[EAX];
    }

    public Frame newFrame(int numparams, int level) {
	int i;

	FrameIntel fi = new FrameIntel();

        fi.m_level = level;
	fi.m_numparams = numparams; fi.m_wordsize = 4;
	for (i = 0; i < fi.m_numparams; i++) {
	    fi.m_params.add(new FrameStack(fi.m_wordsize*(i+2),false));
	}

	return fi;
    }

    public Temp getGeneralTemp(int index) {
        if (index < 6) {
            return m_registers[index];
        }

        return new Temp();
    }

    public void finalize(List code) {
        if (code.size() < 5) {
            return;
        }
        code.set(4,new OperInstruction("\tsubl\t$" + (8 + 4*m_stacksize) + ", %esp\n",null,null));
        code.set(code.size()-4,new OperInstruction("\taddl\t$" + (8 + 4*m_stacksize) + ", %esp\n",null,null));
    }

    public List generateEntry(String name) {

        List instrs = new ArrayList();

        instrs.add(new OperInstruction(".globl " + name + "\n",null,null));
        instrs.add(new OperInstruction(name + ":\n",null,null));
     //   instrs.add(new OperInstruction("\tpushl\t%esi\n",null,null));
     //   instrs.add(new OperInstruction("\tpushl\t%edi\n",null,null));
        instrs.add(new OperInstruction("\tpushl\t%ebp\n",null,null));
     //   instrs.add(new OperInstruction("\tpushl\t%ebx\n",null,null));
        instrs.add(new OperInstruction("\tmovl\t%esp, %ebp\n",null,null));
        instrs.add(new OperInstruction("\tsubl\t$" + (8 + 4*m_numlocals) + ", %esp\n",null,null));
        instrs.add(new OperInstruction("\tpushl\t%edi\n",null,null));
        instrs.add(new OperInstruction("\tpushl\t%ebx\n",null,null));
        instrs.add(new OperInstruction("\tpushl\t%esi\n",null,null));

        return instrs;
    }

    public List generateExit() {

        List instrs = new ArrayList();

        instrs.add(new LabelInstruction("." + m_rl.toString()+":\n",m_rl));
        instrs.add(new OperInstruction("\tpopl\t%esi\n",null,null));
        instrs.add(new OperInstruction("\tpopl\t%ebx\n",null,null));
        instrs.add(new OperInstruction("\tpopl\t%edi\n",null,null));
        instrs.add(new OperInstruction("\taddl\t$" + (8 + 4*m_numlocals) + ", %esp\n",null,null));
        instrs.add(new OperInstruction("\tmovl\t%ebp, %esp\n",null,null));
     //   instrs.add(new OperInstruction("\tpopl\t%ebx\n",null,null));
        instrs.add(new OperInstruction("\tpopl\t%ebp\n",null,null));
     //   instrs.add(new OperInstruction("\tpopl\t%edi\n",null,null));
     //   instrs.add(new OperInstruction("\tpopl\t%esi\n",null,null));
        instrs.add(new OperInstruction("\tret\n",null,null));

        return instrs;
    }

    public IRExpression getParam(int index) {

        FrameStack param = (FrameStack) m_params.get(index);
        IRExpression access = new IRMem(new IRBinop(IRBinop.PLUS,
                new IRTemp(m_fp),
                new IRConst(param.getOffset())));

        return access;
    }

    public FrameAccess allocLocal(int escape, int size) {

	FrameAccess newlocal=null;
        Temp t=null;

	switch (escape) {
	    case STACK:
		newlocal = new FrameStack(-(m_stacksize+1)*m_wordsize,false);
                m_stacksize++;
		break;
	    case REG:
                t = new Temp();
		newlocal = new FrameRegister(t,-(m_numlocals+1)*m_wordsize);
		break;
	    case HEAP:
		newlocal = new FrameStack(-(m_numlocals+1)*m_wordsize,true);
		break;
	}

	m_numlocals += size;
	addLocal(newlocal,t);

	return newlocal;
    }

    private void addLocal(FrameAccess local, Temp t) {
        if (t == null) {
            m_spilllist.add(local);
        }
        else {
            m_reglist.put(t,local);
        }
    }

    public Temp allocLocalTemp() {

        Temp newtemp = new Temp();
        FrameAccess newlocal = new FrameRegister(newtemp,-(m_numlocals+1)*m_wordsize);
        addLocal(newlocal,newtemp);
        m_numlocals++;
        //m_locallist.add(newlocal);

        return newtemp;
    }

    public String tempMap(Temp t) {

        if (t.isGlobal()) {
            if (t.isGlobalPointer())
                return "$"+t.getGlobalName();
            else
                return t.getGlobalName();
        }

        if (t.hasValue()) {
            return "$" + t.getValue();
        }

        if (t == m_rv) {
	    return "%eax";
	}
	else if (t == m_fp) {
	    return "%ebp";
	}
        else if (t == m_registers[EAX]) {
            return "%eax";
        }
        else if (t == m_registers[EDX]) {
            return "%edx";
        }
        else if (t == m_registers[ECX]) {
            return "%ecx";
        }
        else if (t == m_registers[ESI]) {
            return "%esi";
        }
        else if (t == m_registers[EDI]) {
            return "%edi";
        }
        else if (t == m_registers[EBX]) {
            return "%ebx";
        }
	else {
        //    System.out.println("WARNING: Allocating non-reg temp!");
            return findTemp(t) + "(%ebp)";
	}
    }

    public Temp[] getRegisters() {
        return m_registers;
    }

    private int findTemp(Temp t) {

        FrameRegister fr = (FrameRegister) m_reglist.get(t);
        if (fr == null) {
            System.out.println("WARNING: Lost Temp!: " + t);
            return 0;
        }
        else {
            return fr.getStackOffset();
        }
    }

    private List m_spilllist;
    private Hashtable m_reglist;
    private Temp[] m_registers;
    private int m_stacksize=1;
}
