/*
 * Copyright 2016 Substance Mobile
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.animbus.music.media;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.media.session.MediaControllerCompat.TransportControls;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import android.widget.Toast;

import com.animbus.music.R;
import com.animbus.music.media.objects.Album;
import com.animbus.music.media.objects.Song;
import com.animbus.music.media.stable.PlaybackManager;
import com.animbus.music.media.stable.QueueManager;
import com.animbus.music.media.stable.ServiceHelper;
import com.animbus.music.util.Options;

import java.util.ArrayList;
import java.util.List;

import static com.animbus.music.media.MediaService.ACTION_START;
import static com.animbus.music.media.MediaService.LocalPlayback;
import static com.animbus.music.media.MediaService.PlaybackBase;

/**
 * Created by Adrian on 11/14/2015.
 */
public class PlaybackRemote {
    private static final String TAG = "PlaybackRemote";
    static volatile TransportControls remote;
    private static volatile Context mContext;
    private static volatile MediaService mService;
    static volatile List<Song> mQueue = new ArrayList<>();
    static volatile int mCurrentSongPos = 0;

    static volatile List<Song> tempSongList;
    static volatile int tempListStartPos;
    static volatile boolean tempRepeating;
    static volatile PlaybackBase tempIMPL;
    static volatile boolean tempNotifyListener;
    static volatile Song tempSong;
    static volatile Uri tempUri;
    static volatile int tempCommand = -1;

    public static final PlaybackBase LOCAL = new LocalPlayback();

    private PlaybackRemote() {
    }

    // This is separate from init because the context needs to be set at the very beginning. init is called
    // when the service starts, and the only way to start the service is by using a context.
    // the service is started right before the media request is called, so the remote variable will be initialised and
    // can be used because it is set when init is called, when the service starts.
    public static void setUp(Context context) {
        mContext = context;
        ServiceHelper.get(context).initService();
    }

    public static void init(MediaService service) {
        mService = service;
        remote = service.mSession.getController().getTransportControls();
        if (tempIMPL != null) {
            service.inject(tempIMPL);
            tempIMPL = null;
        }


        if (tempCommand == 0) {
            play(tempUri);
        } else if (tempCommand == 1) {
            play(tempSong);
        } else if (tempCommand == 2) {
            play(tempSongList, tempListStartPos);
        }
        tempUri = null;
        tempSong = null;
        tempSongList = null;
        tempListStartPos = -1;
        tempCommand = -1;
    }

    public static void inject(PlaybackBase impl) {
        if (mService != null) mService.inject(impl); else tempIMPL = impl;
    }

