/*
 * 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.LinkedHashMap;
import java.util.LinkedList;

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

    protected BasicBlockMiddleware basicBlocks;

    private BlockBaseGroup group;

    public LoadStoreMiddleware() {
    }

    @Override
    public void Exec(Code c, LinkedHashMap<String, MiddlewareInterface> cp) throws Exception {
        this.basicBlocks = (BasicBlockMiddleware) cp.get("ir.basic.blocks");

        BlockBaseOcorrences g;
        Integer leader, limit, lposition;
        Block block = c.Block();
        String blockname = block.getName();
        LinkedList<Instruction> instructions = block.Instructions();
        ArrayList<Instruction> remove = new ArrayList<>();
        Instruction IL, IS;
        // Group contem a lista de blocos basicos do bloco atual
        group = basicBlocks.getGroups().get(blockname);
        HashMap<String, Boolean> loaded, stored;
        String varL, varS;
        Integer fix = 0;
        group.Init();

        System.out.println("Inicianado load store processor....");

        while (true) {
            g = group.getCurrent();

            if (g == null) {
                break;
            }
            System.out.println("------------------------------>:");

            // Corrige o endereco inicial do leader caso alguma instrucao tenha sido removida
            leader = g.getLeader() - fix;

            limit = leader + g.Position;

            System.out.println("LoadStoreProcessor:[" + leader + "][" + limit + "]");

            loaded = new HashMap<>();
            stored = new HashMap<>();

            for (int i = leader, j = limit - 1; i < limit; i++, j--) {
                IL = instructions.get(i);
                IS = instructions.get(j);

                varL = IL.Get("p1");
                varS = IS.Get("p1");
                // Verifica a possibilidade de remover a instrucao atual considerando:
                // * A instrucao é um load
                // * O endereco carregado é uma variavel da pilha
                // Variaveis globais sempre são lidas

                if (IL.eq("type", "load") && varL.contains("_V")) {
//                    System.out.println("Looad:" + instruction);
                    if (loaded.containsKey(varL)) {
                        remove.add(IL);
                        fix++;
                    } else {
                        loaded.put(varL, Boolean.TRUE);
                    }
                }

                // Verifica a possibilidade de remover a instrucao atual considerando:
                // * A instrucao é um store
                // * O endereco carregado é uma variavel da pilha
                // Variaveis globais sempre são gravadas
//                if (IS.eq("type", "store") && varS.contains("_V")) {
//                    if (varS.equals("_V8")) {
//                        System.out.println("Looad:" + IS + " > " + stored.containsKey(varS));
//                    }
//                    if (stored.containsKey(varS)) {
//                        remove.add(IS);
//                        fix++;
//                    } else {
//                        stored.put(varS, Boolean.TRUE);
//                    }
//                }
            }
            // Corrige o endereco final do bloco caso alguma instrucao tenha sido removida
            g.setLeader(leader);
            g.Position -= fix;

            System.out.println("------------------------------<:" + g);

            if (!group.NextBlock()) {
                break;
            }
        }

        for (Instruction i : remove) {
            instructions.remove(i);
        }
    }

}
