/*
 * Copyright (C) 2016 Actinarium
 *
 * 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.actinarium.rhythm.control;

import android.app.Application;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.app.NotificationCompat;
import com.actinarium.rhythm.RhythmOverlay;


/**
 * An intent service backing Quick Control notification functionality. Requires {@link Application} to implement {@link
 * RhythmControl.Host} to access the application’s {@link RhythmControl} singleton instance
 *
 * @author Paul Danyliuk
 */
public class RhythmNotificationService extends IntentService {

    static final String ACTION_SHOW_QUICK_CONTROL = "com.actinarium.rhythm.action.SHOW_QUICK_CONTROL";
    static final String ACTION_NEXT_GROUP = "com.actinarium.rhythm.action.NEXT_GROUP";
    static final String ACTION_NEXT_OVERLAY = "com.actinarium.rhythm.action.NEXT_OVERLAY";
    static final String ACTION_DISMISS_QUICK_CONTROL = "com.actinarium.rhythm.action.DISMISS_QUICK_CONTROL";

    static final String EXTRA_NOTIFICATION_ID = "com.actinarium.rhythm.extra.NOTIFICATION_ID";

    private static final int NOTIFICATION_ICON_COLOR = 0x105AAE;
    private static final int NOTIFICATION_ERROR_COLOR = 0xEF4343;

    public RhythmNotificationService() {
        super("RhythmService");
    }

