package org.implab.gradle.common.utils;

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.gradle.api.provider.Provider;

public final class Values {

    private Values() {
    }

    /**
     * Converts values in the specified map
     * @param <K>
     * @param <V>
     * @param <U>
     * @param map
     * @param mapper
     * @return
     */
    public static <K, V, U> Map<K, U> mapValues(Map<K, V> map, Function<V, U> mapper) {
        Function<Entry<K, V>, V> getter = Entry::getValue;

        return map.entrySet().stream()
                .collect(Collectors.toMap(Entry::getKey, getter.andThen(mapper)));
    }

    /** Converts the supplied value to a string.
     */
    public static String toString(Object value) {
        if (value == null) {
            return null; 
         } else if (value instanceof String string) {
            return string;
        } else if (value instanceof Provider<?> provider) {
            return toString(provider.getOrNull());
        } else if (value instanceof Supplier<?> supplier) {
            return toString(supplier.get());
        } else {
            return value.toString();
        }
    }

    public static <T> Iterable<T> iterable(T[] values) {
        return () -> new ArrayIterator<>(values);
    }

    private static class ArrayIterator<T> implements Iterator<T> {
        private final T[] data;

        private int pos = 0;

        ArrayIterator(T[] data) {
            this.data = data;
        }

        @Override
        public boolean hasNext() {
            return pos < data.length;
        }

        @Override
        public T next() {
            return data[pos++];
        }
    }

}
