/*
 * *
 *  * This file is part of QuickLyric
 *  * Copyright © 2017 QuickLyric SPRL
 *  *
 *  * QuickLyric 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.
 *  *
 *  * QuickLyric 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 QuickLyric.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package com.geecko.QuickLyric.services;

import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaMetadataEditor;
import android.media.MediaMetadataRetriever;
import android.media.RemoteControlClient;
import android.media.RemoteController;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;

import com.geecko.QuickLyric.App;
import com.geecko.QuickLyric.broadcastReceiver.MusicBroadcastReceiver;
import com.geecko.QuickLyric.fragment.LyricsViewFragment;
import com.geecko.QuickLyric.utils.MediaControllerCallback;

import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Set;

import static com.geecko.QuickLyric.utils.NotificationUtil.NOTIFICATION_ID;

@SuppressWarnings("deprecation")
@TargetApi(Build.VERSION_CODES.KITKAT)
public class NotificationListenerService extends android.service.notification.NotificationListenerService implements RemoteController.OnClientUpdateListener, MediaControllerCallback.MetadataUpdateListener {

    public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";

    private static int scrobblingProcessPID = -1;
    private static WeakReference<RemoteController> mRemoteController = new WeakReference<>(null);
    private IBinder mBinder;
    private MediaControllerCallback mediaControllerCallback;
    private String track;
    private String artist;
    private Object durationObject;

    @Override
    public IBinder onBind(Intent intent) {
        if (mBinder == null)
            mBinder = super.onBind(intent);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            onListenerConnected();
        return mBinder;
    }

    @Override
    @SuppressWarnings("NewApi")
    public void onCreate() {
        super.onCreate();
        if (!isListeningAuthorized(this))
            return;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            mRemoteController = new WeakReference<>(new RemoteController(this, this));
            mRemoteController.get().setArtworkConfiguration(3000, 3000);
            if (!((AudioManager) getSystemService(Context.AUDIO_SERVICE)).registerRemoteController(mRemoteController.get())) {
                throw new RuntimeException("Error while registering RemoteController!");
            }
            mediaControllerCallback = new MediaControllerCallback(this);
        } else {
            startScrobblingService();
            // disableNotificationListenerService();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        this.mBinder = null;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            if (mRemoteController != null && mRemoteController.get() != null)
                ((AudioManager) getSystemService(Context.AUDIO_SERVICE)).unregisterRemoteController(mRemoteController.get());
        }
    }

    @Override
    public void onListenerConnected() {
        super.onListenerConnected();
        Intent intent = new Intent("com.geecko.QuickLyric.NLS_CONNECTED");
        sendBroadcast(intent);
        MusicBroadcastReceiver.disableBroadcastReceiver(this);
    }

    @Override
    @TargetApi(24)
    public void onListenerDisconnected() {
        super.onListenerDisconnected();
        requestRebind(new ComponentName(getApplicationContext(), NotificationListenerService.class));
    }

    private void startScrobblingService() {
        Log.d("geecko", "NLS starting Scrobbler");
        Intent intent = new Intent(getApplicationContext(), ScrobblerService.class);
        startService(intent);
    }

    public static boolean isListeningAuthorized(Context context) {
        ContentResolver contentResolver = context.getContentResolver();
        String enabledNotificationListeners = Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
        String packageName = context.getPackageName();

        return !(enabledNotificationListeners == null || !enabledNotificationListeners.contains(packageName));
    }

    public static boolean isNotificationListenerServiceEnabled(Context context) {
        Set<String> packageNames = NotificationManagerCompat.getEnabledListenerPackages(context);
        return packageNames.contains(context.getPackageName());
    }

    public static boolean isAppScrobbling(Context context) {
        if (context == null)
            return false;
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> pids = manager.getRunningAppProcesses();
        if (scrobblingProcessPID == -1) {
            String needle = Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT ? "com.geecko.QuickLyric:NLS" : "com.geecko.QuickLyric";
            for (int i = 0; i < pids.size(); i++) {
                ActivityManager.RunningAppProcessInfo info = pids.get(i);
                if (info.processName.equalsIgnoreCase(needle)) {
                    scrobblingProcessPID = info.pid;
                    break;
                }
            }
        }

        ComponentName serviceComponent = new ComponentName(context, Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT ?
                NotificationListenerService.class : ScrobblerService.class);
        boolean serviceRunning = false;
        List<ActivityManager.RunningServiceInfo> runningServices = manager.getRunningServices(Integer.MAX_VALUE);
        if (runningServices != null ) {
            for (ActivityManager.RunningServiceInfo service : runningServices) {
                if ((service.service.equals(serviceComponent) && service.pid == scrobblingProcessPID)) {
                    serviceRunning = true;
                    break;
                }
            }
        }
        if (!serviceRunning) {
            StackTraceElement traceElement = Thread.currentThread().getStackTrace()[3];
            Bundle bundle = new Bundle();
            bundle.putString("file", traceElement.getFileName());
            bundle.putString("method", traceElement.getMethodName());
        }
        return serviceRunning;
    }

    private void disableNotificationListenerService() {
        PackageManager pm = getPackageManager();
        pm.setComponentEnabledSetting(new ComponentName(getApplicationContext(), NotificationListenerService.class),
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    }

    /* KitKat stuff */

    private boolean isRemoteControllerPlaying;
    private boolean mHasBug = true;

    @Override
    public void onClientChange(boolean clearing) {
        // isListeningAuthorized(NotificationListenerService.this);
    }

    @Override
    public void onClientPlaybackStateUpdate(int state) {
        this.isRemoteControllerPlaying = state == RemoteControlClient.PLAYSTATE_PLAYING;
    }


    public static long getRemotePlayerPosition() {
        return mRemoteController.get() != null ?
                Math.min(3600000,mRemoteController.get().getEstimatedMediaPosition()) : -1L;
    }

        @Override
    public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, long currentPosMs, float speed) {
        this.isRemoteControllerPlaying = state == RemoteControlClient.PLAYSTATE_PLAYING;
        mHasBug = false;
        if (currentPosMs > 3600000)
            currentPosMs = -1L;
        SharedPreferences current = getSharedPreferences("current_music", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = current.edit();
        editor.putLong("position", currentPosMs);

        if (isRemoteControllerPlaying) {
            long currentTime = System.currentTimeMillis();
            editor.putLong("startTime", currentTime);
        } else {
            NotificationManager notificationManager =
                    ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE));
            notificationManager.cancel(NOTIFICATION_ID);
            notificationManager.cancel(8);
        }
        editor.putBoolean("playing", isRemoteControllerPlaying);
        editor.apply();

        if (App.isMainActivityVisible() && isRemoteControllerPlaying) {
            Intent internalIntent = new Intent(LyricsViewFragment.UPDATE_LYRICS_ACTION);
            internalIntent
                    .putExtra("artist", current.getString("artist", ""))
                    .putExtra("track", current.getString("track", ""));
            LyricsViewFragment.sendIntent(NotificationListenerService.this, internalIntent);
        }
        Log.d("geecko", "PlaybackStateUpdate - position stored: " + currentPosMs);

        long position = getRemotePlayerPosition();

        if (durationObject instanceof Double) {
            if (artist != null && !artist.isEmpty())
                mediaControllerCallback.broadcast(this, artist, track, isRemoteControllerPlaying, (Double) durationObject, position, null);
        } else if (durationObject instanceof Integer) {
            if (artist != null && !artist.isEmpty())
                mediaControllerCallback.broadcast(this, artist, track, isRemoteControllerPlaying, (Integer) durationObject, position, null);
        } else if (durationObject instanceof Long)
            if (artist != null && !artist.isEmpty())
                mediaControllerCallback.broadcast(this, artist, track, isRemoteControllerPlaying, (Long) durationObject, position, null);
    }

    @Override
    public void onClientTransportControlUpdate(int transportControlFlags) {
        if (mHasBug && mRemoteController.get() != null) {
            long position = mRemoteController.get().getEstimatedMediaPosition();
            if (position > 3600000)
                position = -1L;
            SharedPreferences current = getSharedPreferences("current_music", Context.MODE_PRIVATE);
            current.edit().putLong("position", position).apply();
            if (isRemoteControllerPlaying) {
                long currentTime = System.currentTimeMillis();
                current.edit().putLong("startTime", currentTime).apply();
            }
            Log.d("geecko", "TransportControlUpdate - position stored: " + position);
        }
    }

    @Override
    public void onClientMetadataUpdate(RemoteController.MetadataEditor metadataEditor) {
        // isRemoteControllerPlaying = true;

        durationObject = metadataEditor.getObject(MediaMetadataRetriever.METADATA_KEY_DURATION, 1200); //allow it to pass if not present
        artist = metadataEditor.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST,
                metadataEditor.getString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, ""));
        track = metadataEditor.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, "");
        Bitmap artwork = metadataEditor.getBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK, null);

        mediaControllerCallback.saveArtwork(this, artwork, artist, track);
    }

    @Override
    public void onMetadataUpdated(Bundle metadata) {
    }

    /* NLS Stuff */

    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (!isAppScrobbling(this))
                    startScrobblingService();

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) // Hide that "
                snoozeSystemNotification(sbn);
        }
    }

    @RequiresApi(26)
    private void snoozeSystemNotification(StatusBarNotification sbn) {
        if (sbn.getPackageName().equals("android") && sbn.getNotification().extras.containsKey(EXTRA_FOREGROUND_APPS)) {
            String key = sbn.getNotification().extras.getString(Notification.EXTRA_TITLE);
            if (key == null)
                return;

            String[] svcs = sbn.getNotification().extras.getStringArray(EXTRA_FOREGROUND_APPS);
            String packageName =  getApplicationContext().getPackageName();
            boolean snooze = false;
            for (String svc : svcs) {
                if (packageName.equals(svc)) {
                    snooze = true;
                    break;
                }
            }

            if (snooze)
                snoozeNotification(sbn.getKey(), 10000000000000L);
            //Long.MAX_VALUE = 9223372036854775807 = 292.5 million years -> not working
            //10000000000000 = 317.09792 years -> working
        }
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {
    }
}