package org.apache.maven.artifact.deployer;

/* ====================================================================
 *   Copyright 2001-2004 The Apache Software Foundation.
 *
 *   Licensed 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.
 * ====================================================================
 */

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.MavenConstants;
import org.apache.maven.MavenException;
import org.apache.maven.deploy.DeployTool;
import org.apache.maven.deploy.RepositoryInfo;
import org.apache.maven.project.Project;
import org.apache.maven.util.MD5Sum;

/**
 * 
 * Default implemenataion of Artifact Deployer interface.
 *   
 *   
 * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a> 
 * @version $Id: DefaultArtifactDeployer.java,v 1.16 2004/03/02 14:59:31 evenisse Exp $
 */
public class DefaultArtifactDeployer implements ArtifactDeployer
{

    /** 
     * Indicate if POM of given artifact should be also deployed 
     * to remote repository*/
    public static final boolean DEPLOY_POM = true;

    /** 
     * Indicate if POM of given artifact should be also deployed 
     * in local repository*/
    public static final boolean INSTALL_POM = true;

    /** 
     * Indicate if snapshot versio of 
     * POM of given artifact should be also deployed 
     * to remote repository*/
    public static final boolean DEPLOY_POM_SNAPSHOT = true;

    /** 
     * Indicate if POM of given artifact should be also installed 
     * in local repository*/
    public static final boolean INSTALL_POM_SNAPSHOT = true;

    /**
     * Date/time stamp which is appended to snapshot filenames  
     */
    public final static DateFormat SNAPSHOT_SIGNATURE_FMT =
        new SimpleDateFormat("yyyyMMdd.HHmmss");

    static {
        SNAPSHOT_SIGNATURE_FMT.setTimeZone(TimeZone.getTimeZone("GMT"));
    }

    /**
     * @see ArtifactDeployer#deploy(String, String, Project)
     * 
     * This is "5 files" version.
     * It deploys (example):
     *   foo-20030620.124616.jar, 
     *   foo-20030620.124616.jar.md5 , 
     *   foo-SNAPSHOT.jar
     *   foo-SNAPSHOT.jar.md5
     *   foo-snapshot-version 
     *
     */
    public void deploy(String artifact, String type, Project project)
        throws MavenException
    {
        File file;
        if("pom".equals(type))
        {
            file = project.getFile();
        }
        else
        {
            file = getFileForArtifact(artifact);
        }
        File md5File = createMD5Checksum(file);
        String repositoryPath =
            getRepositoryFullPath(type, project, project.getCurrentVersion());

        List srcFiles = new ArrayList();
        srcFiles.add(file.getAbsolutePath());
        srcFiles.add(md5File.getAbsolutePath());

        List destFiles = new ArrayList();
        destFiles.add(repositoryPath);
        destFiles.add(repositoryPath + ".md5");

        //do not deploy POM twice
        if (DEPLOY_POM && !"pom".equals(type))
        {
            deploy(artifact, "pom", project);
        }
        doDeploy(srcFiles, destFiles, project);
        //Delete md5 file
        md5File.delete();
    }

    /**
     * @see DefaultArtifactDeployer#deploySnapshot(String, String, Project)
     */
    public void deploySnapshot(String artifact, String type, Project project)
        throws MavenException
    {

        String snapshotSignature = getSnapshotSignature();
        File file;
        if("pom".equals(type))
        {
            file = project.getFile(); 
        }
        else
        {
            file = getFileForArtifact(artifact);
        }
        File md5File = createMD5Checksum(file);
        File snapshotVersionFile =
            createSnapshotVersionFile(file, snapshotSignature, project, type);

        List srcFiles = new ArrayList();
        srcFiles.add(file.getAbsolutePath());
        srcFiles.add(md5File.getAbsolutePath());
        srcFiles.add(file.getAbsolutePath());
        srcFiles.add(md5File.getAbsolutePath());
        srcFiles.add(snapshotVersionFile.getAbsolutePath());

        String snapshotFilename =
            getRepositoryFullPath(
                type,
                project,
                MavenConstants.SNAPSHOT_SIGNIFIER);
        String timestampedFilename =
            getRepositoryFullPath(type, project, snapshotSignature);
        String snapshotVersionsFilename = 
            getRepositoryDirectoryPath(type, project)
                + project.getArtifactId()
                + "-snapshot-version";

        List destFiles = new ArrayList();
        destFiles.add(snapshotFilename);
        destFiles.add(snapshotFilename + ".md5");
        destFiles.add(timestampedFilename);
        destFiles.add(timestampedFilename + ".md5");
        destFiles.add(snapshotVersionsFilename);

        // do not deploy POM twice
        if (DEPLOY_POM_SNAPSHOT && !"pom".equals(type))
        {
            deploySnapshot(artifact, "pom", project);
        }

        doDeploy(srcFiles, destFiles, project);
        // Delete md5 file
        md5File.delete();
    };

