/FileExtraction/Java_unseen/AIMLang_aiml-java-interpreter/core/src/main/java/org/aimlang/core/loaders/AimlLoader.java
package org.aimlang.core.loaders;

import org.aimlang.core.consts.AimlConst;
import org.aimlang.core.consts.AimlTag;
import org.aimlang.core.entity.AimlCategory;
import org.slf4j.Logger;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.aimlang.core.utils.AppUtils.node2String;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * Aiml loader
 *
 * @author anton
 * @since 21/06/15
 */
public class AimlLoader {
    private static final Logger log = getLogger(AimlLoader.class);

    private final XmlLoader loader;

    public AimlLoader() {
        this.loader = new XmlLoader();
    }

    /**
     * Loading all aiml files from folder
     *
     * @param aimlDir folder contain all aiml files
     * @return list of loaded categories
     */
    public List<AimlCategory> loadFiles(String aimlDir) {
        var categories = new ArrayList<AimlCategory>();
        var aimls = new File(aimlDir);
        var files = aimls.listFiles();
        if (files == null || files.length == 0) {
            log.warn("Not files in folder {} ", aimlDir);
            return categories;
        }
        int countNotAimlFiles = 0;
        for (File file : files) {
            if (file.getName().endsWith(AimlConst.AIML_FILE_SUFFIX))
                categories.addAll(loadFile(file));
            else
                ++countNotAimlFiles;
        }
        if (countNotAimlFiles != 0)
            log.warn("Founded {} not aiml files in folder {}", countNotAimlFiles, aimlDir);
        log.info("Loaded {} categories", categories.size());
        return categories;
    }

    /**
     * Loading single aiml file
     *
     * @param aimlFile aiml file
     */
    private List<AimlCategory> loadFile(File aimlFile) {
        var aimlRoot = loader.load(aimlFile);
        if (aimlRoot == null)
            return Collections.emptyList();

        if (!aimlRoot.getNodeName().equals(AimlTag.aiml)) {
            log.warn(aimlFile.getName() + " is not AIML file");
            return Collections.emptyList();
        }
        var aimlVersion = aimlRoot.getAttribute("version");
        log.debug("Load aiml " + aimlFile.getName() + (aimlVersion.isEmpty() ? "" : " [v." + aimlVersion + "]"));

        return aimlParser(aimlRoot.getChildNodes());
    }

    private List<AimlCategory> aimlParser(NodeList nodes) {
        var categories = new ArrayList<AimlCategory>();
        for (int i = 0; i < nodes.getLength(); ++i) {
            var node = nodes.item(i);

            var nodeName = node.getNodeName();
            switch (nodeName) {
                case AimlTag.text:
                case AimlTag.comment:
                    break;
                case AimlTag.topic:
                    categories.addAll(parseTopic(node));
                    break;
                case AimlTag.category:
                    if (!categories.add(parseCategory(node)))
                        log.debug(node2String(node));
                    break;
                default:
                    log.warn("Wrong structure: <aiml> tag contain " + nodeName + " tag");
            }
        }
        return categories;
    }

    private List<AimlCategory> parseTopic(Node node) {
        var categories = new ArrayList<AimlCategory>();
        var childNodes = node.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); ++i) {
            var childNodeName = childNodes.item(i).getNodeName();
            switch (childNodeName) {
                case AimlTag.text:
                case AimlTag.comment:
                    break;
                case AimlTag.category:
                    categories.add(parseCategory(childNodes.item(i), getAttribute(node, AimlTag.name)));
                    break;
                default:
                    log.warn("Wrong structure: <topic> tag contain " + childNodeName + " tag");
            }
        }
        return categories;
    }

    private String getAttribute(Node node, String attributeName) {
        return node.getAttributes()
                .getNamedItem(attributeName)
                .getNodeValue();
    }

    private AimlCategory parseCategory(Node node) {
        return parseCategory(node, AimlConst.default_topic);
    }

    private AimlCategory parseCategory(Node node, String topic) {
        var category = new AimlCategory();
        category.setTopic(topic);
        var childNodes = node.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); ++i) {
            var childNode = childNodes.item(i);
            childNode.normalize();
            var childNodeName = childNode.getNodeName();
            switch (childNodeName) {
                case AimlTag.text:
                case AimlTag.comment:
                    break;
                case AimlTag.pattern:
                    category.setPattern(node2String(childNode));
                    break;
                case AimlTag.template:
                    category.setTemplate(childNode);
                    break;
                case AimlTag.topic:
                    category.setTopic(node2String(childNode));
                    break;
                case AimlTag.that:
                    category.setThat(node2String(childNode));
                    break;
                default:
                    log.warn("Wrong structure: <category> tag contain " + childNodeName + " tag");
            }
        }
        return category;
    }
}
