/* BdfServer_API - Copyright (c) 2014-2023 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package fr.exemole.bdfserver.tools.subsettree;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.groups.GroupDef;
import fr.exemole.bdfserver.api.subsettree.GroupNode;
import fr.exemole.bdfserver.api.subsettree.SubsetNode;
import fr.exemole.bdfserver.api.subsettree.SubsetTree;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
import net.fichotheque.SubsetKey;
import net.mapeadores.util.localisation.Lang;


/**
 *
 * @author Vincent Calame
 */
public final class TreeUtils {

    public final static SubsetTree EMPTY_SUBSETTREE = new EmptySubsetTree();
    public final static List<SubsetTree.Node> EMPTY_NODELIST = Collections.emptyList();

    private TreeUtils() {
    }

    public static String getTitle(BdfServer bdfServer, GroupNode groupNode, Lang lang) {
        String name = groupNode.getName();
        GroupDef groupDef = bdfServer.getGroupManager().getGroupDef(name);
        if (groupDef != null) {
            return groupDef.getTitle(lang);
        } else {
            return name;
        }
    }

    public static List<SubsetKey> getCorpusKeyList(BdfServer bdfServer) {
        return toList(bdfServer.getTreeManager().getSubsetTree(SubsetKey.CATEGORY_CORPUS));
    }

    public static List<SubsetKey> toList(SubsetTree subsetTree) {
        List<SubsetKey> subsetKeyList = new ArrayList<SubsetKey>();
        add(subsetKeyList, subsetTree.getNodeList());
        return subsetKeyList;
    }

    private static void add(List<SubsetKey> subsetKeyList, List<SubsetTree.Node> nodeList) {
        for (SubsetTree.Node node : nodeList) {
            if (node instanceof GroupNode) {
                add(subsetKeyList, ((GroupNode) node).getSubnodeList());
            } else if (node instanceof SubsetNode) {
                subsetKeyList.add(((SubsetNode) node).getSubsetKey());
            }
        }
    }

    public static int size(SubsetTree subsetTree) {
        return size(subsetTree.getNodeList());
    }

    public static int size(GroupNode groupNode) {
        return size(groupNode.getSubnodeList());
    }

    private static int size(List<SubsetTree.Node> nodeList) {
        int size = 0;
        for (SubsetTree.Node node : nodeList) {
            if (node instanceof GroupNode) {
                size += size((GroupNode) node);
            } else if (node instanceof SubsetNode) {
                size++;
            }
        }
        return size;
    }

    public static SubsetKey getFirstSubsetKey(SubsetTree subsetTree) {
        for (SubsetTree.Node node : subsetTree.getNodeList()) {
            if (node instanceof GroupNode) {
                SubsetKey result = checkFirstSubset((GroupNode) node);
                if (result != null) {
                    return result;
                }
            } else if (node instanceof SubsetNode) {
                return ((SubsetNode) node).getSubsetKey();
            }
        }
        return null;
    }

    private static SubsetKey checkFirstSubset(GroupNode groupNode) {
        for (SubsetTree.Node subnode : groupNode.getSubnodeList()) {
            if (subnode instanceof GroupNode) {
                SubsetKey result = checkFirstSubset((GroupNode) subnode);
                if (result != null) {
                    return result;
                }
            } else if (subnode instanceof SubsetNode) {
                return ((SubsetNode) subnode).getSubsetKey();
            }
        }
        return null;
    }

    public static boolean containsSubsetKey(SubsetTree subsetTree, SubsetKey subsetKey) {
        for (SubsetTree.Node node : subsetTree.getNodeList()) {
            if (node instanceof GroupNode) {
                boolean result = containsSubsetKey((GroupNode) node, subsetKey);
                if (result) {
                    return true;
                }
            } else if (node instanceof SubsetNode) {
                if (((SubsetNode) node).getSubsetKey().equals(subsetKey)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static boolean containsSubsetKey(GroupNode groupNode, SubsetKey subsetKey) {
        for (SubsetTree.Node subnode : groupNode.getSubnodeList()) {
            if (subnode instanceof GroupNode) {
                boolean result = containsSubsetKey((GroupNode) subnode, subsetKey);
                if (result) {
                    return true;
                }
            } else if (subnode instanceof SubsetNode) {
                if (((SubsetNode) subnode).getSubsetKey().equals(subsetKey)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static List<SubsetTree.Node> wrap(SubsetTree.Node[] array) {
        return new NodeList(array);
    }

    public static String toString(SubsetTree subsetTree) {
        StringBuilder buf = new StringBuilder();
        for (SubsetTree.Node node : subsetTree.getNodeList()) {
            if (node instanceof SubsetNode) {
                appendSubsetNode(buf, (SubsetNode) node, 0);
            } else if (node instanceof GroupNode) {
                appendGroupNode(buf, (GroupNode) node, 1);
            }
        }
        return buf.toString();
    }

    private static void appendSubsetNode(StringBuilder buf, SubsetNode subsetNode, int indent) {
        for (int i = 0; i < indent; i++) {
            buf.append("  ");
        }
        buf.append(subsetNode.getSubsetKey());
        buf.append("\n");
    }

    private static void appendGroupNode(StringBuilder buf, GroupNode groupNode, int level) {
        buf.append("\n");
        String name = groupNode.getName();
        for (int i = 0; i < (level - 1); i++) {
            buf.append("  ");
        }
        for (int i = 0; i < level; i++) {
            buf.append('+');
        }
        buf.append(" ");
        buf.append(name);
        buf.append("\n");
        for (SubsetTree.Node subnode : groupNode.getSubnodeList()) {
            if (subnode instanceof SubsetNode) {
                appendSubsetNode(buf, (SubsetNode) subnode, level + 1);
            } else if (subnode instanceof GroupNode) {
                appendGroupNode(buf, (GroupNode) subnode, level + 1);

            }
        }
        for (int i = 0; i < (level - 1); i++) {
            buf.append("  ");
        }
        for (int i = 0; i < level; i++) {
            buf.append('-');
        }
        buf.append(" ");
        buf.append(name);
        buf.append("\n\n");
    }


    private static class EmptySubsetTree implements SubsetTree {

        private EmptySubsetTree() {

        }

        @Override
        public List<SubsetTree.Node> getNodeList() {
            return EMPTY_NODELIST;
        }

    }


    private static class NodeList extends AbstractList<SubsetTree.Node> implements RandomAccess {

        private final SubsetTree.Node[] array;

        private NodeList(SubsetTree.Node[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public SubsetTree.Node get(int index) {
            return array[index];
        }

    }

}
