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

import API.Utils;
import API.CodeProcessorInterface;
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 RemoveUnusedLabelsProcessor implements CodeProcessorInterface {

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

    public RemoveUnusedLabelsProcessor() {

    }

    @Override
    public void Exec(Code c, LinkedHashMap<String, CodeProcessorInterface> cp) throws Exception {
        String label;
        Instruction instruction = null;
        block = c.Block();
        currentBlock = block.getName();

        HashMap<String, Integer> rc = 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() + "-------------------------------");

        // Remove todos os labels não referenciados
        while (interator.hasNext()) {
            position++;
            last = instruction;
            instruction = interator.next();

//            System.out.println("instruction:" + instruction);
            if (instruction.eq("type", "branch")) {
                branch = instruction;
                position = 0;
            }

            if (position == 3 && branch != null && last.eq("type", "jump")) {

//                System.out.println(":" + branch + last);
                branch.S("op", Utils.ComplementOperation(branch.G("op")))
                        .S("label", last.G("label"));
                branch = null;
//                instructions.remove(last);
//                interator.remove();

//                interator.remove(last);
                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
            }

            label = instruction.G("label");
            if (!rc.containsKey(label) || rc.get(label) == 0) {
                interator.remove();
            }
        }

//        System.out.println("Remover:" + remover);
        Instruction r;
        while (!remover.isEmpty()) {
            r = remover.remove(0);
            instructions.remove(r);
        }

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

        while (!remover.isEmpty()) {
            r = remover.remove(0);
            instructions.remove(r);
        }
    }

    protected void UpdateLabel(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<String, ArrayList<Instruction>>();
            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.G("label");
                    if (!lab.containsKey(label)) {
                        lab.put(label, new ArrayList<Instruction>());
                    }
                    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)) {

            for (Instruction instruction : lab.get(old)) {
                instruction.S("label", newl);
            }
        }

//labelInstructions
    }
}
