/**
 * Copyright (c) 2012 Todoroo Inc
 *
 * See the file "LICENSE" for the full license governing this code.
 */
package com.todoroo.astrid.widget;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.IBinder;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import android.widget.RemoteViews;

import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.TagViewFragment;
import com.todoroo.astrid.activity.TaskEditActivity;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterWithCustomIntent;
import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.core.CoreFilterExposer;
import com.todoroo.astrid.core.SortHelper;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.service.ThemeService;
import com.todoroo.astrid.subtasks.SubtasksHelper;
import com.todoroo.astrid.utility.AstridPreferences;
import com.todoroo.astrid.utility.Constants;

public class TasksWidget extends AppWidgetProvider {

    public static final int THEME_LEGACY = -1;

    public static long suppressUpdateFlag = 0; // Timestamp--don't update widgets if this flag is non-zero and now() is within 5 minutes
    private static final long SUPPRESS_TIME = DateUtilities.ONE_MINUTE * 5;

    static {
        AstridDependencyInjector.initialize();
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {

        try {
            ContextManager.setContext(context);
            super.onUpdate(context, appWidgetManager, appWidgetIds);

            // Start in service to prevent Application Not Responding timeout
            updateWidgets(context);
        } catch (Exception e) {
            Log.e("astrid-update-widget", "widget update error", e); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    /**
     * Update all widgets
     * @param id
     */
    public static void updateWidgets(Context context) {
        if (suppressUpdateFlag > 0 && DateUtilities.now() - suppressUpdateFlag < SUPPRESS_TIME)
            return;
        suppressUpdateFlag = 0;
        context.startService(new Intent(context,
                TasksWidget.WidgetUpdateService.class));
    }

    /**
     * Update widget with the given id
     * @param id
     */
    public static void updateWidget(Context context, int id) {
        Intent intent = new Intent(ContextManager.getContext(),
                TasksWidget.WidgetUpdateService.class);
        intent.putExtra(WidgetUpdateService.EXTRA_WIDGET_ID, id);
        context.startService(intent);
    }

    public static class ConfigActivity extends WidgetConfigActivity {
        @Override
        public void updateWidget() {
            TasksWidget.updateWidget(this, mAppWidgetId);
        }
    }

    public static class WidgetUpdateService extends Service {

        public static final String EXTRA_WIDGET_ID = "widget_id"; //$NON-NLS-1$

        @Autowired
        Database database;

        @Autowired
        TaskService taskService;

        @Autowired
        TagDataService tagDataService;

        @Override
        public void onStart(final Intent intent, int startId) {
            ContextManager.setContext(this);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    startServiceInBackgroundThread(intent);
                }
            }).start();
        }

        public void startServiceInBackgroundThread(Intent intent) {
            ComponentName thisWidget = new ComponentName(this,
                    TasksWidget.class);
            AppWidgetManager manager = AppWidgetManager.getInstance(this);

            int extrasId = AppWidgetManager.INVALID_APPWIDGET_ID;
            if(intent != null)
                extrasId = intent.getIntExtra(EXTRA_WIDGET_ID, extrasId);
            if(extrasId == AppWidgetManager.INVALID_APPWIDGET_ID) {
                int[] ids;
                try {
                    ids = manager.getAppWidgetIds(thisWidget);
                    for(int id : ids) {
                        RemoteViews updateViews = buildUpdate(this, id);
                        manager.updateAppWidget(id, updateViews);
                    }
                } catch (RuntimeException e) {
                    // "System server dead" was sometimes thrown here by the OS. Abort if that happens
                }
            } else {
                int id = extrasId;
                RemoteViews updateViews = buildUpdate(this, id);
                manager.updateAppWidget(id, updateViews);
            }

            stopSelf();
        }

        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }

