/*
 * 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.maven.shared.filtering;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.function.Consumer;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.interpolation.Interpolator;

/**
 * @since 1.0-beta-3
 */
public class AbstractMavenFilteringRequest {

    private MavenProject mavenProject;

    private List<String> filters;

    private boolean escapeWindowsPaths = true;

    private MavenSession mavenSession;

    /**
     * List of Strings considered as expressions which contains values in the project/pom: pom project Default value
     * will be pom and project.
     *
     * @since 1.0-beta-2
     */
    private List<String> projectStartExpressions = new ArrayList<>();

    /**
     * String which will escape interpolation mechanism: foo \${foo.bar} -> foo ${foo.bar}
     *
     * @since 1.0-beta-2
     */
    private String escapeString;

    /**
     * @since 1.0-beta-3
     */
    private Properties additionalProperties;

    /**
     * @since 1.0-beta-3
     */
    private boolean injectProjectBuildFilters = false;

    /**
     * Set of expression delimiter specifications to use during filtering. Delimiter specifications are given in the
     * form 'BEGIN*END' or, for symmetrical delimiters, simply 'TOKEN'. The default values are '${*}' and '@'.
     *
     * @since 1.0-beta-3
     */
    private LinkedHashSet<String> delimiters = new LinkedHashSet<>();

    /**
     * Do not stop trying to filter tokens when reaching EOL.
     *
     * @since 1.0
     */
    private boolean supportMultiLineFiltering;

    private Consumer<Interpolator> interpolatorCustomizer;

    /**
     * Create instance.
     */
    protected AbstractMavenFilteringRequest() {
        initDefaults();
    }

    /**
     * Create instance with given parameters
     *
     * @param mavenProject The instance of MavenProject.
     * @param filters The list of filters.
     * @param mavenSession The MavenSession.
     */
    protected AbstractMavenFilteringRequest(
            MavenProject mavenProject, List<String> filters, MavenSession mavenSession) {
        initDefaults();
        this.mavenProject = mavenProject;
        this.filters = filters;
        this.mavenSession = mavenSession;
    }

    private void initDefaults() {
        projectStartExpressions.add("pom");
        projectStartExpressions.add("project");

        delimiters.add("${*}");
        delimiters.add("@");
    }

    /**
     * @return The MavenProject
     */
    public MavenProject getMavenProject() {
        return mavenProject;
    }

    /**
     * Set the MavenProject.
     *
     * @param mavenProject The MavenProject to be set.
     */
    public void setMavenProject(MavenProject mavenProject) {
        this.mavenProject = mavenProject;
    }

    /**
     * The list of filters.
     *
     * @return The list of currently set filters.
     */
    public List<String> getFilters() {
        return filters;
    }

    /**
     * Set the filters.
     *
     * @param filters Set the list of filters
     */
    public void setFilters(List<String> filters) {
        this.filters = filters;
    }

    /**
     * Alias for {@link #getFilters()}.
     *
     * @return The list of filters.
     */
    public List<String> getFileFilters() {
        return getFilters();
    }

    /**
     * Alias for {@link #setFilters(List)}
     *
     * @param paramfilters The list of filters to be set.
     */
    public void setFileFilters(List<String> paramfilters) {
        setFilters(paramfilters);
    }

    /**
     * @since 1.0-beta-3
     * @return true if escape is activated false otherwise.
     */
    public boolean isEscapeWindowsPaths() {
        return escapeWindowsPaths;
    }

    /**
     * @since 1.0-beta-3
     * @param escapedBackslashesInFilePath true or false.
     */
    public void setEscapeWindowsPaths(boolean escapedBackslashesInFilePath) {
        this.escapeWindowsPaths = escapedBackslashesInFilePath;
    }

    /**
     * Alias for {@link #isEscapeWindowsPaths()}
     *
     * @return The current value of {@link #isEscapeWindowsPaths()}
     */
    public boolean isEscapedBackslashesInFilePath() {
        return isEscapeWindowsPaths();
    }

    /**
     * Alias for {@link #setEscapeWindowsPaths(boolean)}
     *
     * @param escape activate or deactivate escaping.
     */
    public void setEscapedBackslashesInFilePath(boolean escape) {
        setEscapeWindowsPaths(escape);
    }

    /**
     * @return Current value of mavenSession
     */
    public MavenSession getMavenSession() {
        return mavenSession;
    }

    /**
     * @param mavenSession Set new value for the MavenSession of the instance.
     */
    public void setMavenSession(MavenSession mavenSession) {
        this.mavenSession = mavenSession;
    }

