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

import API.Api;
import API.Traps;
import remove.FormatadorMips;
import remove.MipsCodeGen;
import IntermediaryCode.TestInstruction;
import API.Instruction;
import API.Utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

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

    protected MipsCodeGen code;
    protected FormatadorMips fm;
    protected ArrayList<Instruction> blocoAtual;
    protected ArrayList<Instruction> bloco;
    protected boolean hex = false;
    private final TestInstruction testIntruction;

    protected Mif mifAtual;

    public MifFactory(MipsCodeGen code) {
        this.code = code;
        testIntruction = new TestInstruction();
//        hex = true;
//        fm = new FormatadorMips();
//        fm.setLabels(code.labels);
    }

    public void usarBloco(String nome) {
        code.use(nome);
    }

    public void criarTodos() throws Exception {
        for (Map.Entry<String, ArrayList<Instruction>> bloco : code.blocks.entrySet()) {
            criar(bloco.getKey());
        }
    }

    public Mif gerarMif(ArrayList<Instruction> bloco, Mif mif, String path) throws IOException {
        blocoAtual = new ArrayList<Instruction>(bloco);
        _gerarMif(blocoAtual, mif, path, 0);
        return mif;
    }

    protected void _gerarMif(ArrayList<Instruction> lista, Mif mif, String path, int count) throws IOException {
        try {
            ArrayList<Instruction> tmp = new ArrayList<>(lista);
            for (Instruction instrucao : tmp) {
                mif.Add(extrair(instrucao));
                lista.remove(instrucao);
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
            _gerarMif(lista, new Mif(mif.size), path, count + 1);
        }
        mif.save(path + "_" + count);
    }

    public MipsCodeGen code() {
        return code;
    }

    public Mif criarCodeStream() throws Exception {
        Mif mif = new Mif();
        mif.setHexMode(hex);
        for (Map.Entry<String, ArrayList<Instruction>> bloco : code.blocks.entrySet()) {
            for (Instruction instrucao : bloco.getValue()) {
                mif.Add(extrair(instrucao));
            }
        }
        System.out.println("mif[]:\n" + mif);
        return mif;
    }

    public Mif criarDataStream() throws Exception {
        System.out.println("Gerando memória de dados............................");
        Mif mif = new Mif(1024);
        mif.setHexMode(hex);
//        Offset of = code.getCodigoTresEnderecos().gOffset;
//        System.out.println("of:" + of);
//        HashMap<String, String> valores = of.getValoresInicializados();
//        Instruction r;
//        String original;
//        String endereco;
//        for (Map.Entry<String, String> entry : valores.entrySet()) {
//            String var = entry.getKey();
//            String valor = entry.getValue();
//            original = Code.reverseLabel(var);
////            r.S("text", num2bin(valor, 32) + ";\t-- " + formatVar(var) + " -> " + original);
//            endereco = var.split(",")[0];
////            completarComZero(mif);
//
//            mif.Add(endereco, criarLinha(Api.num2bin(valor, 32) + ";\t-- " + formatVar(var) + " -> " + original));
//        }
//        System.out.println(mif);
        return mif;
    }

    protected Instruction criarLinha(String linha) {
        Instruction r = new Instruction();
        r.S("text", linha);
        return r;
    }

    public void criar(String nome) throws Exception {
        usarBloco(nome);
        bloco = code.current;
        Mif mif = new Mif();
        mif.setHexMode(hex);
        for (Instruction registro : bloco) {
            mif.Add(extrair(registro));
        }
        System.out.println("mif[" + nome + "]:\n" + mif);
    }

    protected String formatVar(String var) {
        String parts[] = var.split("\\.");
        if (parts.length > 1) {
            return parts[0] + "[" + parts[1] + "]";
        }
        return parts[0];
    }

    protected Instruction extrair(Instruction r) throws Exception {
        Instruction r2 = new Instruction();
        r2.copiar("type", r);
        r2.copiar("codop", r);
        r2.S("comment", ";\t-- " + r.G("mipsInstruction"));
        switch (r.G("type")) {
            case "R":
                _R(r, r2);
                break;
            case "I":
                _I(r, r2);
                break;
            case "J":
                _J(r, r2);
                break;
        }
        return r2;
    }

    protected void _R(Instruction r, Instruction r2) throws Exception {
//        System.out.println("R:" + r);
        r2.S("rs", Api.reg2bin5(r.G("rs")));
        r2.S("rt", Api.reg2bin5(r.get("rt", "zero")));
        r2.S("rd", Api.reg2bin5(r.get("rd", "zero")));
        r2.S("sa", Api.num2bin(r.get("sa", "0"), 5));
//        r2.S("func", num2bin(r.G("func", "0"), 6));
        r2.S("func", r.get("func", "0"));
        testIntruction.testar(r2, "R");
    }

    protected void _I(Instruction r, Instruction r2) throws Exception {
//        System.out.println("I:" + r);
        if (r.isNumber("rs")) {
            r2.S("rs", Api.num2bin(r.G("rs"), 5));
        } else {
            r2.S("rs", Api.reg2bin5(r.G("rs")));
        }
        if (r.isNumber("rt")) {
            r.S("rd", r.G("rt"));
            r2.S("rt", Api.reg2bin5("zero"));
//            System.out.println("entrei no numerico:" + r.G("rt"));
        } else {
            r2.S("rt", Api.reg2bin5(r.G("rt")));
        }
        r2.S("constant", Api.num2bin(r.G("rd"), 16));
        testIntruction.testar(r2, "I");
//        System.out.println("Iadd:" + r2);
    }

    protected void _J(Instruction r, Instruction r2) throws Exception {
//        System.out.println("J:" + r);
        String target;
        String label = r.G("rs");
        if (r.isNumber("rs")) {
            target = Api.num2bin(label, 26);
        } else {
            target = Api.reg2Bin(label, 26);
        }
        r2.S("target", target);
        testIntruction.testar(r2, "J");
    }

    public ArrayList<Mif> criarTraps(boolean save) throws Exception {
        Mif mif = null;
        ArrayList<Mif> mifs = new ArrayList<Mif>();
        String blocoName = "";
        for (Map.Entry<String, ArrayList<Instruction>> bloco : code.blocks.entrySet()) {
            mif = new Mif();
            blocoName = bloco.getKey();
            if (!Traps.isTrap(blocoName)) {
                continue;
            }
            for (Instruction instrucao : bloco.getValue()) {
                mif.Add(extrair(instrucao));
            }
            mifs.add(mif);
            if (save) {
                mif.save(blocoName + ".mif");
            }
        }
        return mifs;
    }

}
