/*
 * 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 target.mips;

import API.Api;
import API.Utils;
import java.util.ArrayList;
import java.util.HashMap;
import template.TPLInterface;
import template.TemplateCtx;
import template.TemplateParser;

/**
 *
 * @author EUGENIO CARVALHO
 */
class TListenerMips extends template.TListener {

    public HashMap<String, TPLInterface> templates;
//    public int AddressGap = 1;

    public TListenerMips(TemplateCtx ctx, TemplateCtx gctx) {
        super(ctx, gctx);
    }

    @Override
    public void exitCall(TemplateParser.CallContext ctx) {
        ArrayList<String> arguments = ExecCall(ctx);

        if (Utils.Empty(this.returnCall)) {
            switch (ctx.ID().getText()) {
                case "OFFSET":
                    this.returnCall = OFFSET(arguments);
                    break;
                case "INSTHEX":
                    this.returnCall = INSTHEX(arguments);
                    break;
                case "INSTDEC":
                    this.returnCall = INSTDEC(arguments);
                    break;
                case "INSTBIN":
                    this.returnCall = INSTBIN(arguments);
                    break;
                case "BIN":
                    this.returnCall = BIN(arguments);
                case "RBIN":
                    this.returnCall = RBIN(arguments);
                    break;
            }
        }
    }

    public String OFFSET(ArrayList<String> args) {
        return args.get(0) + "(" + args.get(1) + ")";
    }

    @Override
    protected String L(ArrayList<String> arguments) {

//        System.out.println("TemplateListener::" + arguments);
        String label = arguments.get(0).trim();

        if (label.equals("")) {
            return label;
        }

        String format,
                base = gctx.G("label." + label).split("\\+")[0],
                laddress = gctx.G(label);

//        System.out.printf("TemplateListener:{%s\n %s\n %s\n}\n", base, label, laddress);
        int value = Integer.parseInt(laddress);

        if (arguments.size() > 1) {
            format = arguments.get(1);
        } else {
            format = "dec";
        }

        int address = value;

        switch (ctx.G("type")) {
            case "J": // (j | jal) Salta para o endereco de destino
//                System.out.println(">>>>" + label + ":" + format + ":" + value + ":" + FormatNum(value, format));
//                address = valueint;
                break;
            case "I": // (bne...) Salta para posicao em relacao ao pc atual

//                System.out.println("I Branch:" + base + "->" + Integer.parseInt(gctx.G(base)) + ":" + label + ":" + value+":"+FormatNum(value, format));
                address = Integer.parseInt(gctx.G(base)) + value;

//                System.out.println("Tranalate>>>>>>>>> I:[" + address + "]" + ctx);
                //                address -= Integer.parseInt(ctx.G("block.position"));
                break;
            default:
                System.out.println(String.format("Label type '%s' not defined", ctx.G("type")));
        }
//        address = address * this.AddressGap;
//        value = value * this.AddressGap;
//        System.out.println("============LaBEL:" + label + ":" + format + ":" + value + ":" + base);
//        System.out.println("============LaBEL:" + value + ":" + address);

        return laddress.trim().equals("")
                ? ""
                : (FormatNum(address, format) + " <" + base + "+0x" + FormatNum(value, format) + ">");
    }

    // Converte um binario para hex
    protected String INSTHEX(ArrayList<String> arguments) {
        try {
            String bin = INSTBIN(arguments);
            if (Utils.Empty(bin)) {
                return "";
            }
            String hex = PAD(Long.toString(Long.parseLong(bin, 2), 16), "0", 8);
            ctx.Set("inst.hex", hex);
            return hex;

        } catch (Exception e) {
            if (ctx.G("inst").equals("bgtz")) {
                System.out.println("hex-Error:" + e.getMessage());
                e.printStackTrace();
            }
            return "error!";
        }
    }

    protected String INSTDEC(ArrayList<String> arguments) {
        try {
            String bin = INSTBIN(arguments);
            if (Utils.Empty(bin)) {
                return "";
            }
            String hex = PAD(Long.toString(Long.parseLong(bin, 2), 10), "0", 10);
            ctx.Set("inst.hex", hex);
            return hex;

        } catch (Exception e) {
            if (ctx.G("inst").equals("bgtz")) {
                System.out.println("hex-Error:" + e.getMessage());
                e.printStackTrace();
            }
            return "error!";
        }
    }

    protected String INSTBIN(ArrayList<String> arguments) {
        String type = ctx.G("type");
        if (type.equals("label")) {
            return "";
        }
        
        //
        switch (type) {
            case "S": // (j | jal) Salta para o endereco de destino
//                System.out.println("instbin:" + ctx);
                break;
            case "J": // (j | jal) Salta para o endereco de destino
                ctx.Set("target", BIN(gctx.G(ctx.G("label")), 26));
                break;
            case "I": // (bne...) Salta para posicao em relacao ao pc atual

                if (ctx.G("offset").equals("") && !ctx.G("label").equals("")) {

                    int target = Integer.parseInt(gctx.G(ctx.G("label")));
                    int position = Integer.parseInt(ctx.G("global.position")) + 1;

//                    System.out.println("INSTBIN:->" + FormatNum(target - position, "hex"));
                    ctx.Set("offset", (target - position) + "");

                }
//                System.out.println("INSTBIN[" + type + ":" + bin + "]:" + ctx);
                break;
            case "R": // (bne...) Salta para posicao em relacao ao pc atual
//                System.out.println("INSTBIN[" + type + "]:" + ctx);
                break;
            default:
        }
        String bin = templates.get("bin." + type).Render(ctx);
        ctx.Set("inst.bin", bin);
//        System.out.println("bin." + type + "::" + bin);
        return bin;
    }

    protected String BIN(ArrayList<String> arguments) {
//        System.out.println("BIN(" + arguments + ")");
        return BIN(arguments.get(0), arguments.get(1));
    }

    protected String BIN(String val, String pad) {
        return BIN(val, Integer.parseInt(pad));
    }

    protected String BIN(String val, int pad) {
        return Api.num2bin(val, pad);
    }

    protected String RBIN(ArrayList<String> arguments) {
//        System.out.println("RBIN(" + arguments + ")");
        String reg = arguments.get(0);
        if (reg.equals("")) {
            reg = "0";
        }
        if (!Utils.isNumber(reg)) {
            reg = Mips.Register(reg);
        }
        return Api.num2bin(reg, Integer.parseInt(arguments.get(1)));
    }

}
