package com.atlassian.bitbucket.mesh.git.commit;

import com.atlassian.bitbucket.mesh.git.AbstractParameters;
import com.atlassian.bitbucket.mesh.git.builder.revlist.GitRevListOrder;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;

public class TraverseCommitsParameters extends AbstractParameters {

    private final boolean all;
    private final boolean branches;
    private final List<Repository> alternates;
    private final Set<String> excludes;
    private final boolean ignoreMissing;
    private final Set<String> includes;
    private final GitRevListOrder order;
    private final Repository repository;
    private final boolean tags;

    private TraverseCommitsParameters(Builder builder) {
        super(builder);

        all = builder.all;
        alternates = builder.alternates.build();
        branches = builder.branches;
        excludes = builder.excludes.build();
        ignoreMissing = builder.ignoreMissing;
        includes = builder.includes.build();
        order = builder.order;
        repository = builder.repository;
        tags = builder.tags;

        checkState(all || tags || branches || !includes.isEmpty() || !excludes.isEmpty(),
                "at least one include or exclude must be provided");
    }

    @Nonnull
    public List<Repository> getAlternates() {
        return alternates;
    }

    public boolean isAll() {
        return all;
    }

    public boolean isBranches() {
        return branches;
    }

    public boolean isIgnoreMissing() {
        return ignoreMissing;
    }

    public boolean isTags() {
        return tags;
    }

    @Nonnull
    public Set<String> getExcludes() {
        return excludes;
    }

    @Nonnull
    public Set<String> getIncludes() {
        return includes;
    }

    @Nonnull
    public GitRevListOrder getOrder() {
        return order;
    }

    @Nonnull
    public Repository getRepository() {
        return repository;
    }

    public static class Builder extends AbstractBuilder<Builder, TraverseCommitsParameters> {

        private final ImmutableList.Builder<Repository> alternates;
        private final ImmutableSet.Builder<String> excludes;
        private final ImmutableSet.Builder<String> includes;
        private final Repository repository;

        private boolean all;
        private boolean branches;
        private boolean ignoreMissing;
        private GitRevListOrder order;
        private boolean tags;

        public Builder(@Nonnull Repository repository) {
            this.repository = requireNonNull(repository, "repository");

            alternates = ImmutableList.builder();
            excludes = ImmutableSet.builder();
            includes = ImmutableSet.builder();
            order = GitRevListOrder.TOPOLOGICAL;
        }

        @Nonnull
        public Builder all(boolean value) {
            all = value;
            return self();
        }

        @Nonnull
        public Builder alternate(@Nullable Iterable<Repository> values) {
            addIf(Objects::nonNull, alternates, values);
            return self();
        }

        @Nonnull
        public Builder alternate(@Nullable Repository value, @Nullable Repository... moreValues) {
            addIf(Objects::nonNull, alternates, value, moreValues);
            return self();
        }

        @Nonnull
        public Builder branches(boolean value) {
            branches = value;
            return self();
        }

        @Nonnull
        @Override
        public TraverseCommitsParameters build() {
            return new TraverseCommitsParameters(this);
        }

        @Nonnull
        public Builder exclude(@Nullable Iterable<String> values) {
            addIf(Objects::nonNull, excludes, values);
            return self();
        }

        @Nonnull
        public Builder excludes(@Nullable String value, String... moreValues) {
            addIf(Objects::nonNull, excludes, value, moreValues);
            return self();
        }

        @Nonnull
        public Builder ignoreMissing(boolean value) {
            ignoreMissing = value;
            return self();
        }

        @Nonnull
        public Builder include(@Nullable Iterable<String> values) {
            addIf(Objects::nonNull, includes, values);
            return self();
        }

        @Nonnull
        public Builder include(@Nullable String value, String... moreValues) {
            addIf(Objects::nonNull, includes, value, moreValues);
            return self();
        }

        @Nonnull
        public Builder order(@Nonnull GitRevListOrder value) {
            order = requireNonNull(value, "order");
            return self();
        }

        @Nonnull
        public Builder tags(boolean value) {
            tags = value;
            return self();
        }

        @Nonnull
        @Override
        protected Builder self() {
            return this;
        }
    }
}
