package org.implab.gradle.variants.core;

import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;

import org.gradle.api.Action;
import org.gradle.api.Named;
import org.gradle.api.provider.SetProperty;
import org.implab.gradle.common.core.lang.Closures;
import org.implab.gradle.common.core.lang.Strings;

import groovy.lang.Closure;

public interface VariantDefinition extends Named {
    /**
     * Role bindings declared inside this variant.
     *
     * The binding pair of role and layer names.
     */
    SetProperty<RoleLayerBinding> getRoleBindings();

    /**
     * Creates or returns an existing role binding and configures it.
     */
    default void role(String name, Action<? super RoleSpec> action) {
        var spec = new RoleSpec(name);
        action.execute(spec);
        spec.accept(getRoleBindings()::add);
    }

    default void role(String name, Closure<?> closure) {
        role(name, Closures.action(closure));
    }

    default void finalizeVariant() {
        getRoleBindings().finalizeValue();
    }

    public static class RoleSpec {
        private final String name;
        private final Set<String> layerNames;

        public RoleSpec(String name) {
            this.name = name;
            this.layerNames = new HashSet<>();
        }

        public String getName() {
            return name;
        }

        public Set<String> getLayerNames() {
            return layerNames;
        }

        public void layers(String name, String... extraNames) {
            Stream.concat(Stream.of(name), Stream.of(extraNames))
                .map(Strings::requireNonBlank)
                .forEach(this.layerNames::add);
        }

        void accept(Consumer<? super RoleLayerBinding> consumer) {
            layerNames.stream()
                    .map(layerName -> new RoleLayerBinding(name, layerName))
                    .forEach(consumer);
        }
    }
}
