/*
 * Copyright 2017 the original author or authors.
 *
 * 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.
 */

package org.gradle.api.internal.provider;

import org.gradle.api.Transformer;
import org.gradle.api.provider.Provider;
import org.gradle.internal.Cast;

import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class Providers {
    public static final String NULL_TRANSFORMER_RESULT = "Transformer for this provider returned a null value.";
    public static final String NULL_VALUE = "No value has been specified for this provider.";

    private static final Provider<Object> NULL_PROVIDER = new AbstractMinimalProvider<Object>() {
        @Override
        public Object get() {
            throw new IllegalStateException(NULL_VALUE);
        }

        @Nullable
        @Override
        public Class<Object> getType() {
            return null;
        }

        @Override
        public Object getOrNull() {
            return null;
        }

        @Override
        public Object getOrElse(Object defaultValue) {
            return defaultValue;
        }

        @Override
        public <S> ProviderInternal<S> map(Transformer<? extends S, ? super Object> transformer) {
            return Cast.uncheckedCast(this);
        }

        @Override
        public boolean isPresent() {
            return false;
        }

        @Override
        public String toString() {
            return "undefined";
        }
    };

    public static final Provider<Boolean> TRUE = of(true);
    public static final Provider<Boolean> FALSE = of(false);

    public static <T> ProviderInternal<T> notDefined() {
        return Cast.uncheckedCast(NULL_PROVIDER);
    }

    public static <T> ProviderInternal<T> of(T value) {
        return new FixedValueProvider<T>(value);
    }

    public static <T> CollectionProviderInternal<T, Collection<T>> collectionOf(final T... values) {
        return new CollectionProviderInternal<T, Collection<T>>() {
            @Nullable
            @Override
            public Class<Collection<T>> getType() {
                return Cast.uncheckedCast(List.class);
            }

            @Override
            public boolean maybeVisitBuildDependencies(TaskDependencyResolveContext context) {
                return false;
            }

            @Override
            public Collection<T> get() {
                return Arrays.asList(values);
            }

            @Nullable
            @Override
            public Collection<T> getOrNull() {
                return get();
            }

            @Override
            public Collection<T> getOrElse(Collection<T> defaultValue) {
                return get();
            }

            @Override
            public <S> ProviderInternal<S> map(Transformer<? extends S, ? super Collection<T>> transformer) {
                throw new UnsupportedOperationException();
            }

            @Override
            public boolean isPresent() {
                return values.length > 0;
            }

            @Override
            public Class<? extends T> getElementType() {
                if (values.length == 0) {
                    throw new UnsupportedOperationException("Need to have at least one element");
                }
                return Cast.uncheckedCast(values[0].getClass());
            }

            @Override
            public int size() {
                return values.length;
            }
        };
    }

    public static <T> ProviderInternal<T> internal(final Provider<T> value) {
        return Cast.uncheckedCast(value);
    }

    private static class FixedValueProvider<T> extends AbstractMinimalProvider<T> {
        private final T value;

        FixedValueProvider(T value) {
            this.value = value;
        }

        @Nullable
        @Override
        public Class<T> getType() {
            return Cast.uncheckedCast(value.getClass());
        }

        @Override
        public T get() {
            return value;
        }

        @Override
        public T getOrElse(T defaultValue) {
            return value;
        }

        @Override
        public T getOrNull() {
            return value;
        }

        @Override
        public boolean isPresent() {
            return true;
        }

        @Override
        public <S> ProviderInternal<S> map(final Transformer<? extends S, ? super T> transformer) {
            return new MappedFixedValueProvider<S, T>(transformer, this);
        }

        @Override
        public String toString() {
            return String.format("fixed(%s, %s)", getType(), value);
        }
    }

    private static class MappedFixedValueProvider<S, T> extends AbstractMinimalProvider<S> {
        private final Transformer<? extends S, ? super T> transformer;
        private final Provider<T> provider;
        private S value;

        MappedFixedValueProvider(Transformer<? extends S, ? super T> transformer, Provider<T> provider) {
            this.transformer = transformer;
            this.provider = provider;
        }

        @Nullable
        @Override
        public Class<S> getType() {
            if (value != null) {
                return Cast.uncheckedCast(value.getClass());
            }
            return null;
        }

        @Override
        public boolean isPresent() {
            return true;
        }

        @Override
        public S get() {
            if (value == null) {
                value = transformer.transform(provider.get());
                if (value == null) {
                    throw new IllegalStateException(NULL_TRANSFORMER_RESULT);
                }
            }
            return value;
        }

        @Override
        public S getOrElse(S defaultValue) {
            return get();
        }

        @Nullable
        @Override
        public S getOrNull() {
            return get();
        }

        @Override
        public <U> ProviderInternal<U> map(Transformer<? extends U, ? super S> transformer) {
            return new MappedFixedValueProvider<U, S>(transformer, this);
        }

        @Override
        public String toString() {
            if (value == null) {
                return String.format("transform(not calculated)");
            }
            return String.format("transform(%s, %s)", getType(), value);
        }
    }
}

@@ -2361,1672 +2361,8 @@
  %7D%0A%0A
-    public static %3CT%3E CollectionProviderInternal%3CT, Collection%3CT%3E%3E collectionOf(final T... values) %7B%0A        return new CollectionProviderInternal%3CT, Collection%3CT%3E%3E() %7B%0A            @Nullable%0A            @Override%0A            public Class%3CCollection%3CT%3E%3E getType() %7B%0A                return Cast.uncheckedCast(List.class);%0A            %7D%0A%0A            @Override%0A            public boolean maybeVisitBuildDependencies(TaskDependencyResolveContext context) %7B%0A                return false;%0A            %7D%0A%0A            @Override%0A            public Collection%3CT%3E get() %7B%0A                return Arrays.asList(values);%0A            %7D%0A%0A            @Nullable%0A            @Override%0A            public Collection%3CT%3E getOrNull() %7B%0A                return get();%0A            %7D%0A%0A            @Override%0A            public Collection%3CT%3E getOrElse(Collection%3CT%3E defaultValue) %7B%0A                return get();%0A            %7D%0A%0A            @Override%0A            public %3CS%3E ProviderInternal%3CS%3E map(Transformer%3C? extends S, ? super Collection%3CT%3E%3E transformer) %7B%0A                throw new UnsupportedOperationException();%0A            %7D%0A%0A            @Override%0A            public boolean isPresent() %7B%0A                return values.length %3E 0;%0A            %7D%0A%0A            @Override%0A            public Class%3C? extends T%3E getElementType() %7B%0A                if (values.length == 0) %7B%0A                    throw new UnsupportedOperationException(%22Need to have at least one element%22);%0A                %7D%0A                return Cast.uncheckedCast(values%5B0%5D.getClass());%0A            %7D%0A%0A            @Override%0A            public int size() %7B%0A                return values.length;%0A            %7D%0A        %7D;%0A    %7D%0A%0A
     

Remove Providers.collectionOf