package de.danoeh.antennapod.playback.service.internal;

import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
import android.os.Build;
import android.support.v4.media.session.MediaSessionCompat;
import android.util.Log;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.app.NotificationCompat;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.playback.service.MediaButtonReceiver;
import de.danoeh.antennapod.playback.service.PlaybackService;
import de.danoeh.antennapod.playback.service.R;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.common.Converter;
import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.ui.episodes.ImageResourceUtils;
import de.danoeh.antennapod.ui.episodes.TimeSpeedConverter;
import de.danoeh.antennapod.ui.notifications.NotificationUtils;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import de.danoeh.antennapod.playback.base.PlayerStatus;
import org.apache.commons.lang3.ArrayUtils;

public class PlaybackServiceNotificationBuilder {
    private static final String TAG = "PlaybackSrvNotification";
    private static Bitmap defaultIcon = null;

    private final Context context;
    private Playable playable;
    private MediaSessionCompat.Token mediaSessionToken;
    private PlayerStatus playerStatus;
    private Bitmap icon;
    private String position;

    public PlaybackServiceNotificationBuilder(@NonNull Context context) {
        this.context = context;
    }

    public void setPlayable(Playable playable) {
        if (playable != this.playable) {
            clearCache();
        }
        this.playable = playable;
    }

    private void clearCache() {
        this.icon = null;
        this.position = null;
    }

    public void updatePosition(int position, float speed) {
        TimeSpeedConverter converter = new TimeSpeedConverter(speed);
        this.position = Converter.getDurationStringLong(converter.convert(position));
    }

    public boolean isIconCached() {
        return icon != null;
    }

    public void loadIcon() {
        int iconSize = (int) (128 * context.getResources().getDisplayMetrics().density);
        final RequestOptions options = new RequestOptions().centerCrop();
        try {
            icon = Glide.with(context)
                    .asBitmap()
                    .load(playable.getImageLocation())
                    .apply(options)
                    .submit(iconSize, iconSize)
                    .get();
        } catch (ExecutionException e) {
            try {
                icon = Glide.with(context)
                        .asBitmap()
                        .load(ImageResourceUtils.getFallbackImageLocation(playable))
                        .apply(options)
                        .submit(iconSize, iconSize)
                        .get();
            } catch (InterruptedException ignore) {
                Log.e(TAG, "Media icon loader was interrupted");
            } catch (Throwable tr) {
                Log.e(TAG, "Error loading the media icon for the notification", tr);
            }
        } catch (InterruptedException ignore) {
            Log.e(TAG, "Media icon loader was interrupted");
        } catch (Throwable tr) {
            Log.e(TAG, "Error loading the media icon for the notification", tr);
        }
    }

    public Bitmap getCachedIcon() {
        return icon;
    }

    private Bitmap getDefaultIcon() {
        if (defaultIcon == null) {
            defaultIcon = getBitmap(context, R.mipmap.ic_launcher);
        }
        return defaultIcon;
    }

    private static Bitmap getBitmap(VectorDrawable vectorDrawable) {
        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
                vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        vectorDrawable.draw(canvas);
        return bitmap;
    }