    /**
     * @see ArtifactDeployer#install(String, String, Project)
     */
    public void install(String artifact, String type, Project project)
        throws MavenException
    {
        File file;
        if("pom".equals(type))
        {
            file = project.getFile(); 
        }
        else
        {
            file = getFileForArtifact(artifact);
        }
        doInstall(file, type, project, project.getCurrentVersion());
        // do not install twice
        if (INSTALL_POM && !"pom".equals(type))
        {
            doInstall(
                project.getFile(),
                "pom",
                project,
                project.getCurrentVersion());
        }
    }

    /**
     * @see ArtifactDeployer#installSnapshot(String, String, Project)
     */
    public void installSnapshot(String artifact, String type, Project project)
        throws MavenException
    {
        File file;
        if("pom".equals(type))
        {
            file = project.getFile(); 
        }
        else
        {
            file = getFileForArtifact(artifact);
        }
        String snapshotSignature = getSnapshotSignature();
        System.out.println("Installing snapshot of:'" + artifact + "''");
        doInstall(file, type, project, MavenConstants.SNAPSHOT_SIGNIFIER);
        doInstall(file, type, project, snapshotSignature);
        if (INSTALL_POM_SNAPSHOT && !"pom".equals(type))
        {
            File projectFile = project.getFile();
            doInstall(
                projectFile,
                "pom",
                project,
                MavenConstants.SNAPSHOT_SIGNIFIER);
            doInstall(projectFile, "pom", project, snapshotSignature);
        }
    }

    /**
     * Install given file in local repository 
     * @param artifact the artifact file to install 
     * @param type The type of the artiafct
     * @param project 
     * @param version String denominating the version of the artifact 
     * @throws MavenException
     */
    private void doInstall(
        File file,
        String type,
        Project project,
        String version)
        throws MavenException
    {
        try
        {
            File destFile =
                new File(
                    getLocalRepository(project),
                    getRepositoryFullPath(type, project, version));
            if (!destFile.getParentFile().exists())
            {
                destFile.getParentFile().mkdirs();
            }
            System.out.println(
                "Copying: from '" + file + "' to: '" + destFile + "'");
            FileUtils.copyFile(file, destFile);
        }
        catch (IOException e)
        {
            String msg =
                "Cannot install file: '"
                    + file
                    + "'. Reason: "
                    + e.getMessage();
            throw new MavenException(msg, e);
        }
    }

    /**
     * @param artifact
     * @param type
     * @param project
     * @param snapshot
         */
    private void doDeploy(List srcFiles, List destFiles, Project project)
        throws MavenException
    {

        DeployTool deployTool = new DeployTool();

        // trick add special values to context for default repository;

        String repoStr =
            (String) project.getContext().getVariable("maven.repo.list");

        if (repoStr == null || repoStr.length() == 0)
        {
            System.out.println("No remote repository was defined.");
            return;
        }
        String[] repos = StringUtils.split(repoStr, ",");

        System.out.println(
            "Will deploy to " + repos.length + " repository(ies): " + repoStr);
        for (int i = 0; i < repos.length; i++)
        {

            String repo = repos[i].trim();
            System.out.println("Deploying to repository: " + repo);
            RepositoryInfo repoInfo = null;
            try
            {
                repoInfo =
                    RepositoryInfoBuilder.getRepositoryInfo(project, repo);

                deployTool.deploy(repoInfo, srcFiles, destFiles);
            }
            catch (Exception e)
            {
                String msg =
                    "Failed to deploy to: "
                        + repoInfo.getRepositoryAlias()
                        + " Reason: "
                        + e.getMessage();
                System.out.print(msg);
                // deploy to next repository
                e.printStackTrace();
                continue;
            }

        }
    }

