/*
 * 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.scm.client.cli;

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

import org.apache.maven.scm.ScmBranch;
import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFile;
import org.apache.maven.scm.ScmFileSet;
import org.apache.maven.scm.ScmResult;
import org.apache.maven.scm.ScmRevision;
import org.apache.maven.scm.ScmTag;
import org.apache.maven.scm.ScmVersion;
import org.apache.maven.scm.command.checkin.CheckInScmResult;
import org.apache.maven.scm.command.checkout.CheckOutScmResult;
import org.apache.maven.scm.command.update.UpdateScmResult;
import org.apache.maven.scm.manager.NoSuchScmProviderException;
import org.apache.maven.scm.manager.ScmManager;
import org.apache.maven.scm.repository.ScmRepository;
import org.apache.maven.scm.repository.ScmRepositoryException;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.PlexusContainerException;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.DefaultContext;

/**
 * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
 */
public class MavenScmCli {
    private PlexusContainer plexus;

    private ScmManager scmManager;

    // ----------------------------------------------------------------------
    // Lifecycle
    // ----------------------------------------------------------------------

    public MavenScmCli() throws Exception {
        plexus = createPlexusContainer();
        scmManager = plexus.lookup(ScmManager.class);
    }

    private PlexusContainer createPlexusContainer() {
        final Context context = new DefaultContext();
        String path = System.getProperty("basedir");
        if (path == null) {
            path = new File("").getAbsolutePath();
        }
        context.put("basedir", path);

        ContainerConfiguration plexusConfiguration = new DefaultContainerConfiguration();
        plexusConfiguration
                .setName("maven-scm-cli")
                .setContext(context.getContextData())
                .setClassPathScanning(PlexusConstants.SCANNING_CACHE)
                .setAutoWiring(true);
        try {
            return new DefaultPlexusContainer(plexusConfiguration);
        } catch (PlexusContainerException e) {
            throw new IllegalStateException("Could not create Plexus container", e);
        }
    }

    public void stop() {
        try {
            plexus.dispose();
        } catch (RuntimeException ex) {
            // ignore
        }
    }

    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    public static void main(String[] args) {
        MavenScmCli cli;

        try {
            cli = new MavenScmCli();
        } catch (Exception ex) {
            System.err.println("Error while starting Maven SCM.");

            ex.printStackTrace(System.err);

            return;
        }

        String scmUrl;

        String command;

        if (args.length < 3) {
            System.err.println(
                    "Usage: maven-scm-client <command> <working directory> <scm url> [<scmVersion> [<scmVersionType>]]");
            System.err.println("scmVersion is a branch name/tag name/revision number.");
            System.err.println(
                    "scmVersionType can be 'branch', 'tag', 'revision'. " + "The default value is 'revision'.");

            return;
        }

        command = args[0];

        // SCM-641
        File workingDirectory = new File(args[1]).getAbsoluteFile();

        scmUrl = args[2];

        ScmVersion scmVersion = null;
        if (args.length > 3) {
            String version = args[3];

            if (args.length > 4) {
                String type = args[4];

                if ("tag".equals(type)) {
                    scmVersion = new ScmTag(version);
                } else if ("branch".equals(type)) {
                    scmVersion = new ScmBranch(version);
                } else if ("revision".equals(type)) {
                    scmVersion = new ScmRevision(version);
                } else {
                    throw new IllegalArgumentException("'" + type + "' version type isn't known.");
                }
            } else {
                scmVersion = new ScmRevision(args[3]);
            }
        }

        cli.execute(scmUrl, command, workingDirectory, scmVersion);

        cli.stop();
    }

    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    public void execute(String scmUrl, String command, File workingDirectory, ScmVersion version) {
        ScmRepository repository;

        try {
            repository = scmManager.makeScmRepository(scmUrl);
        } catch (NoSuchScmProviderException ex) {
            System.err.println("Could not find a provider.");

            return;
        } catch (ScmRepositoryException ex) {
            System.err.println("Error while connecting to the repository");

            ex.printStackTrace(System.err);

            return;
        }

        try {
            if (command.equals("checkout")) {
                checkOut(repository, workingDirectory, version);
            } else if (command.equals("checkin")) {
                checkIn(repository, workingDirectory, version);
            } else if (command.equals("update")) {
                update(repository, workingDirectory, version);
            } else {
                System.err.println("Unknown SCM command '" + command + "'.");
            }
        } catch (ScmException ex) {
            System.err.println("Error while executing the SCM command.");

            ex.printStackTrace(System.err);
        }
    }

    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    private void checkOut(ScmRepository scmRepository, File workingDirectory, ScmVersion version) throws ScmException {
        if (workingDirectory.exists()) {
            System.err.println("The working directory already exist: '" + workingDirectory.getAbsolutePath() + "'.");

            return;
        }

        if (!workingDirectory.mkdirs()) {
            System.err.println(
                    "Error while making the working directory: '" + workingDirectory.getAbsolutePath() + "'.");

            return;
        }

        CheckOutScmResult result = scmManager.checkOut(scmRepository, new ScmFileSet(workingDirectory), version);

        if (!result.isSuccess()) {
            showError(result);

            return;
        }

        List<ScmFile> checkedOutFiles = result.getCheckedOutFiles();

        System.out.println("Checked out these files: ");

        for (ScmFile file : checkedOutFiles) {
            System.out.println(" " + file.getPath());
        }
    }

    private void checkIn(ScmRepository scmRepository, File workingDirectory, ScmVersion version) throws ScmException {
        if (!workingDirectory.exists()) {
            System.err.println("The working directory doesn't exist: '" + workingDirectory.getAbsolutePath() + "'.");

            return;
        }

        String message = "";

        CheckInScmResult result = scmManager.checkIn(scmRepository, new ScmFileSet(workingDirectory), version, message);

        if (!result.isSuccess()) {
            showError(result);

            return;
        }

        List<ScmFile> checkedInFiles = result.getCheckedInFiles();

        System.out.println("Checked in these files: ");

        for (ScmFile file : checkedInFiles) {
            System.out.println(" " + file.getPath());
        }
    }

    private void update(ScmRepository scmRepository, File workingDirectory, ScmVersion version) throws ScmException {
        if (!workingDirectory.exists()) {
            System.err.println("The working directory doesn't exist: '" + workingDirectory.getAbsolutePath() + "'.");

            return;
        }

        UpdateScmResult result = scmManager.update(scmRepository, new ScmFileSet(workingDirectory), version);

        if (!result.isSuccess()) {
            showError(result);

            return;
        }

        List<ScmFile> updatedFiles = result.getUpdatedFiles();

        System.out.println("Updated these files: ");

        for (ScmFile file : updatedFiles) {
            System.out.println(" " + file.getPath());
        }
    }

    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    private void showError(ScmResult result) {
        System.err.println("There was a error while executing the SCM command.");

        String providerMessage = result.getProviderMessage();

        if (!(providerMessage == null || providerMessage.isEmpty())) {
            System.err.println("Error message from the provider: " + providerMessage);
        } else {
            System.err.println("The provider didn't give a error message.");
        }

        String output = result.getCommandOutput();

        if (!(output == null || output.isEmpty())) {
            System.err.println("Command output:");

            System.err.println(output);
        }
    }
}
