/**

 Copyright (C) 2017, Roman P., dev.roman [at] gmail

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program. If not, see http://www.gnu.org/licenses/

 */

package com.rp.podemu;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.SystemClock;
import android.view.KeyEvent;

import java.util.List;

/**
 * Created by rp on 10/31/15.
 */
public abstract class MediaPlayback
{
    private static MediaPlayback instance=null;

    protected static Context context=null;
    protected static String ctrlAppProcessName=null;
    //protected static String ctrlAppDbName=null;
    protected final static long POLLING_INTERVAL_TARGET = 500;
    protected static long pollingInterval = POLLING_INTERVAL_TARGET;

    private long lastPrevExecuted=System.currentTimeMillis();


    public static MediaController getActiveMediaController()
    {
        ComponentName componentName = new ComponentName(context, NotificationListener4.class);

        MediaSessionManager mediaSessionManager = (MediaSessionManager)context.getSystemService(Context.MEDIA_SESSION_SERVICE);
        List<MediaController> mediaControllerList = null;

        try
        {
            mediaControllerList = mediaSessionManager.getActiveSessions(componentName);

            for(MediaController mediaController: mediaControllerList)
            {
                if(mediaController.getPackageName().equals(ctrlAppProcessName)) return mediaController;
            }
        }
        catch(Exception e)
        {
            PodEmuLog.error("MPlayback: Notification Listener permissions not granted");
        }

        return null;
    }

    public static MediaPlayback getInstance()
    {
        if(instance==null) PodEmuLog.error("MPlayback: instance access before initialization");
        return instance;
    }

    public static void setCtrlAppProcessName(String app)
    {
        ctrlAppProcessName = app;
    }

    public static void initialize(Context c)
    {
        context=c;

        //ctrlAppDbName = appDbName;
        if(instance==null) instance = new MediaPlayback_Generic();

    }


    public abstract void setCurrentPlaylist(PodEmuMediaStore.Playlist playlist);
    public abstract PodEmuMediaStore.Playlist getCurrentPlaylist();

    public abstract boolean getTrackStatusChanged();

    public abstract void    updateCurrentlyPlayingTrack(PodEmuMessage podEmuMessage);

    public abstract boolean isPlaying();

    public abstract long getCurrentTrackPositionMS();

    public static long getPollingInteval()
    {
        return pollingInterval;
    }

    public void execute_action(int keyCode)
    {
        Intent intent;
        KeyEvent keyEvent;

        if(ctrlAppProcessName == null)
        {
            PodEmuLog.error("MPlayback: media control attempt before ctrlAppProcessName");
        }
        PodEmuLog.debug("MPlayback: executing action for " + ctrlAppProcessName);

        intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
        intent.setPackage(ctrlAppProcessName);
        keyEvent = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keyCode, 0);
        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
        context.sendOrderedBroadcast(intent, null);

        intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
        intent.setPackage(ctrlAppProcessName);
        keyEvent = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, keyCode, 0);
        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
        context.sendOrderedBroadcast(intent, null);

    }

    public void execute_action_long_press(int keyCode) {
        Intent intent;
        KeyEvent keyEvent;

        intent  = new Intent(Intent.ACTION_MEDIA_BUTTON);
        keyEvent = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keyCode, 0);
        keyEvent = KeyEvent.changeFlags(keyEvent, KeyEvent.FLAG_LONG_PRESS);
        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
        context.sendOrderedBroadcast(intent, null);

        intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
        keyEvent = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis()+1000, KeyEvent.ACTION_UP, keyCode, 0);
        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
        context.sendOrderedBroadcast(intent, null);
    }


    public synchronized void action_next()
    {
        //int currentTrack = getCurrentPlaylist().getCurrentTrackPos();
        //int trackCount   = getCurrentPlaylist().getTrackCount();
        //int newTrackPos  = currentTrack + 1;
        //if (newTrackPos == trackCount) newTrackPos = 0;
        //PodEmuLog.debug("PEMP: action NEXT requested. newTrackPos=" + newTrackPos);
        PodEmuLog.debug("PEMP: action NEXT requested");

        // TODO: implement repeat and shuffle
        //no loops? if(currentTrack == trackCount-1) return;

        if( shouldUpdatePosition() )
        {
            getCurrentPlaylist().setIncrement(+1);
            //getCurrentPlaylist().setCurrentTrack(newTrackPos);
        }

        PodEmuLog.error("PEMP: action NEXT, time = " + System.currentTimeMillis());

        MediaController mediaController=getActiveMediaController();
        if( mediaController != null) // && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) == PlaybackState.ACTION_SKIP_TO_NEXT)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_SKIP_TO_NEXT)");
            mediaController.getTransportControls().skipToNext();
        }
        else
        {
            execute_action(KeyEvent.KEYCODE_MEDIA_NEXT);
        }

    }

    public synchronized void action_prev(long timeElapsed)
    {
        action_prev(timeElapsed, false);
    }

    public synchronized void action_prev(long timeElapsed, boolean force)
    {
        //int currentTrack = getCurrentPlaylist().getCurrentTrackPos();
        //int trackCount   = getCurrentPlaylist().getTrackCount();
        //int newTrackPos  = currentTrack - 1;
        //if (newTrackPos == -1) newTrackPos = trackCount-1;

        //PodEmuLog.debug("PEMP: action PREV requested, force=" + force + ". newTrackPos=" + newTrackPos);
        PodEmuLog.debug("PEMP: action PREV requested, force=" + force);

        // TODO: implement repeat and shuffle
        //no loops? if(currentTrack == 0) return;

        if( shouldUpdatePosition() )
        {
            getCurrentPlaylist().setIncrement(-1);
            //getCurrentPlaylist().setCurrentTrack(newTrackPos);
        }

        MediaController mediaController=getActiveMediaController();
//        PlaybackState playbackState = mediaController.getPlaybackState();
        if(mediaController != null)// && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) == PlaybackState.ACTION_SKIP_TO_PREVIOUS) )
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_SKIP_TO_PREVIOUS)");
            // media players behave differently, depending on how much time elapsed
            // from the beginning of the song. Usually pressing "back" button after
            // 2 second elapsed from the beginning of the song will only rewind to the beginning of the song.
            if (force && timeElapsed > 2000)
            {
                mediaController.getTransportControls().skipToPrevious();
            }
            mediaController.getTransportControls().skipToPrevious();


            //KeyEvent keyEvent = new KeyEvent(0,200,KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS, 0);
            //mediaController.dispatchMediaButtonEvent(keyEvent);
        }

        else
        {
            PodEmuLog.debug("PEMP: executing action through KeyEvent");
            // media players behave differently, depending on how much time elapsed
            // from the beginning of the song. Usually pressing "back" button after
            // 2 second elapsed from the beginning of the song will only rewind to the beginning of the song.
            if (force && timeElapsed > 2000)
            {
                execute_action(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
            }
            execute_action(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
        }

        lastPrevExecuted=System.currentTimeMillis();
    }

    public void action_prev()
    {
        action_prev(0, false);
    }

    public void action_play()
    {
        PodEmuLog.debug("PEMP: action PLAY requested");
        MediaController mediaController=getActiveMediaController();
        if( mediaController != null)// && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_PLAY) == PlaybackState.ACTION_PLAY)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_PLAY)");
            mediaController.getTransportControls().play();
        }
        else
        {
            PodEmuLog.debug("PEMP: executing action through KeyEvent");
            execute_action(KeyEvent.KEYCODE_MEDIA_PLAY);
        }
    }

    public void action_pause()
    {
        PodEmuLog.debug("PEMP: action PAUSE requested");
        MediaController mediaController=getActiveMediaController();
        if( mediaController != null)// && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_PAUSE) == PlaybackState.ACTION_PAUSE)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_PAUSE)");
            mediaController.getTransportControls().pause();
        }
        else
        {
            PodEmuLog.debug("PEMP: executing action through KeyEvent");
            execute_action(KeyEvent.KEYCODE_MEDIA_PAUSE);
        }
    }

    public void action_play_pause()
    {
        PodEmuLog.debug("PEMP: action PLAY_PAUSE requested");
        MediaController mediaController=getActiveMediaController();
        if( mediaController!=null && mediaController.getPlaybackState()!=null && mediaController.getTransportControls()!=null)// && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_PLAY_PAUSE) == PlaybackState.ACTION_PLAY_PAUSE)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_PLAY_PAUSE)");
            if(mediaController.getPlaybackState().getState() == PlaybackState.STATE_PLAYING)
                mediaController.getTransportControls().pause();
            else
                mediaController.getTransportControls().play();
        }
        else
        {
            PodEmuLog.debug("PEMP: executing action through KeyEvent");
            execute_action(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
        }
    }

    public void action_stop()
    {
        PodEmuLog.debug("PEMP: action STOP requested");
        MediaController mediaController=getActiveMediaController();
        if( mediaController != null)// && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_STOP) == PlaybackState.ACTION_STOP)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_STOP)");
            mediaController.getTransportControls().stop();
        }
        else
        {
            PodEmuLog.debug("PEMP: executing action through KeyEvent");
            execute_action(KeyEvent.KEYCODE_MEDIA_STOP);
        }
    }

    public void action_skip_forward()
    {
        MediaController mediaController=getActiveMediaController();
        if( mediaController != null)// && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_FAST_FORWARD) == PlaybackState.ACTION_FAST_FORWARD)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_FAST_FORWARD)");
            mediaController.getTransportControls().fastForward();
        }
        else
        {
            PodEmuLog.debug("PEMP: executing action through KeyEvent");
            execute_action(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
            /*
            if(Build.VERSION.SDK_INT >= 23)
            {
                execute_action(KeyEvent.KEYCODE_MEDIA_SKIP_FORWARD);
            }
            */
        }
    }

    public void action_skip_backward()
    {
        MediaController mediaController=getActiveMediaController();
        if( mediaController != null)// && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_REWIND) == PlaybackState.ACTION_REWIND)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_REWIND)");
            mediaController.getTransportControls().fastForward();
        }
        else
        {
            PodEmuLog.debug("PEMP: executing action through KeyEvent");
            execute_action(KeyEvent.KEYCODE_MEDIA_REWIND);
            /*
            if(Build.VERSION.SDK_INT >= 23)
            {
                execute_action(KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD);
            }
            */
        }
    }

    public void action_stop_ff_rev()
    {
        if(!isPlaying()) return;

        PodEmuLog.debug("PEMP: action STOP_FF_REV requested");
        MediaController mediaController=getActiveMediaController();
        if( mediaController != null)// && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_PLAY) == PlaybackState.ACTION_PLAY)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_STOP_FF_REV)");
            mediaController.getTransportControls().play();
        }
        else
        {
            PodEmuLog.debug("PEMP: executing action through KeyEvent");
            execute_action(KeyEvent.KEYCODE_MEDIA_PLAY);
        }
    }


    // this function will calculate the number of tracks to jump forward or backward depending on
    // track count distance
    public int calcTrackCountFromPosition(int pos)
    {
        int currentTrack = getCurrentPlaylist().getCurrentTrackPos();
        int trackCount = getCurrentPlaylist().getTrackCount();

        PodEmuLog.debug("PEMS.Playlist: jumpTo: " + pos + " while current pos is " + currentTrack);

        if (pos == 0xffffffff)
        {
            //pos = playlistOffset;
            // don't want to process resetting playlist
            pos = 0;
        }

        // this should not happen - just in case we fix the boundaries
        pos = Math.max(pos, 0);
        pos = Math.min(pos, trackCount);

        int count = pos - currentTrack;

        if (PodEmuMediaStore.getInstance().getPlaylistCountMode() == PodEmuMediaStore.MODE_PLAYLIST_SIZE_FIXED)
        {
            int threshold = trackCount / 2;
            if (count > threshold)
            {
                count = -(currentTrack + trackCount - pos);
            }
            if (count < -threshold)
            {
                count = trackCount - currentTrack + pos;
            }
        }

        PodEmuLog.debug("PEMP: calculated track jump is: " + count);
        return count;
    }


    public boolean jumpToTrack(int pos)
    {
        PodEmuLog.debug("PEMP: jumpToTrack: " + pos);

        // skipToQueueItem does not work correctly so unfortunately we cannot use it
        /*
        MediaController mediaController=getActiveMediaController();
        if( mediaController != null && (mediaController.getPlaybackState().getActions() & PlaybackState.ACTION_SKIP_TO_QUEUE_ITEM) == PlaybackState.ACTION_SKIP_TO_QUEUE_ITEM)
        {
            PodEmuLog.debug("PEMP: executing action through MediaController (ACTION_SKIP_TO_QUEUE_ITEM)");
            mediaController.getTransportControls().skipToQueueItem(pos);
        }
        else
        */
        {
            int count = calcTrackCountFromPosition(pos);
            PodEmuLog.debug("PEMP: executing action through jumpTrackCount, count=" + count);
            jumpTrackCount(count);
        }

        return true;
    }


    private boolean jumpTrackCount(int count)
    {
        long timeElapsed = MediaPlayback.getInstance().getCurrentTrackPositionMS();

        //------------

        if(count >= 0)
        {
            for(int i=0;i<count;i++)
            {
                action_next();
            }
        }
        else
        {
            for(int i=0;i<-count;i++)
            {
                action_prev(timeElapsed,true);
                timeElapsed = 0;
            }
        }

        return true;
    }

    private boolean shouldUpdatePosition()
    {
        switch (PodEmuMediaStore.getInstance().getPlaylistCountMode())
        {
            case PodEmuMediaStore.MODE_PLAYLIST_SIZE_SINGLE:
            case PodEmuMediaStore.MODE_PLAYLIST_SIZE_TRIPLE:
                return false;
            default:
                return true;
        }
    }

    //public abstract boolean jumpTo(int pos);
    public abstract void setTrackStatusChanged(boolean status);

    public String getCtrlAppProcessName()
    {
        return ctrlAppProcessName;
    }

}