package Processors;

import API.CodeProcessorInterface;
import common.Block;
import common.Code;
import common.Instruction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;

/**
 *
 * @author Eugenio
 */
public class CodeOtimizadorProcessor implements CodeProcessorInterface {

    protected Code code;
//    protected ArrayList<Filtros> filtros = new ArrayList<Filtros>();
    protected boolean enable = true;

    public CodeOtimizadorProcessor(boolean enabled) {
        enable = enabled;
    }
//
//    public void otimizar() throws Exception {
//        for (Filtros f : filtros) {
//            f.filtrar(code);
//        }
//    }

    @Override
    public void Exec(Code c, LinkedHashMap<String, CodeProcessorInterface> cp) throws Exception {
        if (!enable) {
            return;
        }
        int index = 0;
        Block block = c.Block();
        Instruction last = null;
        LinkedList<Instruction> instructions = block.Instructions();
        ArrayList<Instruction> remove = new ArrayList<>();
        Iterator<Instruction> interator = block.Instructions().iterator();
//        System.out.println("Exec Oti. in block " + block.getName());
        while (interator.hasNext()) {
            Instruction inst = interator.next();
//            System.out.println("Otimizando 3 enderecos:" + inst);
//            System.out.println("CodeOtimizadorProcessor:" + inst.G("type"));
            switch (inst.G("cat")) {
                /*Se for uma expressão aritimetica*/
                case "exp":
                    switch (inst.G("op")) {
                        /*Se for uma multiplicacao ou divisao por 1 */
                        case "*":
                        case "/":
                            if (inst.eq("p1value", "true") && inst.eq("p1", "1")) {
                                inst.S("format", "copy")
                                        .S("type", "copy")
                                        .S("p1", inst.G("p2"))
                                        .S("p1value", "false")
                                        .R("p2,p2value,op,cat");

                            } else if (inst.eq("p2value", "true") && inst.eq("p2", "1")) {
                                inst.S("format", "copy")
                                        .S("type", "copy")
                                        .R("p2,p2value,op,cat");
                            }
                        case "+":
                    }
            }

//            System.out.println("CodeOtimizadorProcessor2:" + inst.G("type"));
            switch (inst.G("type")) {
                case "copy":
                    if (last != null && last.eq("dst", inst.G("p1"))) {
                        last.copy("dst", inst);
                        remove.add(inst);
                    }
//                    if (!inst.eq("copy", "true")) {
//                        if (!inst.isNumber("p1") && ReplaceVarAndRemoveInstruction(instructions, inst, index, block)) {
//                            System.out.println("Remove instruction:" + inst);
//                            remove.add(inst);
//                        }
//                    } else {
//                        System.out.println("COPIA TRUE:" + inst);
//                    }

                // Se o destino da copia não for indexado então pode executar 
                // o replace da ocorrencia e remover a instrucao de copia
                //                    if (!inst.eq("dst_indexed", "true") && inst.eq("p1value", "false")) {
                //                    if (!inst.eq("dst_indexed", "true")) {
                ////                        System.out.println("Otimizacao - replace " + inst.G("p1") + " -> " + inst.G("dst"));
                //                        if (inst.eq("p1value", "true")) {
                //                            block.ReplaceVar(inst.G("dst"), inst.G("p1"));
                //
                //                        } else {
                //                            block.ReplaceVar(inst.G("p1"), inst.G("dst"));
                //                        }
                //                        block.CurrentAddress--;
                //                        instructions.remove();
                //                    }
            }
            last = inst;
            index++;
        }
        if (!remove.isEmpty()) {
            for (Instruction instruction : remove) {
                instructions.remove(instruction);
            }
        }
        c.PosicaoLabel = block.BaseAddress;
        block.Update();
    }

    private boolean ReplaceVarAndRemoveInstruction(LinkedList<Instruction> instructions, Instruction inst, int index, Block block) {
        Instruction instruction;
        if (inst.eq("p1value", "true")) {
            return false;
        }
        int limit = instructions.size();
        String p1 = inst.G("p1"), dst = inst.G("dst"), attrIndex = "";
//        System.out.println("ReplaceVar[" + index + "]:" + ":" + p1 + ":" + dst + "::" + inst + "{");
        index++;

        String[] attrs = {"dst", "p1", "p2"};

        while (index < limit) {
            instruction = instructions.get(index);
//            System.out.println("instruction:" + instruction);

            for (String attr : attrs) {
                if (instruction.contem(attr, dst)) {

                    instruction.Replace(attr, dst, p1);

                    attrIndex = attr + ".index";
                    if (instruction.Has(attrIndex)) {
                        instruction.S(attrIndex, p1);
                    }
                }
            }
            index++;
        }

        block.Data().Replace(dst, p1);

//        System.out.println("ReplaceVarAndRemoveInstruction:" + dst + ":" +);
//        
//        System.out.println("\t\t" + inst);
//        System.out.println("}");
        return true;
    }
}
