/*
 * 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.Instruction;
import API.Utils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

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

    protected ArrayList<Instruction> instrucoes;
    protected int size = 1024;
    protected StringBuilder sb;
    protected int line = 0;
    protected int wordSize = 32;
    protected boolean showHeader = false;
    protected boolean hex = false;
    protected String ext = "mif";

    public Mif() {
        instrucoes = new ArrayList<>();
        sb = new StringBuilder();
    }

    public void setExt(String ext) {
        this.ext = ext;
    }

    public Mif(int size) {
        instrucoes = new ArrayList<>();
        sb = new StringBuilder();
        this.size = size;
    }

    public void save(String path) throws IOException {
        try (FileWriter fw = new FileWriter(new File(path + "." + ext))) {
            fw.write(getCodeStream());
        }
    }

    /**
     * Adiciona uma inStrução ao mif no proximo endereco diSponivel
     *
     * @param instrucao
     * @throws Exception
     */
    public void Add(Instruction instrucao) throws Exception {
        if (instrucoes.size() == size) {
            throw new Exception("Mif Completo. Não existe espaço para um nova instrução.");
        }
        instrucoes.add(instrucao);
    }

    /**
     * adiciona uma inStrução ao mif em um endereço conhecido
     *
     * @param endereco
     * @param instrucao
     * @throws Exception
     */
    void Add(String endereco, Instruction instrucao) throws Exception {
        if (instrucoes.size() == size) {
            throw new Exception("Mif Completo. Não existe espaço para um nova instrução.");
        }
//        int end = Integer.parseInt(endereco);
//        int dif = end - instrucoes.size();
//        if (dif >= 0) {
//            for (int i = 0; i < dif; i++) {
//                instrucoes.Add(nullLine());
//            }
//        }
        System.out.println("SizeFinal[" + instrucoes.size() + ":" + endereco + "]");
        instrucoes.add(Integer.parseInt(endereco), instrucao);

//        } catch (Exception e) {
//            Pattern p = Pattern.compile("[0-9]+");
//            Matcher m = p.matcher(e.getMessage());
//            while (m.find()) {
//                System.out.println(m.group(0));
//            }
//            System.out.println("Ex:" + e.getMessage());
//
//            instrucoes.Add(, instrucao);
//        }
    }

    protected Instruction nullLine() {
        Instruction r = new Instruction();
        r.S("text", Api.num2bin(0, 32) + ";");
        return r;
    }

    @Override
    public String toString() {
        return getCodeStream();
    }

    public int InstructionCount() {
        return instrucoes.size();
    }

    protected String seHex(int valor, int pad) {
        if (hex) {
            String s = Utils.int2hex(valor);
            return Utils.pad(pad, s);
        } else {
            return "" + valor;
        }
    }

    protected String seHex(String valor) {
//        if (hex) {
//            String S = ivannosysUtils.int2hex(valor);
//            return ivannosysUtils.Pad(wordSize / 4, S);
//        } else {
//            return "" + valor;
//        }
        return (hex) ? Api.bin2Hex(valor, wordSize / 4) : "" + valor;
//        return (hex) ? MifFactory.bin2Hex(valor) : "" + valor;
    }

    protected String seHex(String valor, int pad) {
        String s = seHex(valor);
//        System.out.println("HEX:" + S + "Z" + Pad);
        return Utils.pad(pad, s);
    }

    protected String getCodeStream() {
        sb = new StringBuilder();
        addHeader();
        appendLine("WIDTH=" + wordSize + ";");
        appendLine("DEPTH=" + size + ";");

        appendLine("ADDRESS_RADIX=UNS;");
        appendLine("DATA_RADIX=BIN;");

        appendLine("CONTENT BEGIN");
        String inst;
        String linha;
        int sizeInstruction;

//        System.out.println("Gerar:" + hex);
        int ln;
        int i = 0;
        for (Instruction instrucao : instrucoes) {
//            ln = getLineNumber();
            linha = seHex(i++, 0);
//            System.out.println("LN:" + ln + ">LINHA:" + linha);
            inst = seHex(format(instrucao));

            sizeInstruction = inst.replace(" ", "").length();
            appendLine("\t" + linha + "\t:\t" + inst
                    //                    + " [" + sizeInstruction + "]"
                    + instrucao.G("comment"));
        }

        addFooter();

        return sb.toString();
    }

    protected int getLineNumber() {
        return line++;
    }

    protected void appendLine(String line) {
        sb.append(line + "\n");
    }

    protected String format(Instruction instrucao) {
        String formato = "";
        switch (instrucao.G("type")) {
            case "R":
//                formato = "{codop} {rs} {rt} {rd} {sa} {funct}";
                formato = "{codop}{rs}{rt}{rd}{sa}{func}";
                break;
            case "I":
//                formato = "{codop} {rs} {rt} {constant}";
                formato = "{codop}{rs}{rt}{constant}";
                break;
            case "J":
//                formato = "{codop} {target}";
                formato = "{codop}{target}";
                break;
            default:
                formato = "{text}";
                break;
        }
        return instrucao.format(formato);
    }

    protected void addHeader() {
        if (!showHeader) {
            return;
        }
        appendLine("-- Copyright (C) 1991-2014 Altera Corporation. All rights reserved.");
        appendLine("-- Your use of Altera Corporation's design tools, logic functions");
        appendLine("-- and other software and tools, and its AMPP partner logic");
        appendLine("-- functions, and any output files from any of the foregoing");
        appendLine("-- (including device programming or simulation files), and any");
        appendLine("-- associated documentation or information are expressly subject");

        appendLine("-- to the terms and conditions of the Altera Program License");
        appendLine("-- Subscription Agreement, the Altera Quartus II License Agreement,");
        appendLine("-- the Altera MegaCore Function License Agreement, or other");
        appendLine("-- (including device programming or simulation files), and any");

        appendLine("-- applicable license agreement, including, without limitation,");
        appendLine("-- that your use is for the sole purpose of programming logic ");
        appendLine("-- devices manufactured by Altera and sold by Altera or its ");
        appendLine("-- authorized distributors.  Please refer to the applicable ");
        appendLine("-- agreement for further details.");

        appendLine("-- Quartus II generated Memory Initialization File (.mif)");

    }

    protected void addFooter() {
        String inst = Utils.pad(wordSize, "");

        appendLine("\t[" + seHex(InstructionCount(), 0)
                + ".." + seHex(size - 1, 0) + "]\t:\t" + seHex(inst, 8) + ";");
        appendLine("END;");
    }

    public void setHexMode(boolean hex) {
        this.hex = hex;
    }

}
