/**
 * Copyright (c) 2005-2012 EBM WebSourcing, 2007-2009 Capgemini Sud, 2012-2022 Linagora
 * 
 * This program/library 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 program/library 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/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.plugin.jbiplugin;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.settings.MavenSettingsBuilder;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.MapBasedValueSource;
import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
import org.ow2.petals.jbi.descriptor.JBIDescriptorException;
import org.ow2.petals.jbi.descriptor.original.JBIDescriptorBuilder;
import org.ow2.petals.jbi.descriptor.original.generated.Jbi;

/**
 * This class is used to give the main configuration parameters of all JBI goals.
 * @author Christophe Deneux - Capgemini Sud
 */
public abstract class JBIAbstractMojo extends AbstractMojo {

    /*
     * Definition of all possible values of <packaging>:
     *   - jbi-component <-> binding-component or service-engine
     *   - jbi-service-unit <-> service-unit
     *   - jbi-service-assembly <-> service-assembly
     *   - jbi-shared-library <-> shared-library
     */
    protected static final String PACKAGING_COMPONENT = "jbi-component";

    protected static final String PACKAGING_SU = "jbi-service-unit";

    protected static final String PACKAGING_SA = "jbi-service-assembly";

    protected static final String PACKAGING_SL = "jbi-shared-library";

	/**
     * Name of the generated jbi archive (will be suffixed by .zip).
     */
    @Parameter(required = true, defaultValue = "${project.build.finalName}")
    protected String jbiName;

    /**
     * Verbose
     */
    @Parameter(required = true, property = "verbose", defaultValue = "false")
    protected boolean verbose;

    /**
     * Local maven repository.
     */
    @Parameter(required = true, readonly = true, defaultValue = "${localRepository}")
    protected ArtifactRepository localRepository;

    /**
     * Output directory
     */
    @Parameter(property = "outputDirectory", defaultValue = "${project.build.directory}")
    protected File outputDirectory;

    /**
     * The Maven Project.
     */
    @Parameter(required = true, readonly = true, defaultValue = "${project}")
    protected MavenProject project;

    @Component
    protected MavenProjectHelper projectHelper;
    
    /**
     * Project artifact.
     */
    @Parameter(required = true, readonly = true, defaultValue = "${project.artifact}")
    protected Artifact projectArtifact;

    @Parameter(required = true, readonly = true, defaultValue = "${project.packaging}")
    protected String packaging;

    /**
     * Path of the JBI source directory. This directory will be zipped as it is in the directory META-INF. It should
     * contain only the file jbi.xml. Other files should be read through the classpath.
     */
    @Parameter(required = true, defaultValue = "${basedir}/src/main/jbi")
    protected File jbiDirectory;

    @Component
    protected ArtifactResolver artifactResolver;

    /**
     * Needed to retrieve needed JBI archive dependencies.
     */
    @Component
    protected ArtifactFactory artifactFactory;

    /**
     * Needed to retrieve needed JBI archive dependencies.
     */
    @Component
    protected MavenProjectBuilder mavenProjectBuilder;

    @Component
    protected MavenSettingsBuilder settingsBuilder;

    /**
     * Read and parse the JBI descriptor of an artifact
     *
     * @param artifact JBI artifact containing the JBI descriptor. Not null.
     * @throws MojoExecutionException
     */
    final protected Jbi readJbiDescriptor(final Artifact artifact)
            throws MojoExecutionException {
        try {

            final File artifactFile = artifact.getFile();
            if (artifactFile == null) {
                throw new MojoExecutionException("Artifact file undefined (is null).");
            }
            this.debug("Read JBI descriptor from JBI archive: " + artifactFile.getAbsolutePath());
            
            // Load the JBI descriptor
            final Jbi jbiDescriptor = JBIDescriptorBuilder.getInstance()
                    .buildJavaJBIDescriptorFromArchive(artifactFile);

            if (jbiDescriptor == null) {
                throw new MojoExecutionException("JBI Descriptor is null");
            }

            return jbiDescriptor;

        } catch (JBIDescriptorException e) {
            throw new MojoExecutionException("Error loading JBI descriptor", e);
        }
    }
    
    protected void attachedJBIArchive(final Artifact artifact) {

        this.projectArtifact.setFile(
                new File(this.outputDirectory + File.separator + this.jbiName + ".zip"));

    }

    /**
     * Log the message as DEBUG one
     *
     * @param msg
     */
    protected void debug(String msg) {
        this.getLog().debug(msg);
    }

    /**
     * Log the message as INFO one
     *
     * @param msg
     */
    protected void info(String msg) {
		if (verbose) {
			this.getLog().info(msg);
		}
	}