        @SuppressWarnings("nls")
        public RemoteViews buildUpdate(Context context, int widgetId) {
            DependencyInjectionService.getInstance().inject(this);

            RemoteViews views = getThemedRemoteViews(context);

            int numberOfTasks = getNumberOfTasks();

            TodorooCursor<Task> cursor = null;
            Filter filter = null;
            try {
                filter = getFilter(widgetId);
                if (SubtasksHelper.isTagFilter(filter))
                    ((FilterWithCustomIntent) filter).customTaskList = new ComponentName(context, TagViewFragment.class); // In case legacy widget was created with subtasks fragment
                views.setTextViewText(R.id.widget_title, filter.title);
                views.removeAllViews(R.id.taskbody);

                SharedPreferences publicPrefs = AstridPreferences.getPublicPrefs(this);
                int flags = publicPrefs.getInt(SortHelper.PREF_SORT_FLAGS, 0);
                int sort = publicPrefs.getInt(SortHelper.PREF_SORT_SORT, 0);
                String query = SortHelper.adjustQueryForFlagsAndSort(
                        filter.getSqlQuery(), flags, sort).replaceAll("LIMIT \\d+", "") + " LIMIT " + numberOfTasks;

                String tagName = Preferences.getStringValue(WidgetConfigActivity.PREF_TITLE + widgetId);
                query = SubtasksHelper.applySubtasksToWidgetFilter(filter, query, tagName, numberOfTasks);

                database.openForReading();
                cursor = taskService.fetchFiltered(query, null, Task.ID, Task.TITLE, Task.DUE_DATE, Task.COMPLETION_DATE);
                Task task = new Task();
                int i = 0;
                for (i = 0; i < cursor.getCount() && i < numberOfTasks; i++) {
                    cursor.moveToPosition(i);
                    task.readFromCursor(cursor);

                    String textContent = "";
                    Resources r = context.getResources();
                    int textColor = r
                            .getColor(isDarkTheme() ? R.color.widget_text_color_dark : R.color.widget_text_color_light);
                    if (isLegacyTheme())
                        textColor = r.getColor(android.R.color.white);

                    textContent = task.getValue(Task.TITLE);

                    if(task.isCompleted())
                        textColor = r.getColor(R.color.task_list_done);
                    else if(task.hasDueDate() && task.isOverdue())
                        textColor = r.getColor(R.color.task_list_overdue);

                    RemoteViews row = new RemoteViews(Constants.PACKAGE, R.layout.widget_row);

                    row.setTextViewText(R.id.text, textContent);
                    row.setTextColor(R.id.text, textColor);

                    views.addView(R.id.taskbody, row);

                    RemoteViews separator = new RemoteViews(Constants.PACKAGE, R.layout.widget_separator);
                    boolean isLastRow = (i == cursor.getCount() - 1) || (i == numberOfTasks - 1);
                    if (!isLastRow)
                        views.addView(R.id.taskbody, separator);
                }
                for (; i < numberOfTasks; i++) {
                    RemoteViews row = new RemoteViews(Constants.PACKAGE, R.layout.widget_row);
                    row.setViewVisibility(R.id.text, View.INVISIBLE);
                    views.addView(R.id.taskbody, row);
                }

            } catch (Exception e) {
                // can happen if database is not ready
                Log.e("WIDGET-UPDATE", "Error updating widget", e);
            } finally {
                if(cursor != null)
                    cursor.close();
            }

            Intent listIntent = new Intent(context, TaskListActivity.class);
            String customIntent = Preferences.getStringValue(WidgetConfigActivity.PREF_CUSTOM_INTENT
                    + widgetId);
            if(customIntent != null) {
                String serializedExtras = Preferences.getStringValue(WidgetConfigActivity.PREF_CUSTOM_EXTRAS
                        + widgetId);
                Bundle extras = AndroidUtilities.bundleFromSerializedString(serializedExtras);
                listIntent.putExtras(extras);
            }
            listIntent.putExtra(TaskListActivity.TOKEN_SOURCE, Constants.SOURCE_WIDGET);
            listIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
            if(filter != null) {
                listIntent.putExtra(TaskListFragment.TOKEN_FILTER, filter);
                listIntent.setAction("L" + widgetId + filter.getSqlQuery());
            } else {
                listIntent.setAction("L" + widgetId);
            }
            if (filter instanceof FilterWithCustomIntent) {
                listIntent.putExtras(((FilterWithCustomIntent) filter).customExtras);
            }

            PendingIntent pListIntent = PendingIntent.getActivity(context, widgetId,
                    listIntent, PendingIntent.FLAG_CANCEL_CURRENT);
            if (pListIntent != null)
                views.setOnClickPendingIntent(R.id.taskbody, pListIntent);


            Intent editIntent;
            boolean tablet = AstridPreferences.useTabletLayout(context);
            if (tablet) {
                editIntent = new Intent(context, TaskListActivity.class);
                editIntent.putExtra(TaskListActivity.OPEN_TASK, 0L);
            }
            else
                editIntent = new Intent(context, TaskEditActivity.class);

            editIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
            editIntent.putExtra(TaskEditFragment.OVERRIDE_FINISH_ANIM, false);
            if(filter != null) {
                editIntent.putExtra(TaskListFragment.TOKEN_FILTER, filter);
                if (filter.valuesForNewTasks != null) {
                    String values = AndroidUtilities.contentValuesToSerializedString(filter.valuesForNewTasks);
                    values = PermaSql.replacePlaceholders(values);
                    editIntent.putExtra(TaskEditFragment.TOKEN_VALUES, values);
                    editIntent.setAction("E" + widgetId + values);
                }
                if (tablet) {
                    if (filter instanceof FilterWithCustomIntent) {
                        Bundle customExtras = ((FilterWithCustomIntent) filter).customExtras;
                        editIntent.putExtras(customExtras);
                    }
                }
            } else {
                editIntent.setAction("E" + widgetId);
            }

            PendingIntent pEditIntent = PendingIntent.getActivity(context, -widgetId,
                    editIntent, 0);
            if (pEditIntent != null) {
                views.setOnClickPendingIntent(R.id.widget_button, pEditIntent);
                views.setOnClickPendingIntent(R.id.widget_title, pEditIntent);
            }

            return views;
        }

