/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template 
 varsin the editor.
 */
package target.mips;

import java.util.LinkedList;

/**
 *
 * @author Eugenio
 */
public class Registers {

    public static String R_S0 = "s0";
    public static String R_S1 = "s1";
    public static String R_S2 = "s2";
    public static String R_S3 = "s3";
    public static String R_S4 = "s4";
    public static String R_S5 = "s5";
    public static String R_S6 = "s6";
    public static String R_S7 = "s7";

    public static String R_ZERO = "zero";
    public static String R_FP = "fp";
    public static String R_SP = "sp";
    public static String R_RA = "ra";
    public static String R_V0 = "v0";
    protected static String R_V1 = "v1";
    protected static String R_GP = "gp";

    protected int arg = 0;

    public String Arg() {
        return "a" + (arg++);
    }

    public void ResetArgs() {
        arg = 0;
    }

    protected final LinkedList<String> avaliableSaved = new LinkedList<String>() {
        {
            for (int i = 0; i < 8; i++) {
                add("s" + i);
            }
        }
    };
    protected final LinkedList<String> avaliableTemp = new LinkedList<String>() {
        {
            for (int i = 0; i < 9; i++) {
                add("t" + i);
            }
        }
    };
    protected final LinkedList<String> usedSaved = new LinkedList<>();
    protected final LinkedList<String> usedTemp = new LinkedList<>();

//    public static Instruction regCtrl;
//    protected int line;
//    protected int paramLoaded;
//    protected Code code;
//    protected Allocation allocation;
//    protected Ocorrence variavel;
//    protected HashMap<String, String> vars;
//    protected ArrayList<Instruction> instrucoes;
//    protected HashMap<String, Ocorrence> varsOcorrence;
    public Registers() {
        //        this.code = code;
        //        regCtrl = new Instruction();
        //        vars = new HashMap<>();
    }

    public boolean Free(String reg) throws Exception {
//        System.out.println("Free:" + reg);
        return exec(reg, "free") != null;
    }

    public String Lock(String reg) throws Exception {
        return exec(reg, "lock");
    }

    private String exec(String reg, String act) throws Exception {
//        System.out.println("Exec:" + reg + ":" + act);
        LinkedList<String> u, a;
        String r = null;
        switch (reg.substring(0, 1)) {
            case "t":
                u = usedTemp;
                a = avaliableTemp;
                break;
            case "s":
                u = usedSaved;
                a = avaliableSaved;
                break;
            default:
                throw new Exception("Try " + act + " register '%s'");
        }
        switch (act) {
            case "free":
                if (!u.isEmpty()) {
//                    System.out.println("Try remove(" + reg + ") from " + u);
                    // Encontra o registrador usado e coloca como disponivel
                    int index = u.indexOf(reg);
                    if (index >= 0) {
                        r = u.remove(index);
                        a.add(r);
                    }
                }
                break;
            case "lock":
                if (!a.isEmpty()) {
                    // Pega o primeiro registrador disponivel e marca como usado
                    r = a.removeFirst();
                    u.add(r);
                }
                break;
        }
        return r;
    }

//    protected void visit() throws Exception {
//
//        Instruction reg = instrucoes.get(line);
//
//        switch (reg.get("type")) {
//            case "assign":
//                genAttrib(reg);
//                break;
//            case "indexed_assign":
//                genAttribIndexed(reg);
//                break;
//            case "atribuicao_booleana":
//                genBooleanAttrib(reg);
//                break;
//            case "jump":
//                genJump(reg);
//                break;
//            case "branch":
//                genConditionalBranch(reg);
//                break;
//            case "call":
//                genCall(reg);
//                break;
//            case "return":
//                gerarReturn(reg);
//                break;
//            case "param":
//                genParam(reg);
//                break;
//            case "label":
//                addLabel(reg);
//                break;
//            default:
//        }
//    }
//
//    protected void genJump(Instruction reg) {
//
//    }
//
//    protected void genParam(Instruction reg) throws Exception {
//        int baseA = 4;
//        if (paramLoaded > 3) {
//            return;
//        }
//        allocation.register(line, reg.get("_return"));
//        paramLoaded++;
//    }
//
//    protected void genCall(Instruction reg) {
//        paramLoaded = 0;
//    }
//
//    protected void addLabel(Instruction reg) {
//
//    }
//
//    protected void genAttrib(Instruction reg) throws Exception {
//        allocation.register(line, reg.get("dst"));
//
//        if (reg.Has("param1")) {
//            allocation.register(line, reg.get("param1"));
//        }
//        if (reg.Has("param2")) {
//            allocation.register(line, reg.get("param2"));
//        }
//    }
//
//    protected void genAttribIndexed(Instruction reg) {
//
//    }
//
//    protected void genBooleanAttrib(Instruction reg) {
//
//        System.out.println("RegAlloc[genBooleanAttrib]:" + reg);
//    }
//
//    protected void genConditionalBranch(Instruction reg) {
//
//    }
//
//    protected void gerarReturn(Instruction reg) {
//
//    }
//
//    public Allocation allocate(String block, int sizeOfBlock) throws Exception {
//        varsOcorrence = code.getVarOcorrences(block);
//
//        allocation = new Allocation(sizeOfBlock, varsOcorrence);
//
//        vars = new LinkedHashMap<>();
//        instrucoes = code.Block().Instructions();
//
//        paramLoaded = 0;
//
//        for (line = 0; line < instrucoes.size(); line++) {
//            visit();
//        }
//
//        System.out.println(allocation);
//
//        printListaDeOcorrencias();
//        return allocation;
//    }
//
//    /**
//     * Imprime todas as ocorrencia de um bloco
//     */
//    protected void printListaDeOcorrencias() {
//        System.out.println("Vars On Bloco:");
//        for (Map.Entry<String, Ocorrence> variavel : varsOcorrence.entrySet()) {
//            System.out.println(variavel.getValue());
//        }
//    }
//            /**
//             *
//             */
////            System.out.println(">>> Alocando para a linha i = " + i);
////            for (Iterator<Ocorrencias> it = varsOnBloco.iterator(); it.hasNext();) {
//            for (Map.Entry<String, Ocorrence> entry : varsOnBloco.entrySet()) {
////                String string = entry.getKey();
//
//                variavel = entry.getValue();
//
//                if (!variavel.ocorreEm(line)) {
//                    continue;
//                }
//
//                var = variavel.var.replace("*", "").replace("&", "");
//
//                baseVar = var.substring(0, 2);
//
//                switch (baseVar) {
//                    case "_T":
//                        base = "t";
//                        break;
//                    case "_V":
//                    case "_P":
//                        base = "s";
//                        break;
//                    case "_A":
//                        base = "a";
//                        break;
//                }
//
//                if (!vars.containsKey(var)) {
//                    try {
//                        reg = getRegister(base, var, line);
//                    } catch (Exception e) { //Não tem registrador disponivel.tera que substituir por outro
////                        reg = getRegBloqueados(base, var, i);
//                        reg = reciclarRegistrador(baseVar, line);
////                        System.out.println("[" + var + ":" + i + ":" + reg + "]" + e.getMessage());
//                    }
////                    System.out.println("Aloquei o reg:" + reg + " para a variavel[" + var + "].");
//                    vars.put(var, reg);
//                } else {
////                    System.out.println("Recuperei o reg:" + reg + " para a variavel[" + var + "].");
//                    reg = vars.get(var);
//                }
////                variavel.setRegister(line, reg);
//
//                allocation.put(line, var, reg);
//            }

