/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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.android.systemui.shared.recents.model;

import android.app.ActivityManager.TaskDescription;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.view.ViewDebug;

import com.android.systemui.shared.recents.utilities.Utilities;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;

/**
 * A task represents the top most task in the system's task stack.
 */
public class Task {

    public static final String TAG = "Task";

    /* Task callbacks */
    public interface TaskCallbacks {
        /* Notifies when a task has been bound */
        void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
        /* Notifies when a task has been unbound */
        void onTaskDataUnloaded();
        /* Notifies when a task's windowing mode has changed. */
        void onTaskWindowingModeChanged();
    }

    /* The Task Key represents the unique primary key for the task */
    public static class TaskKey {
        @ViewDebug.ExportedProperty(category="recents")
        public final int id;
        @ViewDebug.ExportedProperty(category="recents")
        public int windowingMode;
        @ViewDebug.ExportedProperty(category="recents")
        public final Intent baseIntent;
        @ViewDebug.ExportedProperty(category="recents")
        public final int userId;
        @ViewDebug.ExportedProperty(category="recents")
        public long lastActiveTime;

        // The source component name which started this task
        public final ComponentName sourceComponent;

        private int mHashCode;

        public TaskKey(int id, int windowingMode, Intent intent,
                ComponentName sourceComponent, int userId, long lastActiveTime) {
            this.id = id;
            this.windowingMode = windowingMode;
            this.baseIntent = intent;
            this.sourceComponent = sourceComponent;
            this.userId = userId;
            this.lastActiveTime = lastActiveTime;
            updateHashCode();
        }

        public void setWindowingMode(int windowingMode) {
            this.windowingMode = windowingMode;
            updateHashCode();
        }

        public ComponentName getComponent() {
            return this.baseIntent.getComponent();
        }

