001package org.intellimate.izou.sdk.frameworks.music.player.template;
002
003import org.intellimate.izou.events.EventModel;
004import org.intellimate.izou.resource.ResourceModel;
005import org.intellimate.izou.sdk.frameworks.music.Capabilities;
006import org.intellimate.izou.sdk.frameworks.music.events.PlayerError;
007import org.intellimate.izou.sdk.frameworks.music.player.*;
008import org.intellimate.izou.sdk.frameworks.music.resources.CommandResource;
009import org.intellimate.izou.sdk.frameworks.music.resources.ProgressResource;
010import org.intellimate.izou.sdk.frameworks.music.resources.TrackInfoResource;
011import org.intellimate.izou.sdk.frameworks.music.resources.VolumeResource;
012
013import java.util.List;
014import java.util.Objects;
015import java.util.Optional;
016import java.util.function.Consumer;
017import java.util.function.Function;
018import java.util.function.Supplier;
019import java.util.stream.Collectors;
020
021/**
022 * this class is used to handle the Commands and directs them to the registered methods.
023 * @author LeanderK
024 * @version 1.0
025 */
026public class CommandHandler {
027    private final MusicHelper musicHelper;
028    private final MusicProvider musicProvider;
029    private final Capabilities capabilities;
030    private final Runnable stopCallback;
031    private Consumer<String> playPause = null;
032    private Consumer<TrackInfo> selectTrack = null;
033    private Consumer<String> nextPrevious = null;
034    private Consumer<Progress> jumpProgress = null;
035    private Consumer<String> changePlayback = null;
036    private Consumer<Volume> changeVolume;
037    private Function<String, Playlist> playlistForNameFunction = null;
038    private Supplier<List<String>> availablePlaylist = null;
039
040    /**
041     * creates a new CommandHandler
042     * @param musicHelper the musicHelper
043     * @param musicProvider the musicProvider
044     * @param stopCallback the callback for the stop-command
045     * @param capabilities the capabilities
046     */
047    public CommandHandler(MusicHelper musicHelper, MusicProvider musicProvider,
048                          Runnable stopCallback, Capabilities capabilities) {
049        this.musicHelper = musicHelper;
050        this.capabilities = capabilities;
051        this.musicProvider = musicProvider;
052        this.stopCallback = stopCallback;
053    }
054
055    /**
056     * adds the ability for the Play/Pause requests
057     * @param controller  the controller for callback
058     */
059    public void setPlayPauseController(Consumer<String> controller) {
060        if (controller == null)
061            return;
062        this.playPause = controller;
063        capabilities.setPlayPauseControl(true);
064    }
065
066    /**
067     * adds the ability to select tracks
068     * @param controller  the controller for callback
069     */
070    public void setTrackSelectorController(Consumer<TrackInfo> controller) {
071        if (controller == null)
072            return;
073        selectTrack = controller;
074        capabilities.setAbleToSelectTrack(true);
075    }
076
077    /**
078     * adds the ability to select the next/previous track
079     * @param controller  the controller for callback
080     */
081    public void setNextPreviousController(Consumer<String> controller) {
082        if (controller == null)
083            return;
084        nextPrevious = controller;
085        capabilities.setNextPrevious(true);
086    }
087
088    /**
089     * adds the ability to jump to a specified position of the current track
090     * @param controller  the controller for callback
091     */
092    public void setJumpProgressController(Consumer<Progress> controller) {
093        if (controller == null)
094            return;
095        jumpProgress = controller;
096        capabilities.setAbleToJump(true);
097    }
098
099    /**
100     * adds the ability to change the playback
101     * @param controller  the controller for callback
102     */
103    public void setPlaybackChangeableController(Consumer<String> controller) {
104        if (controller == null)
105            return;
106        changePlayback = controller;
107        capabilities.setPlaybackChangeable(true);
108    }
109
110    /**
111     * adds the ability to change the volume from outside the player
112     * @param controller  the controller for callback
113     */
114    public void setVolumeChangeableController(Consumer<Volume> controller) {
115        if (controller == null)
116            return;
117        changeVolume = controller;
118        capabilities.setChangeVolume(true);
119    }
120
121    /**
122     * adds the ability to return the available playlists on request.
123     * @param availablePlaylist retruns a List of Strings which represent the Playlists
124     * @param playlistForNameFunction takes a String from the List and returns the Playlist
125     */
126    public void broadcastAvailablePlaylists(Supplier<List<String>> availablePlaylist, Function<String, Playlist> playlistForNameFunction) {
127        if (availablePlaylist == null || playlistForNameFunction == null)
128            return;
129        this.availablePlaylist = availablePlaylist;
130        this.playlistForNameFunction = playlistForNameFunction;
131        capabilities.setBroadcasting(true);
132    }
133
134    /**
135     * this method gets called when a new Command was found. It automatically fires the update Event or an error
136     * @param eventModel the event with the Commands
137     */
138    public void handleCommandResources(EventModel eventModel) {
139        List<ResourceModel<String>> resourceModels = eventModel.getListResourceContainer()
140                .provideResource(CommandResource.ResourceID)
141                .stream()
142                .filter(resourceModel -> resourceModel.getResource() instanceof String)
143                .map(resourceModel -> {
144                    try {
145                        //noinspection unchecked
146                        return (ResourceModel<String>) resourceModel;
147                    } catch (ClassCastException e) {
148                        return null;
149                    }
150                })
151                .filter(Objects::nonNull)
152                .collect(Collectors.toList());
153        for (ResourceModel<String> resourceModel : resourceModels) {
154            if (!CommandResource.verifyCommand(resourceModel.getResource()))
155                continue;
156            if (!CommandResource.verifyCapabilities(resourceModel.getResource(), capabilities)) {
157                musicHelper.playerError(PlayerError.ERROR_NOT_ABLE + "command: " + resourceModel.getResource(),
158                        resourceModel.getProvider());
159                continue;
160            }
161            switch (resourceModel.getResource()) {
162                case CommandResource.PLAY: if (!musicProvider.isPlaying())
163                    playPause.accept(resourceModel.getResource());
164                    break;
165                case CommandResource.PAUSE: if (musicProvider.isPlaying())
166                    playPause.accept(resourceModel.getResource());
167                    break;
168                case CommandResource.SELECT_TRACK: handleSelectTrack(eventModel, resourceModel);
169                    break;
170                case CommandResource.NEXT: nextPrevious.accept(resourceModel.getResource());
171                    break;
172                case CommandResource.PREVIOUS: nextPrevious.accept(resourceModel.getResource());
173                    break;
174                case CommandResource.JUMP: handleJump(eventModel, resourceModel);
175                    break;
176                case CommandResource.CHANGE_PLAYBACK: changePlayback.accept(resourceModel.getResource());
177                    break;
178                case CommandResource.CHANGE_VOLUME: handleVolume(eventModel, resourceModel);
179                    break;
180                case CommandResource.STOP: stopCallback.run();
181                    break;
182            }
183        }
184    }
185
186    /**
187     * handles the volume-command
188     * @param eventModel the event
189     * @param resourceModel the resourcemodel
190     */
191    private void handleVolume(EventModel eventModel, ResourceModel<String> resourceModel) {
192        Optional<Volume> volumeResource = VolumeResource.getVolume(eventModel);
193        if (!volumeResource.isPresent()) {
194            musicHelper.playerError(PlayerError.ERROR_ILLEGAL + "command: " + resourceModel.getResource() + "missing resource",
195                    resourceModel.getProvider());
196        }
197        changeVolume.accept(volumeResource.get());
198    }
199
200    /**
201     * handles the jump-command
202     * @param eventModel the event
203     * @param resourceModel the resourcemodel
204     */
205    private void handleJump(EventModel eventModel, ResourceModel<String> resourceModel) {
206        Optional<Progress> progress = ProgressResource.getProgress(eventModel);
207        if (!progress.isPresent()) {
208            musicHelper.playerError(PlayerError.ERROR_ILLEGAL + "command: " + resourceModel.getResource() + "missing resource",
209                    resourceModel.getProvider());
210        }
211        jumpProgress.accept(progress.get());
212    }
213
214    /**
215     * handles the select Track command
216     * @param eventModel the event
217     * @param resourceModel the resource
218     */
219    private void handleSelectTrack(EventModel eventModel, ResourceModel<String> resourceModel) {
220        Optional<TrackInfo> trackInfo = TrackInfoResource.getTrackInfo(eventModel);
221        if (!trackInfo.isPresent()) {
222            musicHelper.playerError(PlayerError.ERROR_ILLEGAL + "command: " + resourceModel.getResource() + "missing resource",
223                    resourceModel.getProvider());
224        }
225        selectTrack.accept(trackInfo.get());
226    }
227
228    /**
229     * retruns the playlist for the specified name
230     * @param name the name of the playlist
231     * @return the playlist
232     */
233    Playlist getPlaylistFromName(String name) {
234        return playlistForNameFunction.apply(name);
235    }
236
237    /**
238     * generates all available playlists.
239     * @return a list containing the names of all available Playlists.
240     */
241    List<String> getAvailablePlaylists() {
242        return availablePlaylist.get();
243    }
244}