package com.atlassian.bitbucket.mesh.util;

import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.Iterables;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Predicate;

import static java.util.Objects.requireNonNull;

public class BuilderSupport {

    protected static final Predicate<String> NOT_BLANK = StringUtils::isNotBlank;

    protected static <T> boolean addIf(@Nonnull Predicate<? super T> predicate, @Nonnull Collection<T> collection,
                                       @Nullable T value) {
        return predicate.test(value) && collection.add(value);
    }

    protected static <T> boolean addIf(@Nonnull Predicate<? super T> predicate, @Nonnull ImmutableCollection.Builder<T> builder,
                                       @Nullable T value) {
        if (predicate.test(value)) {
            //noinspection ConstantConditions
            builder.add(value);

            return true;
        }
        return false;
    }

    /**
     * @param predicate  verifies elements prior to adding them to the collection
     * @param collection the collection to which elements accepted by the predicate should be added
     * @param value      the first element to add
     * @param values     a varargs array containing 0 or more elements to add after the first
     * @param <T>        type for the builder
     */
    @SafeVarargs
    protected static <T> void addIf(@Nonnull Predicate<? super T> predicate, @Nonnull Collection<T> collection,
                                    @Nullable T value, @Nullable T... values) {
        if (addIf(predicate, collection, value) && values != null && values.length > 0) {
            addIf(predicate, collection, Arrays.asList(values));
        }
    }

    @SafeVarargs
    protected static <T> void addIf(@Nonnull Predicate<? super T> predicate, @Nonnull ImmutableCollection.Builder<T> builder,
                                    @Nullable T value, @Nullable T... values) {
        if (addIf(predicate, builder, value) && values != null && values.length > 0) {
            addIf(predicate, builder, Arrays.asList(values));
        }
    }

    protected static <T> void addIf(@Nonnull Predicate<? super T> predicate, @Nonnull Collection<T> collection,
                                    @Nullable Iterable<? extends T> values) {
        if (values != null) {
            Iterables.addAll(collection, Iterables.filter(values, predicate::test));
        }
    }

    protected static <T> void addIf(@Nonnull Predicate<? super T> predicate, @Nonnull ImmutableCollection.Builder<T> builder,
                                    @Nullable Iterable<? extends T> values) {
        if (values != null) {
            builder.addAll(Iterables.filter(values, predicate::test));
        }
    }

    /**
     * @param value the value to ensure is not {@code null} or blank.
     * @param name  a name for the value, used in exception messages
     * @return the provided {@code value}, if it is not {@code null} or blank
     * @throws IllegalArgumentException if the provided {@code value} is blank
     * @throws NullPointerException if the provided {@code value} is {@code null}
     */
    @Nonnull
    protected static String requireNonBlank(@Nullable String value, @Nullable String name) {
        requireNonNull(value, name);
        if (value.trim().isEmpty()) {
            throw new IllegalArgumentException("A non-blank " + StringUtils.defaultString(name, "value") + " is required");
        }

        return value;
    }
}