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

import API.Instruction;
import API.Utils;
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 BlockOcorrences {

    protected LinkedHashMap<String, LinkedList<Integer>> ocorrences = new LinkedHashMap<>();
    protected LinkedHashMap<String, LinkedList<Interval>> intervals = new LinkedHashMap<>();
    protected int Position = 0;
    protected int LastPosition = 0;
    protected Interval interval;
    protected Pattern addresspattern;
    protected String name;

    public BlockOcorrences(String name) {
        this.name = name;
    }

    protected void CloseAllIntervals() {
//        System.out.println("CloseAllIntervals:");
        for (Map.Entry<String, LinkedList<Interval>> x : intervals.entrySet()) {
            interval = x.getValue().peekLast();
//            System.out.println("Check interval:" + x.getKey() + Position + ":" + interval);

            if (interval.Alive(Position)) {
//                System.out.println("Interval:" + x.getKey() + ":open:" + ocorrences.G(x.getKey()).peekLast());
                interval.Close(ocorrences.get(x.getKey()).peekLast());
            }
        }

    }

    protected void Register(Instruction x, String attr) {
        String alias = x.G(attr);
        if (!OcorrenceFinderProcessor.IsAddress(alias)) {
            return;
        }
        alias = ToAliase(x, attr);

//        System.out.println("Register x:" + x);
        ArrayList<String> aliases = new ArrayList<>();

        aliases.add(alias);
        String index = attr + ".indice";
        if (x.Has(index) && !x.isNumber(index)) {
            aliases.add(x.G(index));
        }
        for (String a : aliases) {
            if (!ocorrences.containsKey(a)) {
                ocorrences.put(a, new LinkedList<Integer>());
            }

            ocorrences.get(a).add(Position);
            if (!Alive(a)) {
                OpenInterval(a);
            }
        }

    }

    protected boolean Alive(String alias) {
        if (intervals.containsKey(alias)) {
            return Position <= intervals.get(alias).peekLast().End;
        }
        return false;
    }

    protected void OpenInterval(String alias) {
        if (!intervals.containsKey(alias)) {
            intervals.put(alias, new LinkedList<Interval>());
        }
//        System.out.println("Open interval:(" + alias + "):" + Position + ":" + LastPosition);
        intervals.get(alias).add(new Interval(Position, LastPosition));
    }

    @Override
    public String toString() {
        StringBuilder buffer = new StringBuilder("Block: " + name + "\n");
        buffer.append("Inst/Var   ");
        for (int i = 0; i <= LastPosition; i++) {
            buffer.append(Utils.Pad(i + "", " ", "-", 3));
        }
        buffer.append("\n");
        String append, var, reg;
        Interval inter;

        for (Map.Entry<String, LinkedList<Integer>> x : ocorrences.entrySet()) {
            var = x.getKey();
            buffer.append(Utils.Pad(var, " ", "-", 11));

            for (int i = 0; i <= LastPosition; i++) {
//                System.out.println("OCOR:" +);
                reg = "";
                append = "---";
                if (x.getValue().contains(i)) {
//                    append = Get(var, i).getRegister() + "##"; // Ocorrencia da variavel
                    append = "###"; // Ocorrencia da variavel
                } else if (Inside(var, i)) {
                    append = "+++"; // dentro do intervalo
                }

                inter = Get(x.getKey(), i);
                if (inter != null) {
                    reg = inter.getRegister();
                }

                buffer.append(reg + append.substring(0, append.length() - reg.length()));
            }

            buffer.append("\n");
        }

        return buffer.toString();
    }

    public boolean Inside(String var, int i) {
        return Get(var, i) != null;
    }

    public LinkedList<Interval> Get(String var) {
        if (!intervals.containsKey(var)) {
            return null;
        }
        return intervals.get(var);
    }

    public Interval Last(String addr) {
        LinkedList<Interval> itvls = Get(addr);
        if (itvls != null) {
            return itvls.peekLast();
        }
        return null;
    }

    public Interval Get(String var, int i) {
        LinkedList<Interval> intervls = Get(var);
        if (intervls != null) {
            for (Interval inter : intervls) {
                if (inter.Inside(i)) {
                    return inter;
                }
            }
        }
        return null;
    }

    // Busca por um intervalo expirado ou que esteja disponivel para ser subdividido
    // Retorna a primeira parte intervalo que foi dividido
    public Interval Spill(int position) {
//        System.out.println("Spill:[" + position + "]{");
        HashMap<String, Interval> is = new HashMap<>();
        Interval inter = null;
        String var = "";

        for (Map.Entry<String, LinkedList<Interval>> I : intervals.entrySet()) {
            var = I.getKey();
            inter = Get(var, position);
            System.out.println("Interval:" + inter);
            // Tem intervalo nessa posicao agora deve verificar se ela não ocorre nessa instrucao;
            if (inter != null && !ocorrences.get(var).contains(position)) {
                is.put(var, inter);
            }
        }
        inter = null;
//        System.out.println("Spill disp:" + is.size());
        // is Contem todos os intervalos que podem ser divididos;
        // o intervalo escolhido é o que possui a ocorrencia mais longe
        int pos = 0, pos1, interpos;

        for (Map.Entry<String, Interval> x : is.entrySet()) {
            pos1 = FirstAfter(ocorrences.get(x.getKey()), position);
            if (pos1 > pos) {
                pos = pos1;
                var = x.getKey();
                inter = x.getValue();
            }
        }
//        System.out.println("Spill inter:" + var + "|" + inter);
        // Se encontrou um intervalo para ser dividido executa a divisao
        if (inter != null) {
            LinkedList<Interval> lintervals = intervals.get(var);
            interpos = lintervals.indexOf(inter);
            lintervals.add(interpos + 1, new Interval(pos, inter.getEnd()));
            inter.Close(position);
        }
//        System.out.println("End spill}");
        return inter;
    }

    protected Integer FirstAfter(LinkedList<Integer> get, int position) {
        for (Integer integer : get) {
            if (integer > position) {
                return integer;
            }
        }
        return 0;
    }

    protected static Pattern operators = Pattern.compile("(\\&|\\*)");

    public String ToAliase(Instruction x, String attr) {
        String alias = x.G(attr);
        String op = x.G("op");
//        System.out.println("ToAliase:" + x);
        boolean isdst = attr.equals("dst");
        if (isdst && x.Has("dst.pointer")) {
//            System.out.println("Has Pointer:");
            alias = "*" + alias;
        } else if (!isdst && operators.matcher(op).matches() && x.eq("type", "pointer_assignment")) {
            alias = op + alias;
        }
//        System.out.println("Aliase:" + alias + ":" + attr + ":" + x);
        return alias;
    }

}