    private static Bitmap getBitmap(Context context, int drawableId) {
        Drawable drawable = AppCompatResources.getDrawable(context, drawableId);
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        } else if (drawable instanceof VectorDrawable) {
            return getBitmap((VectorDrawable) drawable);
        } else {
            return null;
        }
    }

    public Notification build() {
        NotificationCompat.Builder notification = new NotificationCompat.Builder(context,
                NotificationUtils.CHANNEL_ID_PLAYING);

        if (playable != null) {
            notification.setContentTitle(playable.getFeedTitle());
            notification.setContentText(playable.getEpisodeTitle());
            addActions(notification, mediaSessionToken, playerStatus);

            if (icon != null) {
                notification.setLargeIcon(icon);
            } else {
                notification.setLargeIcon(getDefaultIcon());
            }

            if (Build.VERSION.SDK_INT < 29) {
                notification.setSubText(position);
            }
        } else {
            notification.setContentTitle(context.getString(R.string.app_name));
            notification.setContentText("Loading. If this does not go away, play any episode and contact us.");
        }

        notification.setContentIntent(getPlayerActivityPendingIntent());
        notification.setWhen(0);
        notification.setSmallIcon(R.drawable.ic_notification);
        notification.setOngoing(false);
        notification.setOnlyAlertOnce(true);
        notification.setShowWhen(false);
        notification.setPriority(UserPreferences.getNotifyPriority());
        notification.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
        notification.setColor(NotificationCompat.COLOR_DEFAULT);
        return notification.build();
    }

    private PendingIntent getPlayerActivityPendingIntent() {
        return PendingIntent.getActivity(context, R.id.pending_intent_player_activity,
                PlaybackService.getPlayerActivityIntent(context), PendingIntent.FLAG_UPDATE_CURRENT
                        | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
    }

    private void addActions(NotificationCompat.Builder notification, MediaSessionCompat.Token mediaSessionToken,
                            PlayerStatus playerStatus) {
        ArrayList<Integer> compactActionList = new ArrayList<>();

        int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction

        PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
                KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
        notification.addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label),
                rewindButtonPendingIntent);
        compactActionList.add(numActions);
        numActions++;

        if (playerStatus == PlayerStatus.PLAYING) {
            PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction(
                    KeyEvent.KEYCODE_MEDIA_PAUSE, numActions);
            notification.addAction(R.drawable.ic_notification_pause, //pause action
                    context.getString(R.string.pause_label),
                    pauseButtonPendingIntent);
        } else {
            PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction(
                    KeyEvent.KEYCODE_MEDIA_PLAY, numActions);
            notification.addAction(R.drawable.ic_notification_play, //play action
                    context.getString(R.string.play_label),
                    playButtonPendingIntent);
        }
        compactActionList.add(numActions++);

        // ff follows play, then we have skip (if it's present)
        PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction(
                KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions);
        notification.addAction(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label),
                ffButtonPendingIntent);
        compactActionList.add(numActions);
        numActions++;

        if (UserPreferences.showNextChapterOnFullNotification() && playable.getChapters() != null) {
            PendingIntent nextChapterPendingIntent = getPendingIntentForCustomMediaAction(
                    PlaybackService.CUSTOM_ACTION_NEXT_CHAPTER, numActions);
            notification.addAction(R.drawable.ic_notification_next_chapter, context.getString(R.string.next_chapter),
                    nextChapterPendingIntent);
            numActions++;
        }

        if (UserPreferences.showSkipOnFullNotification()) {
            PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
                    KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
            notification.addAction(R.drawable.ic_notification_skip, context.getString(R.string.skip_episode_label),
                    skipButtonPendingIntent);
            numActions++;
        }

        PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction(
                KeyEvent.KEYCODE_MEDIA_STOP, numActions);
        notification.setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
                .setMediaSession(mediaSessionToken)
                .setShowActionsInCompactView(ArrayUtils.toPrimitive(compactActionList.toArray(new Integer[0])))
                .setShowCancelButton(true)
                .setCancelButtonIntent(stopButtonPendingIntent));
    }

    private PendingIntent getPendingIntentForMediaAction(int keycodeValue, int requestCode) {
        Intent intent = new Intent(context, PlaybackService.class);
        intent.setAction("MediaCode" + keycodeValue);
        intent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, keycodeValue);

        if (Build.VERSION.SDK_INT >= 26) {
            return PendingIntent.getForegroundService(context, requestCode, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        } else {
            return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT
                    | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
        }
    }

    private PendingIntent getPendingIntentForCustomMediaAction(String action, int requestCode) {
        Intent intent = new Intent(context, PlaybackService.class);
        intent.setAction("MediaAction" + action);
        intent.putExtra(MediaButtonReceiver.EXTRA_CUSTOM_ACTION, action);

        if (Build.VERSION.SDK_INT >= 26) {
            return PendingIntent.getForegroundService(context, requestCode, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        } else {
            return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT
                    | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
        }
    }

    public void setMediaSessionToken(MediaSessionCompat.Token mediaSessionToken) {
        this.mediaSessionToken = mediaSessionToken;
    }

    public void setPlayerStatus(PlayerStatus playerStatus) {
        this.playerStatus = playerStatus;
    }

    public PlayerStatus getPlayerStatus() {
        return playerStatus;
    }
}
