/**
 * This file is part of the Meeds project (https://meeds.io/).
 *
 * Copyright (C) 2020 - 2025 Meeds Association contact@meeds.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.exoplatform.web.controller.router;

import java.io.IOException;
import java.util.List;

import org.exoplatform.web.controller.QualifiedName;
import org.exoplatform.web.controller.metadata.PathParamDescriptor;
import org.exoplatform.web.controller.regexp.RENode;
import org.exoplatform.web.controller.regexp.REParser;
import org.exoplatform.web.controller.regexp.RERenderer;
import org.exoplatform.web.controller.regexp.REVisitor;
import org.exoplatform.web.controller.regexp.SyntaxException;

class PathParam extends Param {

    static PathParam create(QualifiedName name, Router router) {
        return create(new PathParamDescriptor(name), router);
    }

    static PathParam create(PathParamDescriptor descriptor, Router router) {
        if (descriptor == null) {
            throw new NullPointerException("No null descriptor accepted");
        }

        //
        String regex = null;
        EncodingMode encodingMode = EncodingMode.FORM;
        if (descriptor != null) {
            regex = descriptor.getPattern();
            encodingMode = descriptor.getEncodingMode();
        }

        //
        if (regex == null) {
            if (encodingMode == EncodingMode.FORM) {
                regex = ".+";
            } else {
                regex = "[^/]+";
            }
        }

        // Now work on the regex
        StringBuilder routingRegex = new StringBuilder();
        Regex[] renderingRegexes;
        String[] templatePrefixes;
        String[] templateSuffixes;
        try {
            REVisitor<RuntimeException> transformer = descriptor.getCaptureGroup() ? new CaptureGroupTransformation()
                    : new NonCaptureGroupTransformation();
            REParser parser = new REParser(regex);

            //
            RENode.Disjunction routingDisjunction = parser.parseDisjunction();
            if (encodingMode == EncodingMode.FORM) {
                CharEscapeTransformation escaper = new CharEscapeTransformation('/', '_');
                routingDisjunction.accept(escaper);
            }
            routingDisjunction.accept(transformer);
            RERenderer.render(routingDisjunction, routingRegex);

            //
            parser.reset();
            RENode.Disjunction renderingDisjunction = parser.parseDisjunction();
            ValueResolverFactory factory = new ValueResolverFactory();
            renderingDisjunction.accept(transformer);
            List<ValueResolverFactory.Alternative> alt = factory.foo(renderingDisjunction);
            renderingRegexes = new Regex[alt.size()];
            templatePrefixes = new String[alt.size()];
            templateSuffixes = new String[alt.size()];
            for (int i = 0; i < alt.size(); i++) {
                ValueResolverFactory.Alternative v = alt.get(i);
                StringBuilder valueMatcher = v.getValueMatcher();
                valueMatcher.insert(0, '^');
                valueMatcher.append("$");
                renderingRegexes[i] = router.compile(valueMatcher.toString());
                templatePrefixes[i] = v.getPrefix();
                templateSuffixes[i] = v.getSuffix();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (SyntaxException e) {
            throw new RuntimeException(e);
        } catch (MalformedRouteException e) {
            throw new RuntimeException(e);
        }

        //
        return new PathParam(descriptor.getQualifiedName(), encodingMode, routingRegex.toString(), renderingRegexes,
                templatePrefixes, templateSuffixes);
    }

    /** . */
    final EncodingMode encodingMode;

    /** . */
    final String routingRegex;

    /** . */
    final Regex[] matchingRegex;

    /** . */
    final String[] templatePrefixes;

    /** . */
    final String[] templateSuffixes;

    private PathParam(QualifiedName name, EncodingMode encodingMode, String routingRegex, Regex[] matchingRegex,
            String[] templatePrefixes, String[] templateSuffixes) {
        super(name);

        //
        if (matchingRegex == null || matchingRegex.length == 0) {
            throw new NullPointerException("No null or empty pattern accepted");
        }

        //
        this.encodingMode = encodingMode;
        this.routingRegex = routingRegex;
        this.matchingRegex = matchingRegex;
        this.templatePrefixes = templatePrefixes;
        this.templateSuffixes = templateSuffixes;
    }

    @Override
    public String toString() {
        return "PathParam[name=" + name + ",encodingMode=" + encodingMode + ",pattern=" + matchingRegex + "]";
    }
}