    /**
     * @return the additional properties.
     * @since 1.0-beta-3
     */
    public Properties getAdditionalProperties() {
        return additionalProperties;
    }

    /**
     * @param additionalProperties The additional properties to be set.
     * @since 1.0-beta-3
     */
    public void setAdditionalProperties(Properties additionalProperties) {
        this.additionalProperties = additionalProperties;
    }

    /**
     * @return the current value of injectProjectBuildFilters.
     * @since 1.0-beta-3
     */
    public boolean isInjectProjectBuildFilters() {
        return injectProjectBuildFilters;
    }

    /**
     * @param injectProjectBuildFilters true or false.
     * @since 1.0-beta-3
     */
    public void setInjectProjectBuildFilters(boolean injectProjectBuildFilters) {
        this.injectProjectBuildFilters = injectProjectBuildFilters;
    }

    /**
     * @return Current value of escapeString.
     * @since 1.0-beta-2
     */
    public String getEscapeString() {
        return escapeString;
    }

    /**
     * @param escapeString The escape string to use
     * @since 1.0-beta-2
     */
    public void setEscapeString(String escapeString) {
        this.escapeString = escapeString;
    }

    /**
     * @return The list of project start expressions.
     * @since 1.0-beta-2
     */
    public List<String> getProjectStartExpressions() {
        return projectStartExpressions;
    }

    /**
     * @param projectStartExpressions The start expressions
     * @since 1.0-beta-2
     */
    public void setProjectStartExpressions(List<String> projectStartExpressions) {
        this.projectStartExpressions = projectStartExpressions;
    }

    /**
     * See {@link AbstractMavenFilteringRequest#delimiters} for more information and default values.
     *
     * @return Not allowed to be <code>null</code> or empty.
     * @since 1.0-beta-3
     */
    public LinkedHashSet<String> getDelimiters() {
        return delimiters;
    }

    /**
     * Set the delimiter specifications to use during filtering. Specifications should be of the form: 'BEGIN*END' for
     * asymmetrical delimiters, or 'TOKEN' for symmetrical delimiters. See
     * {@link AbstractMavenFilteringRequest#delimiters} for more information and default values.
     *
     * @param delimiters If <code>null</code>, reset delimiters to '${*}' only. Otherwise, use the provided value.
     * @since 1.0-beta-3
     */
    public void setDelimiters(LinkedHashSet<String> delimiters) {
        if (delimiters == null || delimiters.isEmpty()) {
            this.delimiters.clear();
            this.delimiters.add("${*}");
        } else {
            this.delimiters = delimiters;
        }
    }

    /**
     * @param delimiters If {@code null} than nothing will happen. If not {@code null} the delimiters will be set
     *            according to the contents. If delimiter entries are {@code null} those entries will be set to '${*}'.
     * @param useDefaultDelimiters true if the default delimiters will be used false otherwise.
     */
    public void setDelimiters(LinkedHashSet<String> delimiters, boolean useDefaultDelimiters) {
        if (delimiters != null && !delimiters.isEmpty()) {
            LinkedHashSet<String> delims = new LinkedHashSet<>();
            if (useDefaultDelimiters) {
                delims.addAll(this.getDelimiters());
            }

            for (String delim : delimiters) {
                if (delim == null) {
                    // FIXME: ${filter:*} could also trigger this condition. Need a better long-term solution.
                    delims.add("${*}");
                } else {
                    delims.add(delim);
                }
            }

            this.setDelimiters(delims);
        }
    }

    /**
     * @return If support multiple line filtering is active or not.
     */
    public boolean isSupportMultiLineFiltering() {
        return supportMultiLineFiltering;
    }

    /**
     * @param supportMultiLineFiltering activate or deactivate multiple line filtering support.
     */
    public void setSupportMultiLineFiltering(boolean supportMultiLineFiltering) {
        this.supportMultiLineFiltering = supportMultiLineFiltering;
    }

    /**
     *
     * @return the customizer which is supposed to be used by filters creating an {@link Interpolator} like those based on {@link BaseFilter}.
     * @since 3.4.0
     */
    public Consumer<Interpolator> getInterpolatorCustomizer() {
        return interpolatorCustomizer;
    }

    /**
     *
     * @param interpolatorCustomizer the customizer which is supposed to be used by filters creating an {@link Interpolator} like those based on {@link BaseFilter}.
     * @since 3.4.0
     */
    public void setInterpolatorCustomizer(Consumer<Interpolator> interpolatorCustomizer) {
        this.interpolatorCustomizer = interpolatorCustomizer;
    }
}
