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();
    }
}
