package org.jboss.windup.rules.apps.xml.condition;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

/**
 * Converts an XPath from a standard xpath, to one that calls Windup's builtin functions for hooking into the XPath execution lifecycle.
 */
public class XmlFileXPathTransformer {
    private static final String WINDUP_MATCHES_FUNCTION_PREFIX = "windup:matches(";

    /**
     * Performs the conversion from standard XPath to xpath with parameterization support.
     */
    public static String transformXPath(String originalXPath) {
        // use a list to maintain the multiple joined xqueries (if there are multiple queries joined with the "|" operator)
        List<StringBuilder> compiledXPaths = new ArrayList<>(1);

        int frameIdx = -1;
        boolean inQuote = false;
        int conditionLevel = 0;
        char startQuoteChar = 0;
        StringBuilder currentXPath = new StringBuilder();
        compiledXPaths.add(currentXPath);
        for (int i = 0; i < originalXPath.length(); i++) {
            char curChar = originalXPath.charAt(i);
            if (!inQuote && curChar == '[') {
                frameIdx++;
                conditionLevel++;
                currentXPath.append("[windup:startFrame(").append(frameIdx).append(") and windup:evaluate(").append(frameIdx).append(", ");
            } else if (!inQuote && curChar == ']') {
                conditionLevel--;
                currentXPath.append(")]");
            } else if (!inQuote && conditionLevel == 0 && curChar == '|') {
                // joining multiple xqueries
                currentXPath = new StringBuilder();
                compiledXPaths.add(currentXPath);
            } else {
                if (inQuote && curChar == startQuoteChar) {
                    inQuote = false;
                    startQuoteChar = 0;
                } else if (curChar == '"' || curChar == '\'') {
                    inQuote = true;
                    startQuoteChar = curChar;
                }

                if (!inQuote && originalXPath.startsWith(WINDUP_MATCHES_FUNCTION_PREFIX, i)) {
                    i += (WINDUP_MATCHES_FUNCTION_PREFIX.length() - 1);
                    currentXPath.append("windup:matches(").append(frameIdx).append(", ");
                } else {
                    currentXPath.append(curChar);
                }
            }
        }

        Pattern leadingAndTrailingWhitespace = Pattern.compile("(\\s*)(.*?)(\\s*)");
        StringBuilder finalResult = new StringBuilder();
        for (StringBuilder compiledXPath : compiledXPaths) {
            if (StringUtils.isNotBlank(compiledXPath)) {
                Matcher whitespaceMatcher = leadingAndTrailingWhitespace.matcher(compiledXPath);
                if (!whitespaceMatcher.matches())
                    continue;

                compiledXPath = new StringBuilder();
                compiledXPath.append(whitespaceMatcher.group(1));
                compiledXPath.append(whitespaceMatcher.group(2));
                compiledXPath.append("/self::node()[windup:persist(").append(frameIdx).append(", ").append(".)]");
                compiledXPath.append(whitespaceMatcher.group(3));

                if (StringUtils.isNotBlank(finalResult))
                    finalResult.append("|");
                finalResult.append(compiledXPath);
            }
        }
        return finalResult.toString();
    }
}
