/*
 * Copyright (C) 2010 eXo Platform SAS.
 *
 * This 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 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

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;

/**
 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
 * @version $Revision$
 */
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 + "]";
    }
}
