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

import java.util.HashMap;
import java.util.Map;
import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectProvider;
import org.implab.gradle.common.sources.GenericSourceSet;
import org.implab.gradle.variants.core.Layer;
import org.implab.gradle.variants.core.Variant;
import org.implab.gradle.variants.core.VariantsView;
import org.implab.gradle.variants.sources.CompileUnit;
import org.implab.gradle.variants.sources.CompileUnitsView;
import org.implab.gradle.variants.sources.RoleProjectionsView;
import org.implab.gradle.variants.sources.SourceSetMaterializer;
import org.implab.gradle.variants.sources.VariantSourcesContext;

public class DefaultVariantSourcesContext implements VariantSourcesContext {
    private final VariantsView variantsView;
    private final CompileUnitsView compileUnitsView;
    private final RoleProjectionsView roleProjectionsView;
    private final SourceSetMaterializer sourceSetMaterializer;
    private final SourceSetRegistry sourceSetRegistry;
    private final CompileUnitNamer compileUnitNamer;
    private final SourceSetConfigurationRegistry sourceSetConfigurationRegistry;

    public DefaultVariantSourcesContext(
            VariantsView variantsView,
            CompileUnitsView compileUnitsView,
            RoleProjectionsView roleProjectionsView,
            CompileUnitNamer compileUnitNamer,
            SourceSetRegistry sourceSetRegistry,
            SourceSetConfigurationRegistry sourceSetConfigurationRegistry) {
        this.variantsView = variantsView;
        this.compileUnitNamer = compileUnitNamer;
        this.compileUnitsView = compileUnitsView;
        this.roleProjectionsView = roleProjectionsView;
        this.sourceSetRegistry = sourceSetRegistry;
        this.sourceSetConfigurationRegistry = sourceSetConfigurationRegistry;

        sourceSetMaterializer = new LocalSourceSetMaterializer();
    }

    @Override
    public VariantsView getVariants() {
        return variantsView;
    }

    @Override
    public CompileUnitsView getCompileUnits() {
        return compileUnitsView;
    }

    @Override
    public RoleProjectionsView getRoleProjections() {
        return roleProjectionsView;
    }

    @Override
    public SourceSetMaterializer getSourceSets() {
        return sourceSetMaterializer;
    }

    @Override
    public void configureLayer(Layer layer, Action<? super GenericSourceSet> action) {
        sourceSetConfigurationRegistry.addLayerAction(layer, action);
    }

    @Override
    public void configureVariant(Variant variant, Action<? super GenericSourceSet> action) {
        sourceSetConfigurationRegistry.addVariantAction(variant, action);
    }

    @Override
    public void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action) {
        sourceSetConfigurationRegistry.addCompileUnitAction(unit, action);
    }

    class LocalSourceSetMaterializer implements SourceSetMaterializer {
        private final Map<CompileUnit, NamedDomainObjectProvider<GenericSourceSet>> registeredSources = new HashMap<>();

        @Override
        public NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit) {
            return registeredSources.computeIfAbsent(unit, k -> {
                var sourcesName = compileUnitNamer.resolveName(unit);
                sourceSetRegistry.whenMaterialized(sourcesName,
                        sourceSet -> sourceSetConfigurationRegistry.applyConfiguration(unit, sourceSet));
                return sourceSetRegistry.sourceSets().register(sourcesName);
            });
        }

    }
}
