/*
 * 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 middlewares;

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

/**
 *
 * @author EUGENIO CARVALHO
 */
public class RemoveUnusedLabelsMiddleware implements MiddlewareInterface {

    protected HashMap<String, HashMap<String, ArrayList<Instruction>>> labelMap = new HashMap<>();
    protected String currentBlock;
    private Block block;

    public RemoveUnusedLabelsMiddleware() {

    }

    @Override
    public void Exec(Code c, LinkedHashMap<String, MiddlewareInterface> cp) throws Exception {
        String label;
        Instruction instruction = null;
        block = c.Block();
        currentBlock = block.getName();
        HashMap<String, Integer> referenceCount = c.labelsReferenceCount;
        LinkedList<Instruction> instructions = block.Instructions();
        ArrayList<Instruction> remover = new ArrayList();
        Iterator<Instruction> interator = instructions.iterator();
        String[] ignore = new String[]{"block", "user"};
        Instruction branch = null, last = null;
        int position = 0;

//        System.out.println("block:" + block.getName() + "-------------------------------");
//        System.out.println("\n\n####referenceCount:" + referenceCount + "\n\n");
        // Salta o primeiro label
        if (interator.hasNext()) {
            last = interator.next();
        }
        // Remove todos os labels não referenciados
        while (interator.hasNext()) {
            position++;
            last = instruction;
            instruction = interator.next();

            //            if (instruction.eq("type", "branch")) {
            //                branch = instruction;
            ////                position = 0;
            //            }
            //
            //            // Quando salta um jump altera o enredeco para o endereco do jump e inverte o teste
            //            if (branch != null && last.eq("type", "jump")) {
            //                if (instruction.eq("label", branch.Get("label"))) {
            //                    //                position == 3 &&
            //
            //                    branch.Set("op", Utils.ComplementOperation(branch.Get("op")))
            //                            .Set("label", last.Get("label"));
            //                    branch = null;
            //
            //                    remover.add(last);
            //                    remover.add(instruction);
            //
            //                    continue;
            //                }
            //            }
            if (!instruction.eq("type", "label") || instruction.in("label_type", ignore)) {
                continue;
                // Unifica labels sequenciais e atualiza as chamadas dos outros labels
            }

            // A instrucão é um label
            label = instruction.Get("label");

//            System.out.println("Last:(label:" + label + ")" + last);

            if (last != null && last.eq("label", instruction.Get("label"))) {
                remover.add(last);
            }

            // Se o label não existe ou não foi referenciado remove
            if (!referenceCount.containsKey(label) || referenceCount.get(label) == 0) {
//                System.out.println("Removendo o label " + label);
                interator.remove();
            }
        }

        // Remove as instruções sinalizadas
        while (!remover.isEmpty()) {
            block.Remove(remover.remove(0));
        }

        // Remove labels sequenciais
        for (Instruction i : block.Instructions()) {
            if (last != null) {
                if (i.eq("type", "label") && last.eq("type", "label")) {
                    UpdateLabel(c, last.Get("label"), i.Get("label"));
                    last.copy("label", i);
                    remover.add(last);
                }
            }
            last = i;
        }

        while (!remover.isEmpty()) {
            block.Remove(remover.remove(0));
        }
    }

    protected void UpdateLabel(Code c, String old, String newl) {
        HashMap<String, ArrayList<Instruction>> lab;
//        ArrayList<Instruction> labelInstructions;
        String label;
        // Cria o mapa de labels para o bloco atual caso não exista
        if (!labelMap.containsKey(currentBlock)) {
            lab = new HashMap<>();
            labelMap.put(currentBlock, lab);
            // Popula a lista de cada de instrucao de cada label
            for (Instruction instruction : block.Instructions()) {
                if (instruction.Has("label") || instruction.eq("type", "label")) {
                    label = instruction.Get("label");
                    if (!lab.containsKey(label)) {
                        lab.put(label, new ArrayList<>());
                    }
                    lab.get(label).add(instruction);
                }
            }
        } else {
            lab = labelMap.get(currentBlock);
        }
//        System.out.println("lab:" + lab);
//        System.out.println("update label:" + old + "----" + newl);

        if (lab.containsKey(old)) {
//            c.RegisterLabelAddress(old);
            lab.get(old).forEach((instruction) -> {
                instruction.Set("label", newl);
            });
        }

//labelInstructions
    }
}