        private boolean isDarkTheme() {
            int theme = ThemeService.getWidgetTheme();
            return (theme == R.style.Theme || theme == R.style.Theme_Transparent);
        }

        private boolean isLegacyTheme() {
            int theme = ThemeService.getWidgetTheme();
            return theme == THEME_LEGACY;
        }


        /**
         * The reason we use a bunch of different but almost identical layouts is that there is a bug with
         * Android 2.1 (level 7) that doesn't allow setting backgrounds on remote views. I know it's lame,
         * but I didn't see a better solution. Alternatively, we could disallow theming widgets on
         * Android 2.1.
         * @param context
         * @return
         */
        @SuppressWarnings("nls")
        private RemoteViews getThemedRemoteViews(Context context) {
            int theme = ThemeService.getWidgetTheme();
            String packageName = context.getPackageName();
            Resources r = context.getResources();
            int layout;
            RemoteViews views = null;

            int titleColor;
            int buttonDrawable;

            if (isLegacyTheme()) {
                views = new RemoteViews(packageName, R.layout.widget_initialized_legacy);
                views.setTextColor(R.id.widget_title, r.getColor(android.R.color.white));
                views.setInt(R.id.widget_button, "setImageResource", R.drawable.button_plus);
                views.setViewVisibility(R.id.widget_header_separator, View.GONE);
                return views;
            } else if(isDarkTheme()) {
                layout = (theme == R.style.Theme_Transparent ? R.layout.widget_initialized_dark_transparent : R.layout.widget_initialized_dark);
                titleColor = r.getColor(R.color.widget_text_color_dark);
                buttonDrawable = R.drawable.plus_button_blue;
            } else if (theme == R.style.Theme_White) {
                layout = R.layout.widget_initialized_red;
                titleColor = r.getColor(R.color.widget_text_color_light);
                buttonDrawable = R.drawable.plus_button_red;
            } else if (theme == R.style.Theme_White_Alt)  {
                layout = R.layout.widget_initialized;
                titleColor = r.getColor(R.color.widget_text_color_light);
                buttonDrawable = R.drawable.plus_button_blue;
            } else {
                layout = (theme == R.style.Theme_TransparentWhite ? R.layout.widget_initialized_transparent : R.layout.widget_initialized);
                titleColor = r.getColor(R.color.widget_text_color_light);
                buttonDrawable = R.drawable.plus_button_dark_blue;
            }

            views = new RemoteViews(packageName, layout);
            views.setTextColor(R.id.widget_title, titleColor);
            views.setInt(R.id.widget_button, "setImageResource", buttonDrawable);
            return views;
        }

        private int getNumberOfTasks() {
            Display display = ((WindowManager) this.getSystemService(
                    Context.WINDOW_SERVICE)).getDefaultDisplay();

            DisplayMetrics metrics = new DisplayMetrics();
            display.getMetrics(metrics);

            if(metrics.density <= 0.75)
                return 4;
            else
                return 5;
        }

        private Filter getFilter(int widgetId) {

            // base our filter off the inbox filter, replace stuff if we have it
            Filter filter = CoreFilterExposer.buildInboxFilter(getResources());
            String sql = Preferences.getStringValue(WidgetConfigActivity.PREF_SQL + widgetId);
            if(sql != null)
                filter.setSqlQuery(sql);
            String title = Preferences.getStringValue(WidgetConfigActivity.PREF_TITLE + widgetId);
            if(title != null)
                filter.title = title;
            String contentValues = Preferences.getStringValue(WidgetConfigActivity.PREF_VALUES + widgetId);
            if(contentValues != null)
                filter.valuesForNewTasks = AndroidUtilities.contentValuesFromSerializedString(contentValues);

            String customComponent = Preferences.getStringValue(WidgetConfigActivity.PREF_CUSTOM_INTENT
                    + widgetId);
            if (customComponent != null) {
                ComponentName component = ComponentName.unflattenFromString(customComponent);
                filter = new FilterWithCustomIntent(filter.title, filter.title, filter.getSqlQuery(), filter.valuesForNewTasks);
                ((FilterWithCustomIntent) filter).customTaskList = component;
                String serializedExtras = Preferences.getStringValue(WidgetConfigActivity.PREF_CUSTOM_EXTRAS
                        + widgetId);
                Bundle extras = AndroidUtilities.bundleFromSerializedString(serializedExtras);
                ((FilterWithCustomIntent) filter).customExtras = extras;
            }

            return filter;
        }

    }
}