        public String getPackageName() {
            if (this.baseIntent.getComponent() != null) {
                return this.baseIntent.getComponent().getPackageName();
            }
            return this.baseIntent.getPackage();
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof TaskKey)) {
                return false;
            }
            TaskKey otherKey = (TaskKey) o;
            return id == otherKey.id
                    && windowingMode == otherKey.windowingMode
                    && userId == otherKey.userId;
        }

        @Override
        public int hashCode() {
            return mHashCode;
        }

        @Override
        public String toString() {
            return "id=" + id + " windowingMode=" + windowingMode + " user=" + userId
                    + " lastActiveTime=" + lastActiveTime;
        }

        private void updateHashCode() {
            mHashCode = Objects.hash(id, windowingMode, userId);
        }
    }

    @ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
    public TaskKey key;

    /**
     * The temporary sort index in the stack, used when ordering the stack.
     */
    public int temporarySortIndexInStack;

    /**
     * The icon is the task description icon (if provided), which falls back to the activity icon,
     * which can then fall back to the application icon.
     */
    public Drawable icon;
    public ThumbnailData thumbnail;
    @ViewDebug.ExportedProperty(category="recents")
    public String title;
    @ViewDebug.ExportedProperty(category="recents")
    public String titleDescription;
    @ViewDebug.ExportedProperty(category="recents")
    public int colorPrimary;
    @ViewDebug.ExportedProperty(category="recents")
    public int colorBackground;
    @ViewDebug.ExportedProperty(category="recents")
    public boolean useLightOnPrimaryColor;

    /**
     * The task description for this task, only used to reload task icons.
     */
    public TaskDescription taskDescription;

    /**
     * The state isLaunchTarget will be set for the correct task upon launching Recents.
     */
    @ViewDebug.ExportedProperty(category="recents")
    public boolean isLaunchTarget;
    @ViewDebug.ExportedProperty(category="recents")
    public boolean isStackTask;
    @ViewDebug.ExportedProperty(category="recents")
    public boolean isSystemApp;
    @ViewDebug.ExportedProperty(category="recents")
    public boolean isDockable;

    /**
     * Resize mode. See {@link ActivityInfo#resizeMode}.
     */
    @ViewDebug.ExportedProperty(category="recents")
    public int resizeMode;

    @ViewDebug.ExportedProperty(category="recents")
    public ComponentName topActivity;

    @ViewDebug.ExportedProperty(category="recents")
    public boolean isLocked;

    private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();

    public Task() {
        // Do nothing
    }

    public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title,
            String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget,
            boolean isStackTask, boolean isSystemApp, boolean isDockable,
            TaskDescription taskDescription, int resizeMode, ComponentName topActivity,
            boolean isLocked) {
        this.key = key;
        this.icon = icon;
        this.thumbnail = thumbnail;
        this.title = title;
        this.titleDescription = titleDescription;
        this.colorPrimary = colorPrimary;
        this.colorBackground = colorBackground;
        this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
                Color.WHITE) > 3f;
        this.taskDescription = taskDescription;
        this.isLaunchTarget = isLaunchTarget;
        this.isStackTask = isStackTask;
        this.isSystemApp = isSystemApp;
        this.isDockable = isDockable;
        this.resizeMode = resizeMode;
        this.topActivity = topActivity;
        this.isLocked = isLocked;
    }

    /**
     * Copies the metadata from another task, but retains the current callbacks.
     */
    public void copyFrom(Task o) {
        this.key = o.key;
        this.icon = o.icon;
        this.thumbnail = o.thumbnail;
        this.title = o.title;
        this.titleDescription = o.titleDescription;
        this.colorPrimary = o.colorPrimary;
        this.colorBackground = o.colorBackground;
        this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
        this.taskDescription = o.taskDescription;
        this.isLaunchTarget = o.isLaunchTarget;
        this.isStackTask = o.isStackTask;
        this.isSystemApp = o.isSystemApp;
        this.isDockable = o.isDockable;
        this.resizeMode = o.resizeMode;
        this.isLocked = o.isLocked;
        this.topActivity = o.topActivity;
    }

    /**
     * Add a callback.
     */
    public void addCallback(TaskCallbacks cb) {
        if (!mCallbacks.contains(cb)) {
            mCallbacks.add(cb);
        }
    }

    /**
     * Remove a callback.
     */
    public void removeCallback(TaskCallbacks cb) {
        mCallbacks.remove(cb);
    }

    /** Updates the task's windowing mode. */
    public void setWindowingMode(int windowingMode) {
        key.setWindowingMode(windowingMode);
        int callbackCount = mCallbacks.size();
        for (int i = 0; i < callbackCount; i++) {
            mCallbacks.get(i).onTaskWindowingModeChanged();
        }
    }

    /** Notifies the callback listeners that this task has been loaded */
    public void notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon) {
        this.icon = applicationIcon;
        this.thumbnail = thumbnailData;
        int callbackCount = mCallbacks.size();
        for (int i = 0; i < callbackCount; i++) {
            mCallbacks.get(i).onTaskDataLoaded(this, thumbnailData);
        }
    }

    /** Notifies the callback listeners that this task has been unloaded */
    public void notifyTaskDataUnloaded(Drawable defaultApplicationIcon) {
        icon = defaultApplicationIcon;
        thumbnail = null;
        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
            mCallbacks.get(i).onTaskDataUnloaded();
        }
    }

    /**
     * Returns the top activity component.
     */
    public ComponentName getTopComponent() {
        return topActivity != null
                ? topActivity
                : key.baseIntent.getComponent();
    }

    @Override
    public boolean equals(Object o) {
        // Check that the id matches
        Task t = (Task) o;
        return key.equals(t.key);
    }

    @Override
    public String toString() {
        return "[" + key.toString() + "] " + title;
    }

    public void dump(String prefix, PrintWriter writer) {
        writer.print(prefix); writer.print(key);
        if (!isDockable) {
            writer.print(" dockable=N");
        }
        if (isLaunchTarget) {
            writer.print(" launchTarget=Y");
        }
        if (isLocked) {
            writer.print(" locked=Y");
        }
        writer.print(" "); writer.print(title);
        writer.println();
    }
}