    /*Daqui pra cima*/
//    /**
//     * Retorna um registrador disponivel
//     *
//     * @param base base do registrador [a,v,t,s,k]
//     * @param var
//     * @param pos
//     * @return
//     * @throws Exception
//     */
//    public String getRegister(String base, String var, int pos) throws Exception {
//        boolean block = false;
//        switch (base) {
//            case "v":
//            case "a":
//            case "t":
//            case "s":
//            case "k":
//                block = true;
//                break;
//            default:
//                return base;
//        }
//        return base + getFreeRegister(regCtrl.get(base), var, pos, block);
//    }
//
//    public void desbloquearArgs() {
//        String[] partes = regCtrl.get("a").split(",");
//        int init = Integer.parseInt(partes[0]);
//        int end = Integer.parseInt(partes[1]);
//        int fim = init + end;
//        for (int i = init; i < fim; i++) {
//            registers.put(i, false);
//        }
//    }

    /* Metodos protegigos */
//    protected void liberarRegistrador(String regis) throws Exception {
//        int reg = reg2Int(regis);
//        liberarRegistrador(reg);
//    }
//    protected String reciclarRegistrador(String base, int pos) {
////        System.out.println("Reciclar base:[" + base + "][" + pos + "]");
//        String reg = "";
//        String var = "";
//        for (Map.Entry<String, String> entry : vars.entrySet()) {
//            var = entry.getKey();
//            if (!var.substring(0, 2).equals(base)) {
//                continue;
//            }
////            System.out.println("Verificando var[" + var + "]");
//            if (varsOnBloco.get(var).ocorreApos(pos) < 0) {
////                System.out.println("Reciclando [" + var + "]");
//                vars.remove(var);
//                reg = entry.getValue();
////                liberarRegistrador(reg);
//                break;
//            }
//        }
//        return reg;
//    }
//    /**
//     * "int,int" primeiro valor refere-se ao inicio e o segundo a quantidade
//     *
//     * @param
//     * @return
//     */
//    protected String getFreeRegister(String limit, String var, int pos, boolean block) throws Exception {
//        String[] partes = limit.split(",");
//        int init = Integer.parseInt(partes[0]);
//        int end = Integer.parseInt(partes[1]);
//        int fim = init + end;
//        int count = 0;
//        for (int i = init; i < fim; i++) {
//            /*Buscando o primeiro registrador que estiver liberado. quando ocupados sao marcados com true;*/
//            if (registers.get(i)) {
//                count++;
//                continue;
//            }
//            if (block && (isRegistrador("a", i) || code.temVidaApos(pos, var))) {
//                bloqueiarRegistrador(i);
//            } else {
//                liberarRegistrador(i);
//            }
//            return (end == 1) ? "" : "" + count;
//        }
//        throw new Exception("Nenhum registrador disponivel para variavel {" + var + "} na instrucao {" + pos + "}");
//    }
//    protected boolean eTemporario(int i) {
//        String[] partes = regCtrl.get("t").split(",");
//        int init = Integer.parseInt(partes[0]);
//        int end = Integer.parseInt(partes[1]);
//        int fim = init + end;
//        return (i >= init && i <= fim);
//    }
//    protected void bloqueiarRegistrador(int i) {
//        registers.put(i, true);
//    }
//    protected boolean isRegistrador(String clas, int i) {
//        String[] partes = regCtrl.get(clas).split(",");
//        int init = Integer.parseInt(partes[0]);
//        int end = Integer.parseInt(partes[1]);
//        int fim = init + end;
//        return (i >= init && i <= fim);
//    }
//    protected void liberarRegistrador(int i) {
//        if (isRegistrador("a", i)) {
//            return;
//        }
//        registers.put(i, false);
//    }
}
