package Processors;

/*
 * 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.Utils;
import API.CodeProcessorInterface;
import common.Code;
import common.Instruction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.regex.Pattern;

/**
 *
 * @author EUGENIO CARVALHO
 */
public class BaseBlockProcessor implements CodeProcessorInterface {

//    protected LinkedHashMap<String, ArrayList<ArrayList<Integer>>> basicBlocks = new LinkedHashMap<>();
//    protected String basicBlockName;
//    protected Integer basicBlockCount = 0;
    protected BlockBaseGroup group;
    protected HashMap<String, BlockBaseGroup> groups = new HashMap<>();
    protected Integer nextLeader;
    protected LinkedHashMap<Integer, Integer> leaders;

    //OK
//    protected HashMap<String, BlockBaseOcorrences> blocks = new HashMap<>();
    protected BlockBaseOcorrences block;
    public static Pattern addresspattern = Pattern.compile("\\_[VTGC].*", Pattern.CASE_INSENSITIVE);
    private ArrayList<Integer> leadersList;

    public BaseBlockProcessor() {
    }

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

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

//    public HashMap<String, BlockBaseOcorrences> getBlocks() {
//        return blocks;
//    }
//
//    public void setBlocks(HashMap<String, BlockBaseOcorrences> blocks) {
//        this.blocks = blocks;
//    }
    public BlockBaseOcorrences getBlock() {
        return block;
    }

    public void setBlock(BlockBaseOcorrences block) {
        this.block = block;
    }

    public static Pattern getAddresspattern() {
        return addresspattern;
    }

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

//    protected void IncrementBasicBlockCount() {
//        basicBlockCount++;
//    }
    @Override
    public void Exec(Code c, LinkedHashMap<String, CodeProcessorInterface> cp) throws Exception {
        String name = c.Block().getName();
        BlockBaseGroup g = new BlockBaseGroup(name);
        group = g;
        groups.put(name, g);
//        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);
        leaders = new LinkedHashMap<>();

//        block.LastPosition = c.Block().CurrentAddress - 1;
//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.
        Instruction x;
        LinkedList<Instruction> instructions = c.Block().Instructions();
        int instructionPosition = 0,
                fix,
                lastLeader = -1,
                limit = instructions.size() - 2;

        for (int i = 0; i < limit; i++) {
            x = instructions.get(i);
            fix = 0;
            switch (x.G("type")) {
                case "jump":
                case "call":
                case "branch":
                    fix = 1;
                case "label":
                    if (lastLeader >= 0) {
                        leaders.put(lastLeader, instructionPosition);
                    }

                    if (!instructions.get(i + 1).in("type", new String[]{"jump", "label"})) {

                        lastLeader = instructionPosition + fix;
                        leaders.put(lastLeader, 0);
                    }
            }

            if (!x.eq("type", "label")) {
                instructionPosition++;
            }
        }

        leaders.put(lastLeader, instructionPosition);
        // reseta a posicao da instrução
        instructionPosition = 0;
//        System.out.println("basicBlock:::::::::::::" + leaders);
        leadersList = new ArrayList<Integer>();

        for (Map.Entry<Integer, Integer> entry : leaders.entrySet()) {
            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 (Instruction inst : instructions) {
            switch (inst.G("type")) {
                case "label":
                    break;
                default:
//                    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(inst, "dst,p1,p2");

                    instructionPosition++;
            }
        }
        group.Close();
//        // Atualiza todos os encerramentos de intervalo para a ultima ocorrencia da variavel;
//        block.CloseAllIntervals();

        System.out.println("block:" + group);
    }

    protected void RegBasicBlock() {

//        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) {
//        System.out.println("Is Address:" + alias + "::" + addresspattern.matcher(alias).matches());
        return !Utils.Empty(alias) && addresspattern.matcher(alias).matches();
    }
}
