package org.implab.gradle.common.core.gradle;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.gradle.api.Action;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Provider;

public final class Properties {
    private Properties() {
    }

    public static <K> void mergeMap(MapProperty<K, Object> property, Map<K, Object> map) {
        mergeMap(property, map, Function.identity());
    }

    public static <K, V> void putMapEntry(MapProperty<K, V> property, K key, Object value,
            Function<Object, ? extends V> mapper) {
        if (value instanceof Provider<?>)
            property.put(key, ((Provider<?>) value).map(mapper::apply));
        else
            property.put(key, mapper.apply(value));
    }

    public static <K> void putMapEntry(MapProperty<K, Object> property, K key, Object value) {
        putMapEntry(property, key, value, Function.identity());
    }

    public static <K, V> void putMapEntry(MapProperty<K, V> property, K key, Object value, Class<V> valueType) {
        putMapEntry(property, key, value, valueType::cast);
    }

    public static <K, V> void mergeMap(MapProperty<K, V> property, Map<K, ?> map,
            Function<Object, ? extends V> mapper) {
        map.forEach((k, v) -> {
            if (v instanceof Provider<?>)
                property.put(k, ((Provider<?>) v).map(mapper::apply));
            else
                property.put(k, mapper.apply(v));
        });
    }

    public static void mergeList(ListProperty<Object> property, Iterable<Object> values) {
        mergeList(property, values, Function.identity());
    }

    public static <V> void mergeList(ListProperty<V> property, Iterable<Object> values,
            Function<Object, ? extends V> mapper) {
        values.forEach(v -> {
            if (v instanceof Provider<?>)
                property.add(((Provider<?>) v).map(mapper::apply));
            else
                property.add(mapper.apply(v));
        });
    }

    public static <K> void configureMap(MapProperty<K, Object> prop, Action<? super Map<K, Object>> configure) {
        configureMap(prop, configure, Function.identity());
    }

    public static <K, V> void configureMap(MapProperty<K, V> prop, Action<? super Map<K, Object>> configure,
            Function<Object, V> mapper) {
        var map = new HashMap<K, Object>();
        configure.execute(map);
        mergeMap(prop, map, mapper);
    }
}