    public static boolean startServiceIfNecessary() {
        if (mService == null)
            mContext.startService(new Intent(ACTION_START).setClass(mContext, MediaService.class));
        return mService == null;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Here go all of the controls
    ///////////////////////////////////////////////////////////////////////////

    public static void play(Uri uri) {
        if (Options.useStableService()) {
            Toast.makeText(mContext, R.string.msg_coming_soon, Toast.LENGTH_SHORT).show();
            return;
        }
        if (!startServiceIfNecessary()) remote.playFromUri(uri, null); else {
            tempCommand = 0;
            tempUri = uri;
        }
    }

    public static void play(Song song) {
        if (Options.useStableService()) {
            PlaybackManager.get().play(song);
            return;
        }
        if (!startServiceIfNecessary()) remote.playFromMediaId(String.valueOf(song.getID()), null); else {
            tempCommand = 1;
            tempSong = song;
        }
    }

    public static void play(List<Song> songs, int startPos) {
        if (Options.useStableService()) {
            PlaybackManager.get().play(songs, startPos);
            return;
        }
        tempSongList = songs;
        tempListStartPos = startPos;
        if (!startServiceIfNecessary()) remote.sendCustomAction(PlaybackBase.ACTION_PLAY_FROM_LIST, null); else tempCommand = 2;
    }

    public static void playQueueItem(int pos) {
        if (Options.useStableService()) {
            PlaybackManager.get().playQueueItem(pos);
            return;
        }
        play(getQueue().get(pos));
        setCurrentSongPos(pos);
    }

    public static void resume() {
        if (Options.useStableService()) {
            PlaybackManager.get().resume();
            return;
        }
        remote.play();
    }

    public static void pause() {
        if (Options.useStableService()) {
            PlaybackManager.get().pause();
            return;
        }
        remote.pause();
    }

    public static void next() {
        if (Options.useStableService()) {
            PlaybackManager.get().playNext();
            return;
        }
        remote.skipToNext();
    }

    public static void prev() {
        if (Options.useStableService()) {
            PlaybackManager.get().playPrev(true);
            return;
        }
        remote.skipToPrevious();
    }

    public static void toggleRepeat() {
        if (Options.useStableService()) {
            PlaybackManager.get().setRepeat(!PlaybackManager.get().isLooping());
            return;
        }
        setRepeat(!mService.IMPL.isRepeating());
    }

    public static void setRepeat(boolean repeating) {
        if (Options.useStableService()) {
            PlaybackManager.get().setRepeat(repeating);
            return;
        }
        remote.sendCustomAction(PlaybackBase.ACTION_SET_REPEAT, null);
        tempRepeating = repeating;
    }

    public static boolean isRepeating() {
        if (Options.useStableService()) {
            return PlaybackManager.get().isLooping();
        }
        return mService.IMPL.isRepeating();
    }

    public static void seek(long time) {
        if (Options.useStableService()) {
            PlaybackManager.get().seekTo((int)time);
            return;
        }
        remote.seekTo(time);
    }

    public static void stop() {
        if (Options.useStableService()) {
            PlaybackManager.get().stop();
            return;
        }
        remote.stop();
        killService();
    }

    public static void killService() {
        mService.stopSelf();
    }

    ///////////////////////////////////////////////////////////////////////////
    // All of the queue things
    ///////////////////////////////////////////////////////////////////////////

    public static List<Song> getQueue() {
        if (Options.useStableService()) {
            return QueueManager.get().getCurrentQueueAsSong();
        }
        return mQueue;
    }

    public static void setQueue(List<Song> queue) {
        if (Options.useStableService()) {
            QueueManager.get().setQueueAsSongList(queue);
            return;
        }
        mQueue = queue;
    }

    public static void addToQueue(Song s) {
        if (Options.useStableService()) {
            QueueManager.get().addToQueue(s);
            return;
        }
        mQueue.add(s);
    }

    public static void setCurrentSongPos(int currentSongPos) {
        if (Options.useStableService()) {
            QueueManager.get().setCurrentSongPos(currentSongPos);
            return;
        }
        mCurrentSongPos = currentSongPos;
    }

    public static int getCurrentSongPos() {
        if (Options.useStableService()) {
            return QueueManager.get().getCurrentSongPos();
        }
        return mCurrentSongPos;
    }

    public static Song getCurrentSong() {
        if (Options.useStableService()) {
            return PlaybackManager.get().getCurrentSong();
        }
        if (!getQueue().isEmpty()) return getQueue().get(getCurrentSongPos());
        return null;
    }

    public static int getNextSongPos() {
        if (Options.useStableService()) {
            return QueueManager.get().updateNextSongPos();
        }

        int pos;
        pos = mCurrentSongPos + 1;
        if (pos > (mQueue.size() - 1)) {
            pos = 0;
        }
        setCurrentSongPos(pos);
        return pos;
    }

    public static int getPrevSongPos() {
        if (Options.useStableService()) {
            return QueueManager.get().updatePrevSongPos();
        }

        int pos;
        pos = mCurrentSongPos - 1;
        if (pos == 0) {
            pos = (mQueue.size() - 1);
        }
        setCurrentSongPos(pos);
        return pos;
    }

    ///////////////////////////////////////////////////////////////////////////
    // All of the other tidbits
    ///////////////////////////////////////////////////////////////////////////

    public static MediaSessionCompat.Token getToken() {
        if (Options.useStableService()) {
            return PlaybackManager.get().getService().getSession().getSessionToken();
        }

        return mService.mSession.getSessionToken();
    }

    public static PlaybackStateCompat getState() {
        if (Options.useStableService()) {
            return PlaybackManager.get().getService().getStateObj();
        }

        return mService.mState;
    }

    public static MediaSessionCompat getSession() {
        if (Options.useStableService()) {
            return PlaybackManager.get().getService().getSession();
        }

        return mService.mSession;
    }

    public static boolean isActive() {
        if (Options.useStableService()) {
            return PlaybackManager.get().isActive();
        }

        return mService != null && mService.mSession.isActive();
    }

    public static boolean isPlaying() {
        if (Options.useStableService()) {
            return PlaybackManager.get().isPlaying();
        }

        return mService.IMPL.isPlaying();
    }

    public static int getCurrentPosInSong() {
        if (Options.useStableService()) {
            return PlaybackManager.get().getCurrentPosInSong();
        }

        return mService.IMPL.getCurrentPosInSong();
    }

    //////////////////////////////////////////////////////////////////////////
    // Listener
    ///////////////////////////////////////////////////////////////////////////

    static ArrayList<SongChangedListener> songListeners = new ArrayList<>();
    static ArrayList<StateChangedListener> stateListeners = new ArrayList<>();

    static {
        PlaybackManager.get().registerListener(new PlaybackManager.OnChangedListener() {
            @Override
            public void onSongChanged(Song song) {
                updateSongListeners(song);
            }

            @Override
            public void onPlaybackStateChanged(PlaybackStateCompat state) {
                updateStateListeners(state);
            }
        });
    }

    public interface SongChangedListener {
        void onSongChanged(Song newSong);
    }

    public interface StateChangedListener {
        void onStateChanged(PlaybackStateCompat newState);
    }

    public static void registerSongListener(SongChangedListener listener) {
        if (!songListeners.contains(listener)) songListeners.add(listener);
        else Log.d(TAG, "This listener is already registered");
    }

    public static void registerStateListener(StateChangedListener listener) {
        if (!stateListeners.contains(listener)) stateListeners.add(listener);
        else Log.d(TAG, "This listener is already registered");

    }

    protected static void updateSongListeners(Song newSong) {
        for (SongChangedListener l : songListeners) l.onSongChanged(newSong);
    }

    protected static void updateSongListeners(Uri uri) {
        if (Options.useStableService()) {
            Toast.makeText(mContext, R.string.msg_coming_soon, Toast.LENGTH_SHORT).show();
            return;
        }

        Song s = Library.findSongByUri(uri);
        if (s == null) {
            //Builds the song
            s = new Song();
            Album a = new Album();
            s.setTitle(mContext.getString(R.string.title_uri));
            s.setSongArtist(mContext.getString(R.string.artist_uri));
            a.setContext(mContext);

            updateSongListeners(s);
        }

        //Sets the queue to this one song
        List<Song> newQueue = new ArrayList<>();
        newQueue.add(s);
        setQueue(newQueue);
        setCurrentSongPos(0);
    }

    protected static void updateStateListeners(PlaybackStateCompat newState) {
        for (StateChangedListener l : stateListeners) l.onStateChanged(newState);
    }

}
