/*
 *    Copyright 2019 James Fenn
 *
 *    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.james.status.services;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.os.Build;
import android.os.SystemClock;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;

import com.james.status.BuildConfig;
import com.james.status.R;
import com.james.status.activities.AppSettingActivity;
import com.james.status.activities.MainActivity;
import com.james.status.data.AppData;
import com.james.status.data.AppPreferenceData;
import com.james.status.data.NotificationData;
import com.james.status.data.PreferenceData;
import com.james.status.data.icon.AirplaneModeIconData;
import com.james.status.data.icon.AlarmIconData;
import com.james.status.data.icon.BatteryIconData;
import com.james.status.data.icon.BluetoothIconData;
import com.james.status.data.icon.CarrierIconData;
import com.james.status.data.icon.DataIconData;
import com.james.status.data.icon.GpsIconData;
import com.james.status.data.icon.HeadphoneIconData;
import com.james.status.data.icon.IconData;
import com.james.status.data.icon.NetworkIconData;
import com.james.status.data.icon.NfcIconData;
import com.james.status.data.icon.NotificationsIconData;
import com.james.status.data.icon.OrientationIconData;
import com.james.status.data.icon.RingerIconData;
import com.james.status.data.icon.TimeIconData;
import com.james.status.data.icon.WifiIconData;
import com.james.status.receivers.ActivityFullScreenSettingReceiver;
import com.james.status.utils.StaticUtils;
import com.james.status.views.StatusView;

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

import androidx.core.app.NotificationCompat;
import androidx.core.app.TaskStackBuilder;
import androidx.core.content.ContextCompat;

public class StatusServiceImpl {

    public static final String ACTION_CREATE = "com.james.status.ACTION_CREATE";
    public static final String ACTION_START = "com.james.status.ACTION_START";
    public static final String ACTION_STOP = "com.james.status.ACTION_STOP";
    public static final String ACTION_UPDATE = "com.james.status.ACTION_UPDATE";
    public static final String EXTRA_KEEP_OLD = "com.james.status.EXTRA_KEEP_OLD";
    public static final String EXTRA_COLOR = "com.james.status.EXTRA_COLOR";
    public static final String EXTRA_IS_SYSTEM_FULLSCREEN = "com.james.status.EXTRA_IS_SYSTEM_FULLSCREEN";
    public static final String EXTRA_IS_FULLSCREEN = "com.james.status.EXTRA_IS_FULLSCREEN";
    public static final String EXTRA_IS_TRANSPARENT = "com.james.status.EXTRA_IS_TRANSPARENT";
    public static final String EXTRA_PACKAGE = "com.james.status.EXTRA_PACKAGE";
    public static final String EXTRA_ACTIVITY = "com.james.status.EXTRA_ACTIVITY";

    private static final int ID_FOREGROUND = 682;

    private StatusView statusView;
    private View fullscreenView;

    private WindowManager windowManager;

    private String packageName;
    private AppData.ActivityData activityData;
    private AppPreferenceData activityPreference;

    private Service service;

    public StatusServiceImpl(Service service) {
        this.service = service;
    }

    public void onCreate() {
        windowManager = (WindowManager) service.getSystemService(Context.WINDOW_SERVICE);

        if (PreferenceData.STATUS_ENABLED.getValue(service))
            setUp(false);
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        if (!(boolean) PreferenceData.STATUS_ENABLED.getValue(service)) {
            onDestroy();
            disable(service);
            return Service.START_NOT_STICKY;
        }

        if (intent == null) return Service.START_STICKY;
        String action = intent.getAction();
        if (action == null) return Service.START_STICKY;
        switch (action) {
            case ACTION_CREATE:
                if (statusView == null)
                    setUp(false);
                break;
            case ACTION_START:
                setUp(intent.getBooleanExtra(EXTRA_KEEP_OLD, false));
                break;
            case ACTION_STOP:
                onDestroy();
                disable(service);
                break;
            case ACTION_UPDATE:
                if (statusView != null) {
                    if (intent.hasExtra(EXTRA_IS_TRANSPARENT) && intent.getBooleanExtra(EXTRA_IS_TRANSPARENT, false))
                        statusView.setTransparent();
                    else if (intent.hasExtra(EXTRA_COLOR))
                        statusView.setColor(intent.getIntExtra(EXTRA_COLOR, Color.BLACK));

                    statusView.setSystemShowing(intent.getBooleanExtra(EXTRA_IS_SYSTEM_FULLSCREEN, statusView.isSystemShowing()));
                    statusView.setFullscreen(intent.getBooleanExtra(EXTRA_IS_FULLSCREEN, isFullscreen()));

                    if (intent.hasExtra(EXTRA_PACKAGE) && intent.hasExtra(EXTRA_ACTIVITY)) {
                        AppPreferenceData preference = null;

                        packageName = intent.getStringExtra(EXTRA_PACKAGE);
                        activityData = intent.getParcelableExtra(EXTRA_ACTIVITY);

                        if (activityData != null) {
                            preference = new AppPreferenceData(activityData.packageName + "/" + activityData.name);
                            statusView.setIconColor(preference.getIconColor(service));
                            statusView.setTextColor(preference.getTextColor(service));
                        }

                        if (PreferenceData.STATUS_PERSISTENT_NOTIFICATION.getValue(service)) {
                            packageName = intent.getStringExtra(EXTRA_PACKAGE);
                            activityData = intent.getParcelableExtra(EXTRA_ACTIVITY);
                            activityPreference = preference;

                            startForeground(packageName, activityData);
                        } else service.stopForeground(true);
                    }
                }
                return Service.START_STICKY;
        }

        if (PreferenceData.STATUS_PERSISTENT_NOTIFICATION.getValue(service)) {
            if (packageName != null && activityData != null)
                startForeground(packageName, activityData);
            else {
                Intent contentIntent = new Intent(service, MainActivity.class);
                contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

                TaskStackBuilder contentStackBuilder = TaskStackBuilder.create(service);
                contentStackBuilder.addParentStack(MainActivity.class);
                contentStackBuilder.addNextIntent(contentIntent);

                service.startForeground(ID_FOREGROUND, new NotificationCompat.Builder(service)
                        .setSmallIcon(R.drawable.ic_notification)
                        .setColor(ContextCompat.getColor(service, R.color.colorAccent))
                        .setContentTitle(service.getString(R.string.app_name))
                        .setContentIntent(contentStackBuilder.getPendingIntent(0, PendingIntent.FLAG_CANCEL_CURRENT))
                        .build()
                );
            }
        } else service.stopForeground(true);

        return Service.START_STICKY;
    }

    private void startForeground(String packageName, AppData.ActivityData activityData) {
        Intent contentIntent = new Intent(service, MainActivity.class);
        contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        TaskStackBuilder contentStackBuilder = TaskStackBuilder.create(service);
        contentStackBuilder.addParentStack(MainActivity.class);
        contentStackBuilder.addNextIntent(contentIntent);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(service)
                .setSmallIcon(R.drawable.ic_notification)
                .setColor(ContextCompat.getColor(service, R.color.colorAccent))
                .setContentTitle(service.getString(R.string.app_name))
                .setContentText(activityData.name)
                .setSubText(packageName)
                .setContentIntent(contentStackBuilder.getPendingIntent(0, PendingIntent.FLAG_CANCEL_CURRENT));

        if (PreferenceData.STATUS_COLOR_AUTO.getValue(service)) {
            Intent colorIntent = new Intent(service, AppSettingActivity.class);
            colorIntent.putExtra(AppSettingActivity.EXTRA_COMPONENT, activityData.packageName + "/" + activityData.name);
            colorIntent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);

            builder.addAction(R.drawable.ic_notification_color, service.getString(R.string.action_set_color), PendingIntent.getActivity(service, 1, colorIntent, PendingIntent.FLAG_CANCEL_CURRENT));
        }

        boolean isFullscreen = activityPreference != null && activityPreference.isFullScreen(service);
        Intent visibleIntent = new Intent(service, ActivityFullScreenSettingReceiver.class);
        visibleIntent.putExtra(ActivityFullScreenSettingReceiver.EXTRA_COMPONENT, activityData.packageName + "/" + activityData.name);
        visibleIntent.putExtra(ActivityFullScreenSettingReceiver.EXTRA_FULLSCREEN, isFullscreen);

        builder.addAction(R.drawable.ic_notification_visible, service.getString(isFullscreen ? R.string.action_show_status : R.string.action_hide_status), PendingIntent.getBroadcast(service, 0, visibleIntent, PendingIntent.FLAG_CANCEL_CURRENT));

        Intent settingsIntent = new Intent(service, AppSettingActivity.class);
        settingsIntent.putExtra(AppSettingActivity.EXTRA_COMPONENT, activityData.packageName);
        settingsIntent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);

        builder.addAction(R.drawable.ic_notification_settings, service.getString(R.string.action_app_settings), PendingIntent.getActivity(service, 0, settingsIntent, PendingIntent.FLAG_CANCEL_CURRENT));

        service.startForeground(ID_FOREGROUND, builder.build());
    }


    /**
     * I can't remember why this is here or what it does, but it seems important.
     *
     * @param rootIntent a... root intent, possibly part of a plant
     */
    public void onTaskRemoved(Intent rootIntent) {
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
            Intent intent = new Intent(ACTION_UPDATE);
            intent.setClass(service, StatusServiceImpl.class);

            PendingIntent pendingIntent = PendingIntent.getService(service.getApplicationContext(), 1, intent, PendingIntent.FLAG_ONE_SHOT);

            AlarmManager manager = (AlarmManager) service.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
            if (manager != null)
                manager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, pendingIntent);
        }
    }

    /**
     * Decides which overlay type Status should use
     * on a specific device.
     *
     * @return The overlay type for Status to use.
     */
    public int getOverlayType() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
                : WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
    }

    /**
     * Initializes the StatusView if it doesn't exist yet, sets listeners,
     * applies any changes to preferences/icons
     *
     * @param shouldKeepOld whether to reuse the old IconData instances
     */
    public void setUp(boolean shouldKeepOld) {
        if (statusView == null || statusView.getParent() == null) {
            if (statusView != null) {
                windowManager.removeView(statusView);
                statusView.unregister();
            }

            statusView = new StatusView(service);

            WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                    WindowManager.LayoutParams.MATCH_PARENT, StaticUtils.getStatusBarHeight(service),
                    getOverlayType(),
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                    PixelFormat.TRANSLUCENT);

            params.gravity = Gravity.TOP;

            windowManager.addView(statusView, params);
        } else if (!shouldKeepOld)
            statusView.unregister();

        statusView.init();

        if (!shouldKeepOld) {
            if (fullscreenView == null || fullscreenView.getParent() == null) {
                WindowManager.LayoutParams params = new WindowManager.LayoutParams(1, WindowManager.LayoutParams.MATCH_PARENT,
                        getOverlayType(),
                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                        PixelFormat.TRANSPARENT);

                params.gravity = Gravity.START | Gravity.TOP;
                fullscreenView = new View(service);

                windowManager.addView(fullscreenView, params);

                fullscreenView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        if (statusView != null && fullscreenView != null) {
                            if (activityPreference != null && activityPreference.isFullScreenIgnore(service))
                                return;

                            Point size = new Point();
                            windowManager.getDefaultDisplay().getSize(size);
                            statusView.setFullscreen(fullscreenView.getMeasuredHeight() == size.y);
                        }
                    }
                });
            }

            statusView.setIcons(getIcons(service));
            statusView.register();

            if (service instanceof StatusService)
                ((StatusService) service).sendNotifications();
        }

        if (StaticUtils.isAccessibilityServiceRunning(service)) {
            Intent intent = new Intent(AccessibilityService.ACTION_GET_COLOR);
            intent.setClass(service, AccessibilityService.class);
            service.startService(intent);
        }
    }

    public boolean isFullscreen() {
        if (statusView != null && fullscreenView != null) {
            Point size = new Point();
            windowManager.getDefaultDisplay().getSize(size);
            return fullscreenView.getMeasuredHeight() == size.y;
        } else return false;
    }

    public void onDestroy() {
        if (fullscreenView != null) {
            windowManager.removeView(fullscreenView);
            fullscreenView = null;
        }

        if (statusView != null) {
            if (statusView.isRegistered()) statusView.unregister();
            windowManager.removeView(statusView);
            statusView = null;
        }
    }

    public static List<IconData> getIcons(Context context) {
        List<IconData> icons = new ArrayList<>();
        icons.add(new NotificationsIconData(context));
        icons.add(new TimeIconData(context));
        icons.add(new BatteryIconData(context));

        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY) || BuildConfig.DEBUG) {
            icons.add(new NetworkIconData(context));
            icons.add(new CarrierIconData(context));
            icons.add(new DataIconData(context));
        }

        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI) || BuildConfig.DEBUG)
            icons.add(new WifiIconData(context));

        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH) || BuildConfig.DEBUG)
            icons.add(new BluetoothIconData(context));

        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS) || BuildConfig.DEBUG)
            icons.add(new GpsIconData(context));

        icons.add(new AirplaneModeIconData(context));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC) || BuildConfig.DEBUG))
            icons.add(new NfcIconData(context));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP || BuildConfig.DEBUG)
            icons.add(new AlarmIconData(context));

        icons.add(new RingerIconData(context));
        icons.add(new HeadphoneIconData(context));
        icons.add(new OrientationIconData(context));

        return icons;
    }

    public void onNotificationAdded(String key, NotificationData notification) {
        if (statusView != null)
            statusView.sendMessage(NotificationsIconData.class, key, notification);
    }

    public void onNotificationRemoved(String key) {
        if (statusView != null)
            statusView.sendMessage(NotificationsIconData.class, key);
    }

    public static Class getCompatClass(Context context) {
        if (context instanceof StatusService || context instanceof StatusServiceCompat)
            return context.getClass(); //prevents issues disabling services during compatibility switch

        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && !((Boolean) PreferenceData.STATUS_NOTIFICATIONS_COMPAT.getValue(context))
                ? StatusService.class : StatusServiceCompat.class;
    }

    public static void start(Context context) {
        PackageManager pm = context.getPackageManager();
        pm.setComponentEnabledSetting(new ComponentName(context, getCompatClass(context)),
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

        Intent intent = new Intent(ACTION_CREATE);
        intent.setClass(context, getCompatClass(context));
        context.startService(intent);
    }

    public static void stop(Context context) {
        Intent intent = new Intent(ACTION_STOP);
        intent.setClass(context, getCompatClass(context));
        context.startService(intent);
    }

    public static void disable(Context context) {
        Intent intent = new Intent(ACTION_STOP);
        intent.setClass(context, getCompatClass(context));
        context.stopService(intent);

        PackageManager pm = context.getPackageManager();
        pm.setComponentEnabledSetting(new ComponentName(context, getCompatClass(context)),
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    }

}