package com.atlassian.bitbucket.scm.git.command;

import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.Command;
import com.atlassian.bitbucket.scm.CommandOutputHandler;
import com.atlassian.bitbucket.scm.ScmCommandBuilder;
import com.atlassian.bitbucket.scm.git.command.add.GitAddBuilder;
import com.atlassian.bitbucket.scm.git.command.blame.GitBlame;
import com.atlassian.bitbucket.scm.git.command.branch.GitBranch;
import com.atlassian.bitbucket.scm.git.command.catfile.GitCatFile;
import com.atlassian.bitbucket.scm.git.command.checkrefformat.GitCheckRefFormat;
import com.atlassian.bitbucket.scm.git.command.clone.GitClone;
import com.atlassian.bitbucket.scm.git.command.commit.GitCommitBuilder;
import com.atlassian.bitbucket.scm.git.command.config.GitConfig;
import com.atlassian.bitbucket.scm.git.command.diff.GitDiffBuilder;
import com.atlassian.bitbucket.scm.git.command.diff.GitDiffTreeBuilder;
import com.atlassian.bitbucket.scm.git.command.fetch.GitFetchBuilder;
import com.atlassian.bitbucket.scm.git.command.foreachref.GitForEachRefBuilder;
import com.atlassian.bitbucket.scm.git.command.gc.GitGcBuilder;
import com.atlassian.bitbucket.scm.git.command.init.GitInitBuilder;
import com.atlassian.bitbucket.scm.git.command.log.GitLogBuilder;
import com.atlassian.bitbucket.scm.git.command.lsfiles.GitLsFilesBuilder;
import com.atlassian.bitbucket.scm.git.command.lstree.GitLsTreeBuilder;
import com.atlassian.bitbucket.scm.git.command.merge.GitMerge;
import com.atlassian.bitbucket.scm.git.command.merge.GitMergeBase;
import com.atlassian.bitbucket.scm.git.command.notes.GitNotes;
import com.atlassian.bitbucket.scm.git.command.packobjects.GitPackObjectsBuilder;
import com.atlassian.bitbucket.scm.git.command.prunepacked.GitPrunePackedBuilder;
import com.atlassian.bitbucket.scm.git.command.push.GitPushBuilder;
import com.atlassian.bitbucket.scm.git.command.remote.GitRemote;
import com.atlassian.bitbucket.scm.git.command.reset.GitResetBuilder;
import com.atlassian.bitbucket.scm.git.command.revlist.GitRevListBuilder;
import com.atlassian.bitbucket.scm.git.command.rm.GitRmBuilder;
import com.atlassian.bitbucket.scm.git.command.status.GitStatusBuilder;
import com.atlassian.bitbucket.scm.git.command.symbolicref.GitSymbolicRef;
import com.atlassian.bitbucket.scm.git.command.tag.GitTag;
import com.atlassian.bitbucket.scm.git.command.updateref.GitUpdateRef;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;

/**
 * Augments the free-form {@link ScmCommandBuilder} with git-specific type-safe builders, to simplify using well-known
 * commands.
 * <p>
 * Type-safe builders are not provided for all of the commands supported by the {@code git} binary, and the builders
 * that are provided may not expose properties for all of the different arguments or argument combinations the command
 * offers. Free-form and type-safe builder semantics <i>may not be mixed</i>. If a type-safe builder does not exist for
 * the desired command, or it does not support the desired arguments, the free-form builder must be used <i>instead</i>.
 * <p>
 * For type-safe builders provided, the {@code git} command associated with the builder is used to name the interface
 * and the method. Note that some commands accept arguments which, when provided, enable the use of other arguments, or
 * trigger the command to produce significantly different output. For such commands, like {@link #blame() blame} and
 * {@link #catFile() cat-file}, an interim interface is returned to allow the caller to choose the "mode" the command
 * will run in. Once a mode has been chosen, a builder will be returned exposing arguments supported for that mode.
 */
@NotThreadSafe
public interface GitScmCommandBuilder
        extends GitCommandBuilderSupport<GitScmCommandBuilder>, ScmCommandBuilder<GitScmCommandBuilder> {

    @Nonnull
    GitAddBuilder add();

    //TODO: Remove in 5.0
    /**
     * Configures the command to run with the provided alternate repositories, giving the command access to
     * all objects in those repositories.
     * <p>
     * Note: A binary-incompatible change will be introduced in 5.0 when this method is removed from this interface. It
     * has been moved to {@link GitCommandBuilderSupport#alternates}, so no code changes are necessary to <i>compile</i>
     * for 5.0, but plugins which use this method <i>will</i> need to be recompiled for binary compatibility.
     *
     * @param alternates a collection of alternate repositories
     * @return the command builder
     * @since 4.4
     */
    @Nonnull
    @Override
    GitScmCommandBuilder alternates(@Nonnull Iterable<Repository> alternates);

    @Nonnull
    GitBlame blame();

    @Nonnull
    GitBranch branch();

    @Nonnull
    @Override
    <T> GitCommand<T> build(@Nonnull CommandOutputHandler<T> outputHandler);

    @Nonnull
    GitCatFile catFile();

    /**
     * @return a type-safe parent builder for the {@code git check-ref-format} command
     */
    @Nonnull
    GitCheckRefFormat checkRefFormat();

    @Nonnull
    GitClone clone();

    @Nonnull
    GitCommitBuilder commit();

    @Nonnull
    GitConfig config();

    @Nonnull
    GitDiffBuilder diff();

    @Nonnull
    GitDiffTreeBuilder diffTree();

    @Nonnull
    GitFetchBuilder fetch();

    @Nonnull
    GitForEachRefBuilder forEachRef();

    @Nonnull
    GitGcBuilder gc();

    @Nonnull
    GitInitBuilder init();

    /**
     * @since 4.5
     */
    @Nonnull
    GitLogBuilder log();

    @Nonnull
    GitLsFilesBuilder lsFiles();

    @Nonnull
    GitLsTreeBuilder lsTree();

    @Nonnull
    GitMerge merge();
    
    @Nonnull
    GitMergeBase mergeBase();

    @Nonnull
    GitNotes notes();

    @Nonnull
    GitPackObjectsBuilder packObjects();

    @Nonnull
    GitPrunePackedBuilder prunePacked();

    @Nonnull
    GitPushBuilder push();

    @Nonnull
    GitRemote remote();

    @Nonnull
    GitResetBuilder reset();

    @Nonnull
    GitRevListBuilder revList();

    @Nonnull
    GitRmBuilder rm();

    @Nonnull
    GitStatusBuilder status();

    @Nonnull
    GitSymbolicRef symbolicRef();

    /**
     * @return a builder for the {@code git tag} command
     */
    @Nonnull
    GitTag tag();

    @Nonnull
    GitUpdateRef updateRef();

    @Nonnull
    <T> Command<T> version(@Nonnull CommandOutputHandler<T> outputHandler);
}
