001package org.intellimate.izou.resource;
002
003import org.intellimate.izou.util.AddonThreadPoolUser;
004import org.intellimate.izou.util.IzouModule;
005import org.intellimate.izou.events.EventModel;
006import org.intellimate.izou.identification.IllegalIDException;
007import org.intellimate.izou.main.Main;
008
009import java.util.*;
010import java.util.concurrent.CompletableFuture;
011import java.util.concurrent.ExecutionException;
012import java.util.function.Consumer;
013import java.util.stream.Collectors;
014
015/**
016 * this class manages all the ResourceBuilders.
017 */
018public class ResourceManager extends IzouModule implements AddonThreadPoolUser {
019    /**
020     * this object maps all the eventIDs to ResourceBuilders
021     * the key is the registered event (or noEvent)
022     */
023    private HashMap<String, LinkedList<ResourceBuilderModel>> eventSubscribers = new HashMap<>();
024    /**
025     * this object maps all the resourceID to ResourceBuilders
026     * the key is the registered event (or noEvent)
027     * the List contains all the ResourceBuilders registered
028     */
029    private HashMap<String, LinkedList<ResourceBuilderModel>> resourceIDs= new HashMap<>();
030
031    public ResourceManager(Main main) {
032        super(main);
033    }
034
035    /**
036     * generates all the resources for an event
037     * @param event the Event to generate the resources for
038     * @return a List containing all the generated resources
039     */
040    public List<ResourceModel> generateResources(EventModel<?> event) {
041        if(!event.getAllInformations().stream()
042                .anyMatch(eventSubscribers::containsKey)) return new LinkedList<>();
043
044        List<ResourceBuilderModel> resourceBuilders = event.getAllInformations().stream()
045                .map(eventSubscribers::get)
046                .filter(Objects::nonNull)
047                .flatMap(Collection::stream)
048                .distinct()
049                .collect(Collectors.toList());
050        
051        return generateResources(resourceBuilders, event);
052    }
053
054    /**
055     * generates the resources with a 1 sec. timeout for each ResourceBuilder 
056     * @param resourceBuilders the ResourceBuilders
057     * @param event the event or null if not present
058     * @return a List of generated resources
059     */
060    private List<ResourceModel> generateResources(List<ResourceBuilderModel> resourceBuilders, EventModel event) {
061        Optional<EventModel> parameter = event != null ? Optional.of(event) : Optional.empty();
062        List<CompletableFuture<List<ResourceModel>>> futures = resourceBuilders.stream()
063                .map(resourceB -> submit(() -> resourceB.provideResource(resourceB.announceResources(), parameter)))
064                .collect(Collectors.toList());
065
066        try {
067            futures = timeOut(futures, 3000);
068        } catch (InterruptedException e) {
069            debug("interrupted while doing an time-out", e);
070        }
071
072        return futures.stream()
073                .map(future -> {
074                    try {
075                        return future.get();
076                    } catch (InterruptedException | ExecutionException e) {
077                        debug("exception while trying to get the result from the future", e);
078                        return null;
079                    }
080                })
081                .filter(Objects::nonNull)
082                .flatMap(Collection::stream)
083                .collect(Collectors.toList());
084    }
085
086    /**
087     * generates a resources
088     * <p>
089     * It will use the first matching resource! So if you really want to be sure, set the provider
090     * Identification
091     * </p>
092     * @param resource the resource to request
093     * @param consumer the callback when the ResourceBuilder finishes
094     * @throws IllegalIDException not yet implemented
095     */
096    @Deprecated
097    public void generatedResource(ResourceModel resource, Consumer<List<ResourceModel>> consumer) throws IllegalIDException {
098        generateResource(resource)
099                .ifPresent(completableFuture -> completableFuture.thenAccept(consumer));
100    }
101
102    /**
103     * generates a resources
104     * <p>
105     * It will use the first matching resource! So if you really want to be sure, set the provider
106     * Identification
107     * </p>
108     * @param resource the resource to request
109     * @return an optional of an CompletableFuture
110     * @throws IllegalIDException not yet implemented
111     */
112    public Optional<CompletableFuture<List<ResourceModel>>> generateResource (ResourceModel resource) throws IllegalIDException {
113        if(resourceIDs.get(resource.getResourceID()) == null) return Optional.empty();
114        return resourceIDs.get(resource.getResourceID()).stream()
115                //return true if resource has no provider, if not check provider
116                .filter(resourceS -> !resource.hasProvider() || resourceS.isOwner(resource.getProvider()))
117                .findFirst()
118                .map(resourceB -> submit(() -> resourceB.provideResource(Collections.singletonList(resource), Optional.empty())));
119    }
120
121    /**
122     * registers a ResourceBuilder.
123     * <p>
124     * this method registers all the events, resourcesID etc.
125     * @param resourceBuilder an instance of the ResourceBuilder
126     * @throws IllegalIDException not yet implemented
127     */
128    public void registerResourceBuilder(ResourceBuilderModel resourceBuilder) throws IllegalIDException {
129        registerResourceIDsForResourceBuilder(resourceBuilder);
130        registerEventsForResourceBuilder(resourceBuilder);
131    }
132
133    /**
134     * registers all ResourceIDs for the ResourceBuilders
135     * @param resourceBuilder an instance of ResourceBuilder
136     */
137    private void registerResourceIDsForResourceBuilder(ResourceBuilderModel resourceBuilder) {
138        List<? extends ResourceModel> resources = resourceBuilder.announceResources();
139        if(resources == null) return;
140        resources.stream()
141                .map(this::getRegisteredListForResource)
142                .forEach(list -> list.add(resourceBuilder));
143    }
144
145    /**
146     * returns the list with all the ResourceBuilders listening to the Resource
147     * @param resource the resource to listen to
148     * @return a List of ResourceBuilders
149     */
150    private List<ResourceBuilderModel> getRegisteredListForResource(ResourceModel resource) {
151        if(resourceIDs.containsKey(resource.getResourceID())) {
152            return resourceIDs.get(resource.getResourceID());
153        } else {
154            LinkedList<ResourceBuilderModel> tempList = new LinkedList<>();
155            resourceIDs.put(resource.getResourceID(), tempList);
156            return tempList;
157        }
158    }
159
160    /**
161     * Registers the events for the ResourceBuilder
162     *
163     * @param resourceBuilder an instance of ResourceBuilder
164     */
165    private void registerEventsForResourceBuilder(ResourceBuilderModel resourceBuilder) {
166        List<? extends EventModel<?>> events = resourceBuilder.announceEvents();
167        if(events == null) return;
168        events.stream()
169                .filter(event -> event.getAllInformations() != null)
170                .flatMap(event -> event.getAllInformations().stream())
171                .map(this::getRegisteredListForEvent)
172                .forEach(list -> list.add(resourceBuilder));
173    }
174
175    /**
176     * returns a list of all the ResourceBuilders listening to the Event-ID 
177     * @param event the eventID
178     * @return a List of ResourceBuilders
179     */
180    private List<ResourceBuilderModel> getRegisteredListForEvent(String event) {
181        if(eventSubscribers.containsKey(event)) {
182            return eventSubscribers.get(event);
183        } else {
184            LinkedList<ResourceBuilderModel> tempList = new LinkedList<>();
185            eventSubscribers.put(event, tempList);
186            return tempList;
187        }
188    }
189
190    /**
191     * unregister a ResourceBuilder.
192     * <p>
193     * this method unregisters all the events, resourcesID etc.
194     * @param resourceBuilder an instance of the ResourceBuilder
195     */
196    public void unregisterResourceBuilder(ResourceBuilderModel resourceBuilder) {
197        unregisterResourceIDForResourceBuilder(resourceBuilder);
198        unregisterEventsForResourceBuilder(resourceBuilder);
199    }
200
201    /**
202     * Unregisters all ResourceIDs for the ResourceBuilders
203     *
204     * @param resourceBuilder an instance of ResourceBuilder
205     */
206    private void unregisterResourceIDForResourceBuilder(ResourceBuilderModel resourceBuilder) {
207        List<? extends ResourceModel> resources = resourceBuilder.announceResources();
208        resources.stream().map(resource -> resourceIDs.get(resource.getResourceID()))
209                .filter(Objects::nonNull)
210                .forEach(list -> list.remove(resourceBuilder));
211    }
212
213    /**
214     * unregisters the events for the ResourceBuilder
215     * @param resourceBuilder an instance of ResourceBuilder
216     */
217    private void unregisterEventsForResourceBuilder(ResourceBuilderModel resourceBuilder) {
218        resourceBuilder.announceEvents().stream()
219                .map(eventSubscribers::get)
220                .filter(Objects::nonNull)
221                .forEach(list -> list.remove(resourceBuilder));
222    }
223}