    /**
     * Log the message as WARNING one
     *
     * @param msg
     */
    protected void warn(String msg) {
        this.getLog().warn(msg);
    }

    /**
     * Log the message as ERROR one
     *
     * @param msg
     */
    protected void error(String msg) {
		this.getLog().error(msg);
	}

    /**
     * Log the message and the exception as ERROR one
     *
     * @param msg
     * @param exception
     */
	protected void error(String msg, Throwable exception) {
		this.getLog().error(msg, exception);
	}

    @Override
    final public void execute() throws MojoExecutionException, MojoFailureException {
        if (this.packagingIsOkForGoal()) {
            this.executeMojo();
        }
        else {
            this.debug("The project is not a JBI artifact so the plugin skips it.");
        }
    }
    
    /**
     * Check if the current goal can be applied to the current packaging
     * @return
     */
    protected boolean packagingIsOkForGoal() {
        return PACKAGING_COMPONENT.equals(this.project.getPackaging())
                || PACKAGING_SA.equals(this.project.getPackaging())
                || PACKAGING_SL.equals(this.project.getPackaging())
                || PACKAGING_SU.equals(this.project.getPackaging());
    }

    /**
	 * Create a maven project from the given artifact
	 * @param artifact
	 * @return
	 * @throws ProjectBuildingException
	 */
	protected MavenProject buildProjectFromArtifact(Artifact artifact) throws ProjectBuildingException{
		return mavenProjectBuilder.buildFromRepository(artifact, project
				.getRemoteArtifactRepositories(),
				this.localRepository);
	}

	/**
	 * Create an Artifact from the given Dependency
	 * @param dependency MUST be non null
	 * @return the created artifact
	 * @throws InvalidVersionSpecificationException problem with the version of the dependency
	 */
	protected Artifact createDependencyArtifact(final Dependency dependency)
            throws InvalidVersionSpecificationException {
    	final Artifact dependencyArtifact = this.artifactFactory
            	.createDependencyArtifact(
            	        dependency.getGroupId(),
            			dependency.getArtifactId(),
            			VersionRange.createFromVersionSpec(
            			        dependency.getVersion()),
            			dependency.getType(),
            			dependency.getClassifier(),
            			dependency.getScope(),
            			null,
            			dependency.isOptional());

    	return dependencyArtifact;
	}

    /**
     * Retrieve artifact associated to the shared libraries needed by Maven
     * project associated to a JBI compoenent.
     * 
     * @return A shared libraries list used by the current JBI component Maven
     *         project
     * @throws InvalidVersionSpecificationException
     */
    protected List<Artifact> getSharedLibraries(final MavenProject lProject)
            throws InvalidVersionSpecificationException {
        final List<Artifact> result = new ArrayList<Artifact>();
        final List<Dependency> deps = lProject.getDependencies();
        for (final Dependency dependency : deps) {
            if (dependency.getType().equals(PACKAGING_SL)) {

                final Artifact artifact = createDependencyArtifact(dependency);

                result.add(artifact);
            }
        }
        return result;
    }

    /**
     * Evaluates Filename Mapping (source code copied from maven-assembly-plugin)
     * 
     * @param expression
     * @param artifact
     * @return mapped expression
     * @throws InterpolationException
     */
    // TODO (CDE): Rework to simplify after fixing of MNG-3558
    protected String evaluateFileNameMapping(final String expression, final Artifact artifact,
            final Properties properties) throws InterpolationException {

        this.debug("Evaluating expression: " + expression);

        String value = expression.replaceAll("\\$\\{dollarSign\\}", "\\$");

        // FIXME: This is BAD! Accessors SHOULD NOT change the behavior of the object.
        artifact.isSnapshot();

        RegexBasedInterpolator interpolator = new RegexBasedInterpolator();

        interpolator.addValueSource(new ObjectBasedValueSource(artifact));
        interpolator.addValueSource(new ObjectBasedValueSource(artifact.getArtifactHandler()));
        interpolator.addValueSource(new MapBasedValueSource(properties));

        Properties classifierMask = new Properties();
        classifierMask.setProperty("classifier", "");

        interpolator.addValueSource(new PropertiesBasedValueSource(classifierMask));

        value = interpolator.interpolate(value, "__artifact");

        this.debug("Expression evaluated to: " + value);

        return value;
    }
    
    /**
     * Execute the MOJO
     *
     * @throws MojoExecutionException
     * @throws MojoFailureException
     */
    abstract protected void executeMojo() throws MojoExecutionException, MojoFailureException;
}
