001/*
002 * JDrupes Builder
003 * Copyright (C) 2026 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.core;
020
021import java.util.EnumSet;
022import java.util.Set;
023import java.util.concurrent.atomic.AtomicReference;
024import java.util.function.Consumer;
025import java.util.function.Predicate;
026import java.util.stream.Stream;
027import org.jdrupes.builder.api.Intent;
028import static org.jdrupes.builder.api.Intent.Consume;
029import static org.jdrupes.builder.api.Intent.Expose;
030import static org.jdrupes.builder.api.Intent.Supply;
031import org.jdrupes.builder.api.Project;
032import org.jdrupes.builder.api.ProviderSelection;
033import org.jdrupes.builder.api.Resource;
034import org.jdrupes.builder.api.ResourceProvider;
035import org.jdrupes.builder.api.ResourceRequest;
036
037/// The Class DefaultBoundResourceQuery.
038///
039public class DefaultProviderSelection implements ProviderSelection {
040
041    private final AbstractProject project;
042    private final Set<Intent> preSelected;
043    private Predicate<ResourceProvider> filter = _ -> true;
044    private Consumer<ResourceProvider> onBeforeUse = _ -> {
045    };
046
047    /* default */ DefaultProviderSelection(AbstractProject project) {
048        this.project = project;
049        this.preSelected = null;
050    }
051
052    /* default */ DefaultProviderSelection(AbstractProject project,
053            Set<Intent> intends) {
054        this.project = project;
055        this.preSelected = intends;
056    }
057
058    @Override
059    public ProviderSelection filter(Predicate<ResourceProvider> filter) {
060        this.filter = this.filter.and(filter);
061        return this;
062    }
063
064    @Override
065    public DefaultProviderSelection without(ResourceProvider provider) {
066        filter = filter.and(p -> !p.equals(provider));
067        return this;
068    }
069
070    @Override
071    public DefaultProviderSelection
072            without(Class<? extends ResourceProvider> providerType) {
073        filter = filter.and(p -> !providerType.isAssignableFrom(p.getClass()));
074        return this;
075    }
076
077    @Override
078    public DefaultProviderSelection
079            onBeforeUse(Consumer<ResourceProvider> hook) {
080        this.onBeforeUse = hook;
081        return this;
082    }
083
084    @Override
085    public Stream<ResourceProvider> select(Set<Intent> intents) {
086        if (preSelected != null) {
087            if (!intents.isEmpty()) {
088                throw new IllegalArgumentException(
089                    "Duplicates selection of intents.");
090            }
091            intents = preSelected;
092        }
093        return project.dependencies(intents).filter(filter)
094            .peek(onBeforeUse::accept);
095    }
096
097    @Override
098    public <T extends Resource> Stream<T>
099            resources(ResourceRequest<T> requested) {
100        AtomicReference<ResourceRequest<T>> projectRequest
101            = new AtomicReference<>();
102        return select(requested.uses()).map(p -> {
103            if (p instanceof Project) {
104                return project.context().resources(p,
105                    projectRequest.updateAndGet(
106                        r -> r != null ? r : forwardedRequest(requested)));
107            }
108            return project.context().resources(p, requested);
109        }).flatMap(s -> s);
110    }
111
112    private <T extends Resource> ResourceRequest<T>
113            forwardedRequest(ResourceRequest<T> requested) {
114        Set<Intent> uses = preSelected != null ? preSelected : requested.uses();
115        Set<Intent> mapped = EnumSet.copyOf(uses);
116        if (uses.stream().filter(
117            EnumSet.of(Consume, Expose)::contains).findAny().isPresent()) {
118            mapped.remove(Consume);
119            mapped.addAll(EnumSet.of(Supply, Expose));
120        }
121        return requested.using(mapped);
122    }
123}