/* BdfServer - Copyright (c) 2014-2020 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.subsettree.GroupNode;
import fr.exemole.bdfserver.api.subsettree.SubsetNode;
import fr.exemole.bdfserver.api.subsettree.SubsetTree;
import java.text.ParseException;
import java.util.Stack;
import net.fichotheque.SubsetKey;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public class TreeParser {

    private final SubsetTreeBuilder subsetTreeBuilder;
    private final Stack<GroupNodeBuilder> parentStack = new Stack<GroupNodeBuilder>();
    private final short category;
    private GroupNodeBuilder currentGroupNodeBuilder;

    private TreeParser(short category) {
        this.subsetTreeBuilder = new SubsetTreeBuilder(false);
        this.category = category;
    }

    public static SubsetTree parse(BdfServer bdfServer, String text, short category) {
        TreeParser treeParser = new TreeParser(category);
        String[] tokens = StringUtils.getLineTokens(text, StringUtils.EMPTY_EXCLUDE);
        int length = tokens.length;
        for (int i = 0; i < length; i++) {
            treeParser.parseLine(tokens[i]);
        }
        treeParser.flush();
        return treeParser.subsetTreeBuilder.toSubsetTree();
    }

    private void parseLine(String token) {
        char firstChar = token.charAt(0);
        if (firstChar == '+') {
            String groupName = getGroupName(token);
            if (currentGroupNodeBuilder != null) {
                parentStack.add(currentGroupNodeBuilder);
            }
            currentGroupNodeBuilder = new GroupNodeBuilder(groupName, false);
        } else if (firstChar == '-') {
            if (currentGroupNodeBuilder != null) {
                GroupNode groupNode = currentGroupNodeBuilder.toGroupNode();
                if (parentStack.size() == 0) {
                    subsetTreeBuilder.addGroupNode(groupNode);
                    currentGroupNodeBuilder = null;
                } else {
                    currentGroupNodeBuilder = parentStack.pop();
                    currentGroupNodeBuilder.addGroupNode(groupNode);
                }
            }
        } else {
            int idx = token.indexOf('(');
            if (idx != -1) {
                token = token.substring(0, idx).trim();
            }
            try {
                SubsetKey subsetKey = SubsetKey.parse(token);
                if (subsetKey.getCategory() == category) {
                    SubsetNode subsetNode = SubsetNodeBuilder.build(subsetKey);
                    if (currentGroupNodeBuilder != null) {
                        currentGroupNodeBuilder.addSubsetNode(subsetNode);
                    } else {
                        subsetTreeBuilder.addSubsetNode(subsetNode);
                    }
                }
            } catch (ParseException pe) {
            }
        }
    }

    private void flush() {
        if (currentGroupNodeBuilder != null) {
            while (currentGroupNodeBuilder != null) {
                GroupNode groupNode = currentGroupNodeBuilder.toGroupNode();
                if (parentStack.size() == 0) {
                    subsetTreeBuilder.addGroupNode(groupNode);
                    currentGroupNodeBuilder = null;
                } else {
                    currentGroupNodeBuilder = parentStack.pop();
                    currentGroupNodeBuilder.addGroupNode(groupNode);
                }
            }
        }
    }

    private String getGroupName(String token) {
        int idx = token.indexOf('(');
        if (idx != -1) {
            token = token.substring(1, idx).trim();
        }
        int length = token.length();
        for (int i = 0; i < length; i++) {
            char carac = token.charAt(i);
            if ((carac == '+') || (carac == ' ')) {
                continue;
            } else {
                token = token.substring(i).trim();
                break;
            }
        }
        if (!StringUtils.isTechnicalName(token, true)) {
            return "syntax_error";
        } else {
            return token;
        }
    }

}