    /**
     * Return the local repository path form given project
     * @param project
     * @return The path to local repoository in local file system
     */
    private String getLocalRepository(Project project)
    {
        return project.getContext().getMavenRepoLocal();
    }

    /**
     * Return relative path from  repositorry root 
     * for given parameters
     * @param type Artifact type
     * @param project 
     * @param snapshot
     * @return
     * @todo replace this with RepoistoryLayout Service
     */
    private String getRepositoryFullPath(
        String type,
        Project project,
        String version)
    {
        StringBuffer path = new StringBuffer();
        path.append(project.getArtifactDirectory());
        path.append("/");
        path.append(type + "s");
        path.append("/");
        path.append(project.getArtifactId());
        path.append("-");
        path.append(version);
        path.append(".");
        path.append(extensionForType(type));
        return path.toString();
    }

    /**
     * Return relative path from repositorry root to a directory where 
     * given artifact will be stored 
     * @param type Artifact type
     * @param project 
     * @param snapshot
     * @return
     * @todo replace this with RepoistoryLayout Service
     */
    private String getRepositoryDirectoryPath(String type, Project project)
    {
        StringBuffer path = new StringBuffer();
        path.append(project.getArtifactDirectory());
        path.append("/");
        path.append(type + "s");
        path.append("/");
        return path.toString();
    }

    /**
     * 
     * @return
     */
    private String getSnapshotSignature()
    {
        Date now = new Date();
        return SNAPSHOT_SIGNATURE_FMT.format(now);
    }

    /**
     * 
     * @param artifact
     * @return
     */
    private File getFileForArtifact(String artifact) throws MavenException
    {
        File file = new File(artifact);
        if (!file.exists())
        {
            String msg = "Artifact file: '" + artifact + "' must exist";
            throw new MavenException(msg);
        }
        if (!file.canRead())
        {
            String msg = "Artifact file: '" + artifact + "' must be readable";
            throw new MavenException(msg);
        }
        if (file.isDirectory())
        {
            String msg =
                "Artifact file: '" + artifact + "' must not be a directory";
            throw new MavenException(msg);
        }
        return file.getAbsoluteFile();
    }

    /**
     * Create a file which contains timestamp of the latetst snapshot
     * 
     * @param snapshotVersion
     * @param project
     * @param type
     * @return
     */
    private File createSnapshotVersionFile(
        File artifact,
        String snapshotVersion,
        Project project,
        String type)
        throws MavenException
    {
        File file = null;
        String filename =
            project.getArtifactId() + "-" + type + "-snapshot-version";
        try
        {
            file = new File(artifact.getParent(), filename);
            FileUtils.fileWrite(file.getAbsolutePath(), snapshotVersion);

        }
        catch (Exception e)
        {
            throw new MavenException(
                "Cannot create snapshot-version file:" + file);
        }
        return file;
    }

    /**
     * Create MD5 checksum file for given file. File is created 
     * in the same folder
     * @param File
     * @return The <code>File</code> which contatins MD5 checksum 
     * @throws MavenException when opertaion failed 
     */
    private File createMD5Checksum(File file) throws MavenException
    {
        MD5Sum md5Sum = new MD5Sum();
        md5Sum.setFile(file);
        try
        {
            md5Sum.execute();
        }
        catch (Exception e)
        {

            throw new MavenException(
                "MD5 checksum error: " + e.getMessage(),
                e);
        }
        String checksum = md5Sum.getChecksum();

        File md5ChecksumFile = null;
        try
        {
            md5ChecksumFile = new File(file.getAbsoluteFile() + ".md5");
            FileUtils.fileWrite(md5ChecksumFile.getAbsolutePath(), checksum);
        }
        catch (Exception e)
        {
            throw new MavenException(
                "Cannot create md5 checksum file: " + md5ChecksumFile);

        }
        return md5ChecksumFile;
    }

    /**
     * Return file extension for given type
     * @todo Dirty hack Repository Layout Service from maven-new should be used     
     * @return extension for given type
     */
    private String extensionForType(String type)
    {
        if (type.equals("ejb"))
        {
            return "jar";
        }
        else if (type.equals("plugin"))
        {
            return "jar";
        }
        return type;
    }
}
