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

import API.Utils;
import ast.Node;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author Eugenio
 */
public class DataFrame {

    protected String name;
    protected LinkedHashMap<String, Node> values = new LinkedHashMap<>();
    // address guarda o endereço de cada variavel
    protected LinkedHashMap<String, Integer> address = new LinkedHashMap<>();
//    protected LinkedList<Node> values = new LinkedList<>();//Map< String, String> copy = new TreeMap<>();
//    protected HashMap<String, Integer> size = new LinkedHashMap<>();
//    protected HashMap<String, Integer> type = new LinkedHashMap<>();
//    protected HashMap<String, Integer> _offset_ = new LinkedHashMap<>();
    protected boolean initialized = false;
    protected Pattern normalId = Pattern.compile("(?<id>[\\w_]+)(\\[(?<index>\\w+)\\])?");

//    protected static AbstractSyntaxTree ast;
    public static int TYPE_ARGUMENT = 0;
    public static int TYPE_VAR = 1;
    public static int TYPE_RETURN = 2;
    public static int TYPE_STACK = 3;

    public int offsetctrl = 0;
    protected DataFrame global;
    // Guarda o tamanho do frame
    // É atualizado sempre que o mapeamento dos endereços sofre alteração.

    private int frameSize;

    public DataFrame(String name, DataFrame data) {
        this.name = name;
        this.global = data;
    }

    public void SetInitialized(boolean init) {
        initialized = init;
    }

    public void Set(String vname, Node copy) throws Exception {
        values.put(
                vname,
                copy
        );

        address.put(vname, frameSize);
        frameSize += copy.getInt("size");
    }

    public DataFrame Add(String alias, Node var, int elements) throws Exception {
        //        System.out.println("Add global to frame:" + var);
//        int vSize = 1;
//        int size = var.getInt("size"), vSize = 1;
//        boolean isArray = var.eq("array", "true");
//        if (isArray) {
//            vSize = Tipos.Size(var.G("type"));
//        }
        ArrayList<String> defaults = var.getList("default.values");

        Integer defSize = defaults.size();
        String vname;
        Node copy;
        for (int i = 0; i < elements; i++) {
            vname = alias + "." + i;
            copy = var.copy().S("default.value", (i < defSize ? defaults.get(i) : "0"));
            Set(vname, copy);
//            values.put(
//                    vname,
//                    copy
//            );
//
//            address.put(vname, frameSize);
//            frameSize += copy.getInt("size");
        }

        return this;
    }

    public LinkedHashMap<String, Node> values() {
        return values;
    }

    public boolean has(String id) {
        return values.containsKey(id);
//        for (Map.Entry<String, Node> x : values.entrySet()) {
//            if (x.getKey().equals(id)) {
//                return true;
//            }
//        }
//        return false;
    }

//    public static void setAbstractSyntaxTree(AbstractSyntaxTree ast) {
//        DataFrame.ast = ast;
//    }
    public int Size() {
        return frameSize;
//        int val = 0;
//        for (Map.Entry<String, Node> v : values.entrySet()) {
//            try {
//                val += v.getValue().getInt("size");
//            } catch (Exception ex) {
//                Logger.getLogger(DataFrame.class.getName()).log(Level.SEVERE, null, ex);
//            }
//        }
//        return val;
    }

    @Override
    public String toString() {
        StringBuilder s = new StringBuilder("Data Frame <" + name + ">:" + Size() + "\n");
        Node value;
        for (Map.Entry<String, Node> x : values.entrySet()) {
            value = x.getValue();

//            System.out.println("Value:" + value);
            s.append(x.getKey())
                    .append(": ")
                    .append(value.G("name"))
                    .append("\tsize:")
                    .append(value.G("size"))
                    .append("\tvalue:")
                    .append(value.G("default.value"))
                    .append("\n");
//            break;
        }

        return s.toString();
    }

    /**
     * Formata oS haSh para a impreSSão no metoglobalo toString
     *
     * @param hash
     * @return
     */
    protected String hashToString(HashMap<String, Node> hash) {
        String r = "";
        for (Map.Entry<String, Node> entry : hash.entrySet()) {
            String name = entry.getKey();
            Node value = entry.getValue();
//            int type = this.type.G(name);
            r += "{" + name + ":" + value + "},";
        }
        return r;
    }

    public int Offset(String id) throws Exception {
        int offset = 0;
        if (id.contains("_G") && global != null) {
            return global.Offset(id);
        }
        id = normalize(id);
        try {
            offset = address.get(id);
        } catch (Exception e) {
            System.out.println("address(" + id + ")[" + frameSize + "].:"
                    + address
                    + values
            );
            throw new Exception("Invalid offset!");
        }
        return offset;

//        int offset = 0;
//        try {
////            String cp = id;
//            id = normalize(id);
//
//            for (Map.Entry<String, Node> x : values.entrySet()) {
////                System.out.println("Offset:(" + id + ")" + x.getKey());
//
//                if (!x.getKey().equals(id)) {
//                    offset += x.getValue().getInt("size");
//                    continue;
//                }
//                break;
//            }
//        } catch (Exception ex) {
//            Logger.getLogger(DataFrame.class.getName()).log(Level.SEVERE, null, ex);
//        }
//        return offset;
    }

    protected String normalize(String id) {
        String shift = "0", index;
        Matcher m = normalId.matcher(id);
        if (m.find()) {
            id = m.group("id");
            index = m.group("index");
            if (Utils.isNumber(index)) {
                shift = index;
            }
        }

        if (!id.contains(".")) {
            id += "." + shift;
        }
//        System.out.println("Normalize:" + id + "::" + or);
        return id;
    }

    public DataFrame copy(DataFrame data) {
        String k;
        for (Map.Entry<String, Node> x : data.values().entrySet()) {
            System.out.println("Copy:" + x.getKey() + "-" + x.getValue());
            k = x.getKey();
            values.put(k, x.getValue());
            address.put(k, data.address.get(x));
        }
        frameSize = data.frameSize;
        return this;
    }

    public void Replace(String dst, String p1) {
        String rest;
//        System.out.println("Replace >> " + this.values);

        LinkedHashMap<String, Node> add = new LinkedHashMap<>();

        for (Map.Entry<String, Node> x : this.values.entrySet()) {

            rest = x.getKey().replace(dst, "");

            if (rest.equals("") || rest.substring(0, 1).equals(".")) {
                add.put(p1 + rest, x.getValue());
            } else {
                add.put(x.getKey(), x.getValue());
            }
        }
        this.values = add;
//        System.out.println("Replace << " + this.values);
    }

    void Remove(String dst) throws Exception {
        boolean init = false;
        Iterator<String> it = values.keySet().iterator();
        String next;
        int diff = 0;
        while (it.hasNext()) {
            next = it.next();
            if (next.contains(dst)) {
                if (!init) {
                    init = true;
                }
                diff += values().get(next).getInt("size");
                it.remove();
                continue;
            }

            if (init) {
                address.put(next, address.get(next) - diff);
            }
        }

        frameSize -= diff;

        System.out.println("Remove:" + dst + "\n" + values);
    }

}

//    public DataFrame Add(String alias, Node var) throws Exception {
////        System.out.println("Add global to frame:" + var);
//        int size = var.getInt("size"), vSize = 1;
////        boolean isArray = var.eq("array", "true");
//
////        if (isArray) {
////            vSize = Tipos.Size(var.G("type"));
////        }
//        for (int i = 0; i < size; i++) {
//            values.put(
//                    alias + "." + i,
//                    var.copy().S("size", vSize)
//            );
//        }
//
//        return this;
//    }
