package org.implab.gradle.variants.artifacts.internal;

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.model.ObjectFactory;
import org.implab.gradle.common.core.lang.Strings;
import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec;
import org.implab.gradle.variants.core.Layer;
import org.implab.gradle.variants.core.Role;
import org.implab.gradle.variants.artifacts.OutputSelectionSpec;

/**
 * Реализация DSL модели, строит набор {@link SlotContribution}. При построении набора
 * правила и корректность не проверяются. По окончании использования клиент
 * вызывает метод {@link #process(Consumer)} для обработки результатов.
 *
 */
final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec {
    private final Consumer<? super SlotContribution> consumer;
    private final ObjectFactory objectFactory;

    DefaultArtifactAssemblySpec(ObjectFactory objectFactory, Consumer<? super SlotContribution> consumer) {
        this.consumer = consumer;
        this.objectFactory = objectFactory;
    }

    @Override
    public void from(Object artifact) {
        consumer.accept(new DirectContribution(artifact));
    }

    @Override
    public void fromVariant(Action<? super OutputSelectionSpec> action) {
        consumer.accept(new VariantOutputsContribution(outputs(action)));
    }

    @Override
    public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) {

        consumer.accept(new RoleOutputsContribution(
                objectFactory.named(Role.class, roleName),
                outputs(action)));
    }

    @Override
    public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) {
        consumer.accept(new LayerOutputsContribution(
                objectFactory.named(Layer.class, layerName),
                outputs(action)));
    }

    private static Set<String> outputs(Action<? super OutputSelectionSpec> action) {
        var spec = new OutputsSetSpec();
        action.execute(spec);
        return spec.outputs();
    }

    private static class OutputsSetSpec implements OutputSelectionSpec {
        private final Set<String> outputs = new HashSet<>();

        @Override
        public void output(String name, String... extra) {
            Stream.concat(Stream.of(name), Stream.of(extra))
                .map(Strings::requireNonBlank)
                .forEach(outputs::add);
        }

        Set<String> outputs() {
            return Set.copyOf(outputs);
        }

    }
}
