/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.parser.helper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.apache.camel.catalog.CamelCatalog;
import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.catalog.JSonSchemaHelper;

import org.apache.camel.parser.model.CamelNodeDetails;
import org.apache.camel.parser.model.CamelNodeDetailsFactory;

public final class CamelXmlTreeParserHelper {

    private final CamelCatalog camelCatalog = new DefaultCamelCatalog(true);

    public List<CamelNodeDetails> parseCamelRouteTree(Node xmlNode, String routeId, CamelNodeDetails route,
                                                             String baseDir, String fullyQualifiedFileName) {

        CamelNodeDetailsFactory nodeFactory = CamelNodeDetailsFactory.newInstance();
        List<CamelNodeDetails> answer = new ArrayList<>();

        walkXmlTree(nodeFactory, xmlNode, route);

        // now parse the route node and build the correct model/tree structure of the EIPs
        // re-create factory as we rebuild the tree
        nodeFactory = CamelNodeDetailsFactory.newInstance();
        CamelNodeDetails parent = route.getOutputs().get(0);

        // we dont want the route element and only start with from
        for (int i = 0; i < route.getOutputs().size(); i++) {
            CamelNodeDetails node = route.getOutputs().get(i);
            String name = node.getName();

            if ("from".equals(name)) {
                CamelNodeDetails from = nodeFactory.copyNode(null, "from", node);
                from.setFileName(fullyQualifiedFileName);
                answer.add(from);
                parent = from;
            } else {
                // add straight to parent
                parent.addOutput(node);
                node.setFileName(fullyQualifiedFileName);
            }
        }

        return answer;
    }

    private void walkXmlTree(CamelNodeDetailsFactory nodeFactory, Node node, CamelNodeDetails parent) {
        CamelNodeDetails newNode = null;

        String name = node.getNodeName();

        boolean isRoute = "route".equals(name) || "from".equals(name);
        // must be an eip model that has either input or output as we only want to track processors (also accept from)
        boolean isEip = camelCatalog.findModelNames().contains(name) && (hasInput(name) || hasOutput(name));
        // skip when/otherwise (as we do this in Java DSL)
        boolean isWhenOrOtherwise = "when".equals(name) || "otherwise".equals(name);

        // only include if its a known Camel model (dont include languages)
        if (isRoute || isEip) {
            // skip route as we just keep from (and also skip when/otherwise)
            if (!"route".equals(name) && !isWhenOrOtherwise) {
                String lineNumber = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER);
                String lineNumberEnd = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
                newNode = nodeFactory.newNode(parent, name);
                newNode.setRouteId(parent.getRouteId());
                newNode.setFileName(parent.getFileName());
                newNode.setLineNumber(lineNumber);
                newNode.setLineNumberEnd(lineNumberEnd);

                parent.addOutput(newNode);
            }
        }

        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                walkXmlTree(nodeFactory, child, newNode != null ? newNode : parent);
            }
        }

    }

    private boolean hasOutput(String name) {
        String json = camelCatalog.modelJSonSchema(name);
        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("model", json, false);
        return isModelOutput(rows);
    }

    private static boolean isModelOutput(List<Map<String, String>> rows) {
        for (Map<String, String> row : rows) {
            if (row.containsKey("output")) {
                return "true".equals(row.get("output"));
            }
        }
        return false;
    }

    private boolean hasInput(String name) {
        String json = camelCatalog.modelJSonSchema(name);
        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("model", json, false);
        return isModelInput(rows);
    }

    private static boolean isModelInput(List<Map<String, String>> rows) {
        for (Map<String, String> row : rows) {
            if (row.containsKey("input")) {
                return "true".equals(row.get("input"));
            }
        }
        return false;
    }

}
