/*
 * 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 java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;

/**
 *
 * @author Eugenio
 */
public class Ocorrence implements Comparable {

    public static int WRITE_ACCESS = 0;
    public static int READ_ACCESS = 1;
    public String var;
    public NavigableMap<Integer, String> ocorrencias;
    public HashMap<Integer, Integer[]> vizinhos;

    public Ocorrence(String var) {
        this.var = var;
        vizinhos = new LinkedHashMap<>();
        ocorrencias = new TreeMap<>();
    }

    @Override
    public int compareTo(Object o) {
        return ((Ocorrence) o).ocorrencias.size() - ocorrencias.size();
    }

    /**
     *
     * @param i linha da ocorrencia
     * @param accesso se variavel e acessada como leitura o valor é igual a 1,
     * caso contrario terá valor igual a 0
     *
     */
    public void add(Integer i, Integer accesso) {
        ocorrencias.put(i, "");
        vizinhos.put(i, new Integer[]{-1, -1, accesso});
    }

    public void setRegister(Integer i, String register) {
        int last = last();
        ocorrencias.put(i, register);
        if (last >= 0) {
            vizinhos.get(last)[1] = i;
            vizinhos.get(i)[0] = last;
        }
    }

    public int ocorrenceCount() {
        return ocorrencias.size();
    }

    public int first() {
        if (!ocorrencias.isEmpty()) {
            for (Map.Entry<Integer, String> entry : ocorrencias.entrySet()) {
                return entry.getKey();
            }
        }
        return -1;
    }

    public int last() {
        if (ocorrencias.isEmpty()) {
            return -1;
        }
        return ocorrencias.lastKey();
    }

    public int numeroDeOcorrenciasApartirDe(int i) {
        int count = 0;
        for (Map.Entry<Integer, String> integer : ocorrencias.entrySet()) {
            if (integer.getKey() < i) {
                continue;
            }
            count++;
        }
        return count;
    }

    @Override
    public String toString() {
        return "ID: " + var + " > ocorre em:" + ocorrencias;
    }

    public boolean ocorreNoIntervalo(int init, int end) {
        int pos = -1;
        for (Map.Entry<Integer, String> integer : ocorrencias.entrySet()) {
            pos = integer.getKey();
            if (pos >= init && pos <= end) {
                return true;
            }
        }
        return false;
    }

    protected void printVizinhos() {
        for (Map.Entry<Integer, Integer[]> viz : vizinhos.entrySet()) {
            System.out.println("Ocorrencia var{" + var + "}" + viz.getKey() + ";" + viz.getValue()[0] + ";" + viz.getValue()[1]);
        }
    }

    protected boolean _compararOcorrencia(int linha, int pos) throws Exception {
        String reg = ocorrencias.get(linha);
//        System.out.println("V:" + vizinhos + ":" + linha + ":" + vizinhos.get(linha));
        if (!vizinhos.containsKey(linha)) {
            throw new Exception("Ocorrencia var{" + var + "} linha {" + linha + "} não definida.");
        }
//        printVizinhos();
        int key = vizinhos.get(linha)[pos];

//        System.out.println("Vizinhos de linha:" + linha + ";" + key);
        if (ocorrencias.containsKey(key)) {
            String reg2 = ocorrencias.get(key);
//            System.out.println("Ocorrencia L{" + linha + ":" + pos + "} Reg1:{" + reg + "}:{" + reg2 + "}");
            return reg.equals(reg2);
        }
        return false;
    }

    public boolean ocorreEm(int i) {
        return ocorrencias.containsKey(i);
    }

    public boolean proximaOcorrenciaIgual(int linha) throws Exception {
        return _compararOcorrencia(linha, 1);
    }

    public boolean anteriorOcorrenciaIgual(int linha) throws Exception {
        return _compararOcorrencia(linha, 0);
    }

    public int nextOcorrenceAfter(int line) {
        int next = -1;
        for (Map.Entry<Integer, String> oc : ocorrencias.entrySet()) {
            if (oc.getKey() > line) {
                next = oc.getKey();
                break;
            }
        }
        return next;
    }

    public void removerOcorrenciaLinha(int posicao) {
        if (ocorrencias.containsKey(posicao)) {
            ocorrencias.remove(posicao);
            vizinhos.remove(posicao);
        }
    }

    public Integer ocorreApos(Integer i) {
        int pos = -1;
        for (Map.Entry<Integer, String> entry : ocorrencias.entrySet()) {
            Integer integer = entry.getKey();
//            System.out.println("ocorreApos:" + i + ":" + integer);
            if (integer > i) {
                pos = integer;
                break;
            }
        }
        return pos;
    }

    public boolean eAcessadaLeituraApos(Integer i) {
        Integer pos = ocorreApos(i);
//        System.out.println("eAcessadaLeituraApos[" + var + ":" + pos + "]" + ocorrencias);
        if (pos >= 0) {
            return vizinhos.get(pos)[2] == READ_ACCESS;
        }
        return false;
    }

    public boolean eAcessadaEscritaApos(Integer i) {
        Integer pos = ocorreApos(i);
        if (pos >= 0) {
            return vizinhos.get(pos)[2] == WRITE_ACCESS;
        }
        return false;
    }
}
