package dendrarium.trees;

import dendrarium.utils.Pair;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Wezel -- odpowiednik elementu node w forest.xml. Terminal lub nieterminal.
 *
 * Zarzadza swoimi powiazaniami rodzinnymi (lista mozliwych zestawow
 * dzieci), metadanymi z forest.xml (etykieta, id, atrybuty etc.)
 * oraz dodatkowymi oznaczeniami pomocnymi przy obrobce lasu.
 *
 * UWAGA. Jezeli w komentarzu zamiast ,,wezel'' jest napisane ,,terminal''
 * to znaczy, ze w danym kodzie zaklada sie, ze dany wezel jest
 * terminalem.
 *
 * @author Piotr Achinger <piotr.achinger at gmail.com>
 */
public abstract class Node implements Serializable {

    /* --- METADANE --- */
    protected int id;

    /**
     * Atrybuty. Odpowiadaja <f type="key">value</f>
     */
    protected List<Pair<String, String>> attributes = new ArrayList();

    protected Map<String, String> attributesMap = new HashMap();

    /**
     * Atrybut from elementu node w XML
     */
    protected int from;

    /**
     * Atrybut to elementu node w XML
     */
    protected int to;

    /**
     * Atrybut subtrees elementu node w XML
     */
    protected long subtrees;

    /**
     * Konstruktor przyjmujacy tylko obowiazkowe metadane
     */
    public Node(int id, int from, int to,
            long subtrees) {
        this.id = id;
        this.from = from;
        this.to = to;
        this.subtrees = subtrees;
    }

    public boolean addAttribute(String key, String value) {
        if (attributesMap.containsKey(key)) {
            return false;
        }

        attributesMap.put(key, value);
        attributes.add(new Pair<String, String>(key, value));
        return true;
    }

    /* === ROZNE METODY NIE ZMIENIAJACE STANU WEZLA === */
    /**
     * Porownanie potrzebne przy MUZG-owaniu
     *
     * @author Karolina Soltys
     */
    public abstract boolean isEquivalentTo(Node otherNode);

    /**
     * Czy jest terminalem lub dotychczas nierozstrzygniętym wierzchołkiem.
     */
    public abstract boolean isLeaf();

    /**
     * Czy wezel nie ma dzieci -- jest terminalem lub
     * nieterminalem wiszącym zaraz nad swoim zakresem zdania.
     *
     * Istotne w semantyce rysowania
     *
     * @author Tomasz Badowski
     */
    public boolean isDrawLeaf() {
        return isLeaf();
    }

    public abstract String getSymbol();

    public abstract String getLabel();

    /**
     * Lista wartosci atrybutow oddzielonych przecinkami
     */
    public String getAttrString() {
        if (attributes.isEmpty()) {
            return "";
        }

        StringBuilder sb = new StringBuilder();

        for (Pair<String, String> p : attributes) {
            sb.append(p.getF2() + ", ");
        }

        String s = sb.toString();
        return s.substring(0, s.length() - 2);
    }

    /**
     * Czy wezel jest terminalem zgodnym z analiza morfosyntaktyczna NKJP
     * lub posiada wybor zestawu dzieci zgodny z ta analiza.
     *
     * Formalnie:
     *    f(node) = true jesli node jest terminalem
     *    f(node) = true jesli f(children) = true dla PEWNEGO wariantu children
     *                         danego wezla
     *    f(children) = true jesli f(node) = true dla KAZDEGO wezla danego
     *                             wariantu
     */
    public abstract boolean hasNKJPCompliantVariant();

    /* === G & S === */
    public int getFrom() {
        return from;
    }

    public long getSubtrees() {
        return subtrees;
    }

    public int getId() {
        return id;
    }

    public int getTo() {
        return to;
    }

    public List<Pair<String, String>> getAttributes() {
        return attributes;
    }

    public Map<String, String> getAttributesMap() {
        return attributesMap;
    }

    public String getAttribute(String key) {
        return attributesMap.get(key);
    }

    @Override
    public String toString() {
        return "Node[id=" + id + "]";
    }
}