    /**
     * Show the “Quick Control” notification, which allows to switch overlays for all connected Rhythm
     * groups without leaving the app under development. Can be used to update the notification as well.
     *
     * @param context        to start the service
     * @param notificationId Notification ID, must be unique across the app
     */
    static void showNotification(Context context, int notificationId) {
        Intent intent = new Intent(context, RhythmNotificationService.class);
        intent.setAction(ACTION_SHOW_QUICK_CONTROL);
        intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_SHOW_QUICK_CONTROL.equals(action)) {
                final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, Integer.MIN_VALUE);
                handleShowNotification(notificationId);
            } else if (ACTION_NEXT_OVERLAY.equals(action)) {
                handleNextOverlay();
            } else if (ACTION_NEXT_GROUP.equals(action)) {
                handleNextGroup();
            } else if (ACTION_DISMISS_QUICK_CONTROL.equals(action)) {
                handleDismissQuickConfig();
            }
        }
    }

    private void handleShowNotification(int notificationId) {
        Application application = getApplication();
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        Notification notification;

        // If the application is not the host - show error and return.
        if (!(application instanceof RhythmControl.Host)) {
            notification = makeCommonNotification(getString(R.string.arl_no_host_text))
                    .setColor(NOTIFICATION_ERROR_COLOR)
                    .setContentTitle(getString(R.string.arl_no_host_title))
                    .build();
            manager.notify(notificationId, notification);
            return;
        }

        RhythmControl control = ((RhythmControl.Host) application).getRhythmControl();
        RhythmGroup currentGroup = control.getCurrentNotificationGroup();

        // If there are no groups yet - show warning and return.
        if (currentGroup == null) {
            notification = makeCommonNotification(getString(R.string.arl_no_groups_text))
                    .setColor(NOTIFICATION_ERROR_COLOR)
                    .setContentTitle(getString(R.string.arl_no_groups_title))
                    .build();
            manager.notify(notificationId, notification);
            return;
        }

        // Now if everything is OK:
        Intent nextGroupAction = new Intent(this, RhythmNotificationService.class);
        nextGroupAction.setAction(ACTION_NEXT_GROUP);
        PendingIntent piNextGroupAction = PendingIntent.getService(this, 0, nextGroupAction, PendingIntent.FLAG_UPDATE_CURRENT);

        Intent nextOverlayAction = new Intent(this, RhythmNotificationService.class);
        nextOverlayAction.setAction(ACTION_NEXT_OVERLAY);
        PendingIntent piNextOverlayAction = PendingIntent.getService(this, 0, nextOverlayAction, PendingIntent.FLAG_UPDATE_CURRENT);

        Intent dismissAction = new Intent(this, RhythmNotificationService.class);
        dismissAction.setAction(ACTION_DISMISS_QUICK_CONTROL);
        PendingIntent piDismissAction = PendingIntent.getService(this, 0, dismissAction, PendingIntent.FLAG_UPDATE_CURRENT);

        // todo: another action when notification is clicked (control activity will be added in v1.0)

        // Determine what to write in notification
        RhythmOverlay currentOverlay = currentGroup.getCurrentOverlay();
        String groupText = getString(R.string.arl_group, currentGroup.toString());
        String overlayText = currentOverlay == null ?
                getString(R.string.arl_no_overlay) : getString(R.string.arl_overlay, currentOverlay.toString());

        // Finally, build and display the notification
        notification = makeCommonNotification(overlayText)
                .setColor(NOTIFICATION_ICON_COLOR)
                .setContentTitle(groupText)
                .setDeleteIntent(piDismissAction)
                .addAction(new NotificationCompat.Action(R.drawable.arl_loop, getString(R.string.arl_next_group), piNextGroupAction))
                .addAction(new NotificationCompat.Action(R.drawable.arl_loop, getString(R.string.arl_next_overlay), piNextOverlayAction))
                .build();
        manager.notify(notificationId, notification);
    }

    private NotificationCompat.Builder makeCommonNotification(String text) {
        final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.arl_rhythm)
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(false)
                .setShowWhen(false)
                .setContentText(text)
                .setStyle(new NotificationCompat.BigTextStyle().bigText(text));

        // Old androids throw an exception when the notification doesn't have content intent
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
            Intent contentAction = new Intent(this, RhythmNotificationService.class);
            contentAction.setAction(ACTION_NEXT_OVERLAY);
            PendingIntent piContentAction = PendingIntent.getService(this, 0, contentAction, PendingIntent.FLAG_UPDATE_CURRENT);
            builder.setContentIntent(piContentAction);
        }

        return builder;
    }

    private void handleNextGroup() {
        Application application = getApplication();
        if (application instanceof RhythmControl.Host) {
            final RhythmControl rhythmControl = ((RhythmControl.Host) application).getRhythmControl();
            Handler handler = new Handler(Looper.getMainLooper());
            // Not using anonymous classes here to avoid leaking context
            handler.post(new NextGroupRunnable(rhythmControl));
        }
    }

    private void handleNextOverlay() {
        Application application = getApplication();
        if (application instanceof RhythmControl.Host) {
            final RhythmControl rhythmControl = ((RhythmControl.Host) application).getRhythmControl();
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new NextOverlayRunnable(rhythmControl.getCurrentNotificationGroup()));
        }
    }

    private void handleDismissQuickConfig() {
        Application application = getApplication();
        if (application instanceof RhythmControl.Host) {
            final RhythmControl rhythmControl = ((RhythmControl.Host) application).getRhythmControl();
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new HideAllOverlaysRunnable(rhythmControl));
        }
    }

    /**
     * Runnable to dispatch next group call to the UI thread
     */
    private static class NextGroupRunnable implements Runnable {

        private RhythmControl mControl;

        public NextGroupRunnable(RhythmControl control) {
            mControl = control;
        }

        @Override
        public void run() {
            mControl.selectNextNotificationGroup();
        }
    }

    /**
     * Runnable to dispatch next overlay call to the UI thread
     */
    private static class NextOverlayRunnable implements Runnable {

        private RhythmGroup mGroup;

        public NextOverlayRunnable(RhythmGroup group) {
            mGroup = group;
        }

        @Override
        public void run() {
            mGroup.selectNextOverlay();
        }
    }

    /**
     * Runnable to dispatch hide all overlays call to the UI thread
     */
    private static class HideAllOverlaysRunnable implements Runnable {

        private RhythmControl mControl;

        public HideAllOverlaysRunnable(RhythmControl control) {
            mControl = control;
        }

        @Override
        public void run() {
            mControl.onNotificationDismiss();
        }
    }
}