package middlewares;

/*
 * 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.
 */
import API.MiddlewareInterface;
import API.Utils;
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;
import java.util.regex.Pattern;

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

    protected static Integer nextLeader;
    protected static BlockBaseGroup group;
    protected static HashMap<String, BlockBaseGroup> groups = new HashMap<>();
    protected static LinkedHashMap<Integer, Integer> leaders;
    //OK
    protected static Block block;
    protected static ArrayList<Integer> leadersList;

    public static Pattern addresspattern = Pattern.compile("\\_[VTGC].*", Pattern.CASE_INSENSITIVE);
    protected Integer instructionPosition;
    protected Integer lastLeader;
    protected LinkedList<Instruction> instructions;
    protected Instruction instruction;
    protected String[] ignore = "label,alloc".split(","),
            nextIsJump = "jump,label".split(",");

    public BasicBlockMiddleware() {

    }

    protected void SetLeader(Integer position, Integer fix) {

        // Se existe um leader anterior, atualiza a ultima instrução do bloco basico
        if (lastLeader >= 0) {

            leaders.put(lastLeader, (fix == 0 ? (instructionPosition - 1) : instructionPosition));
        }
        // Marca o proximo leader
        // Se a proxima instrução  não foir um jump ou label
        if (instructions.get(position + 1).in("type", nextIsJump)) {
            return;
        }
        // Seta o novo leader
        lastLeader = instructionPosition + fix;
        leaders.put(lastLeader, 0);

    }

    @Override
    public void Exec(Code c, LinkedHashMap<String, MiddlewareInterface> cp) throws Exception {

        String name = c.Block().getName();

        BlockBaseGroup g = new BlockBaseGroup(name);
        block = c.Block();
        group = g;
        groups.put(name, g);
        instructionPosition = 0;
        lastLeader = -1;
        leaders = new LinkedHashMap<>();
        //Step 1. Identify the leaders in the code. Leaders are instructions which come under any of the following 3 categories :
        //The first instruction is a leader.
        //The target of a conditional or an unconditional goto/jump instruction is a leader.
        //The instruction that immediately follows a conditional or an unconditional goto/jump instruction is a leader.
        instructions = block.Instructions();

        // Remove da quantidade o label inicial e final
        Integer limit = instructions.size() - 2;

        for (int i = 0; i < limit; i++) {
            instruction = instructions.get(i);
            // Correcao quando for um salto
            switch (instruction.Get("type")) {
                // Ignora a instrução de alocacao de memoria
                case "alloc":
                    continue;
                case "label":
                    SetLeader(i, 0);
                    break;
                // Cria um novo bloco basico
                case "jump":
                case "call":
                case "branch":
                    SetLeader(i, 1);
            }
            // Se a instrução não faz parte do grupo de instrucoes 
            // de controle incrementa a posicao do contador
            if (!instruction.in("type", ignore)) {
                instructionPosition++;
            }
        }
        leaders.put(lastLeader, instructionPosition);

        VerifyOcorrences();
        System.out.println(group);
    }

    protected void RegBasicBlock() {
//        System.out.println("leaders:" + leaders);
//        System.out.println("RegBasicBlock:(" + nextLeader + ")(" + ((leaders.Get(nextLeader) - nextLeader) + 1) + ")");

        group.RegisterBlock(nextLeader, (leaders.get(nextLeader) - nextLeader) + 1);

        if (!leadersList.isEmpty()) {
            nextLeader = leadersList.remove(0);
        }
    }

    public static boolean IsAddress(String alias) {
        return !Utils.Empty(alias) && addresspattern.matcher(alias).matches();
    }

    public BlockBaseGroup getBasicBlockGroup(String id) {
        return groups.get(id);
    }

    public HashMap<String, BlockBaseGroup> getGroups() {
        return groups;
    }

    public static Pattern getAddresspattern() {
        return addresspattern;
    }

    public static void setAddresspattern(Pattern addresspattern) {
        BasicBlockMiddleware.addresspattern = addresspattern;
    }

    protected void VerifyOcorrences() throws Exception {

        // reseta a posicao da instrução
        instructionPosition = 0;
        leadersList = new ArrayList<>();

        leaders.entrySet().forEach((entry) -> {
            leadersList.add(entry.getKey());
        });

        System.out.println("leadersList:" + leadersList);

        nextLeader = leadersList.remove(0);

        // Registra primeiro bloco basico
        RegBasicBlock();
        // Atribui o segundo leder como 
//        block.LastPosition = nextLeader - 1;

        for (Iterator<Instruction> it = instructions.iterator(); it.hasNext();) {
            instruction = it.next();
            if (!instruction.in("type", ignore)) {

//                System.out.println("Ins:(" + instructionPosition + ")(" + nextLeader + ")");
                // Registra 
                if (instructionPosition == nextLeader) {
//                        block.CloseAllIntervals();
//                        nextLeader = leadersList.remove(0);
//                        block.LastPosition = nextLeader - 1;
                    RegBasicBlock();
                }
                // Registra os acessos de leitura e escrita dos enderecos da instrucao
                group.ParseInstruction(instruction, "dst,p1,p2");

                instructionPosition++;
            }
        }
        // Finaliza o grupo de blocos
        group.Close();

//        // Atualiza todos os encerramentos de intervalo para a ultima ocorrencia da variavel;
//        block.CloseAllIntervals();
//        System.out.println("block:" + groups);
    }

}
//        block = new BlockBaseOcorrences(name);
//        ArrayList<ArrayList<Integer>> bbs = new ArrayList<>();
//        ArrayList<Integer> bb = null;

//        basicBlockName = name;
//        basicBlockCount = 0;
//            protected ArrayList<ArrayList<Integer>> basicBlock = new ArrayList<>();
//        basicBlocks.put(name, bbs);
//        blocks.put(name, block);
