/*
 * Copyright (C) 2021 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 android.app.people;

import android.annotation.NonNull;
import android.app.Person;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;

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

/**
 * The People Space tile contains all relevant information to render a tile in People Space: namely
 * the data of any visible conversation notification associated, associated statuses, and the last
 * interaction time.
 *
 * @hide
 */
public class PeopleSpaceTile implements Parcelable {

    public static final int SHOW_CONVERSATIONS = 1 << 0;
    public static final int BLOCK_CONVERSATIONS =  1 << 1;
    public static final int SHOW_IMPORTANT_CONVERSATIONS = 1 << 2;
    public static final int SHOW_STARRED_CONTACTS = 1 << 3;
    public static final int SHOW_CONTACTS = 1 << 4;

    private String mId;
    private CharSequence mUserName;
    private Icon mUserIcon;
    private UserHandle mUserHandle;
    private Uri mContactUri;
    private String mPackageName;
    private String mBirthdayText;
    private long mLastInteractionTimestamp;
    private boolean mIsImportantConversation;
    private String mNotificationKey;
    private CharSequence mNotificationContent;
    private CharSequence mNotificationSender;
    private String mNotificationCategory;
    private Uri mNotificationDataUri;
    private int mMessagesCount;
    private Intent mIntent;
    private long mNotificationTimestamp;
    private List<ConversationStatus> mStatuses;
    private boolean mCanBypassDnd;
    private boolean mIsPackageSuspended;
    private boolean mIsUserQuieted;
    private int mNotificationPolicyState;
    private float mContactAffinity;

    private PeopleSpaceTile(Builder b) {
        mId = b.mId;
        mUserName = b.mUserName;
        mUserIcon = b.mUserIcon;
        mContactUri = b.mContactUri;
        mUserHandle = b.mUserHandle;
        mPackageName = b.mPackageName;
        mBirthdayText = b.mBirthdayText;
        mLastInteractionTimestamp = b.mLastInteractionTimestamp;
        mIsImportantConversation = b.mIsImportantConversation;
        mNotificationKey = b.mNotificationKey;
        mNotificationContent = b.mNotificationContent;
        mNotificationSender = b.mNotificationSender;
        mNotificationCategory = b.mNotificationCategory;
        mNotificationDataUri = b.mNotificationDataUri;
        mMessagesCount = b.mMessagesCount;
        mIntent = b.mIntent;
        mNotificationTimestamp = b.mNotificationTimestamp;
        mStatuses = b.mStatuses;
        mCanBypassDnd = b.mCanBypassDnd;
        mIsPackageSuspended = b.mIsPackageSuspended;
        mIsUserQuieted = b.mIsUserQuieted;
        mNotificationPolicyState = b.mNotificationPolicyState;
        mContactAffinity = b.mContactAffinity;
    }

    public String getId() {
        return mId;
    }

    public CharSequence getUserName() {
        return mUserName;
    }

    public Icon getUserIcon() {
        return mUserIcon;
    }

    /** Returns the Uri associated with the user in Android Contacts database. */
    public Uri getContactUri() {
        return mContactUri;
    }

    public UserHandle getUserHandle() {
        return mUserHandle;
    }

    public String getPackageName() {
        return mPackageName;
    }

    public String getBirthdayText() {
        return mBirthdayText;
    }

    /** Returns the timestamp of the last interaction. */
    public long getLastInteractionTimestamp() {
        return mLastInteractionTimestamp;
    }

    /**
     * Whether the conversation is important.
     */
    public boolean isImportantConversation() {
        return mIsImportantConversation;
    }

    /**
     * If a notification is currently active that maps to the relevant shortcut ID, provides the
     * associated notification's key.
     */
    public String getNotificationKey() {
        return mNotificationKey;
    }

    public CharSequence getNotificationContent() {
        return mNotificationContent;
    }

    public CharSequence getNotificationSender() {
        return mNotificationSender;
    }

    public String getNotificationCategory() {
        return mNotificationCategory;
    }

    public Uri getNotificationDataUri() {
        return mNotificationDataUri;
    }

    public int getMessagesCount() {
        return mMessagesCount;
    }

    /**
     * Provides an intent to launch. If present, we should manually launch the intent on tile
     * click, rather than calling {@link android.content.pm.LauncherApps} to launch the shortcut ID.
     *
     * <p>This field should only be used if manually constructing a tile without an associated
     * shortcut to launch (i.e. birthday tiles).
     */
    public Intent getIntent() {
        return mIntent;
    }

    /** Returns the timestamp of the last notification. */
    public long getNotificationTimestamp() {
        return mNotificationTimestamp;
    }

    /** Returns the statuses associated with the tile. */
    public List<ConversationStatus> getStatuses() {
        return mStatuses;
    }

    /**
     * Whether the app associated with the conversation can bypass DND.
     */
    public boolean canBypassDnd() {
        return mCanBypassDnd;
    }

    /**
     * Whether the app associated with the conversation is suspended.
     */
    public boolean isPackageSuspended() {
        return mIsPackageSuspended;
    }

    /**
     * Whether the user associated with the conversation is quieted.
     */
    public boolean isUserQuieted() {
        return mIsUserQuieted;
    }

    /**
     * Returns the state of notifications for the conversation.
     */
    public int getNotificationPolicyState() {
        return mNotificationPolicyState;
    }

    /**
     * Returns the contact affinity (whether the contact is starred).
     */
    public float getContactAffinity() {
        return mContactAffinity;
    }

    /** Converts a {@link PeopleSpaceTile} into a {@link PeopleSpaceTile.Builder}. */
    public Builder toBuilder() {
        Builder builder =
                new Builder(mId, mUserName, mUserIcon, mIntent);
        builder.setContactUri(mContactUri);
        builder.setUserHandle(mUserHandle);
        builder.setPackageName(mPackageName);
        builder.setBirthdayText(mBirthdayText);
        builder.setLastInteractionTimestamp(mLastInteractionTimestamp);
        builder.setIsImportantConversation(mIsImportantConversation);
        builder.setNotificationKey(mNotificationKey);
        builder.setNotificationContent(mNotificationContent);
        builder.setNotificationSender(mNotificationSender);
        builder.setNotificationCategory(mNotificationCategory);
        builder.setNotificationDataUri(mNotificationDataUri);
        builder.setMessagesCount(mMessagesCount);
        builder.setIntent(mIntent);
        builder.setNotificationTimestamp(mNotificationTimestamp);
        builder.setStatuses(mStatuses);
        builder.setCanBypassDnd(mCanBypassDnd);
        builder.setIsPackageSuspended(mIsPackageSuspended);
        builder.setIsUserQuieted(mIsUserQuieted);
        builder.setNotificationPolicyState(mNotificationPolicyState);
        builder.setContactAffinity(mContactAffinity);
        return builder;
    }

    /** Builder to create a {@link PeopleSpaceTile}. */
    public static class Builder {
        private String mId;
        private CharSequence mUserName;
        private Icon mUserIcon;
        private Uri mContactUri;
        private UserHandle mUserHandle;
        private String mPackageName;
        private String mBirthdayText;
        private long mLastInteractionTimestamp;
        private boolean mIsImportantConversation;
        private String mNotificationKey;
        private CharSequence mNotificationContent;
        private CharSequence mNotificationSender;
        private String mNotificationCategory;
        private Uri mNotificationDataUri;
        private int mMessagesCount;
        private Intent mIntent;
        private long mNotificationTimestamp;
        private List<ConversationStatus> mStatuses;
        private boolean mCanBypassDnd;
        private boolean mIsPackageSuspended;
        private boolean mIsUserQuieted;
        private int mNotificationPolicyState;
        private float mContactAffinity;

        /** Builder for use only if a shortcut is not available for the tile. */
        public Builder(String id, CharSequence userName, Icon userIcon, Intent intent) {
            mId = id;
            mUserName = userName;
            mUserIcon = userIcon;
            mIntent = intent;
            mPackageName = intent == null ? null : intent.getPackage();
            mNotificationPolicyState = SHOW_CONVERSATIONS;
        }

        public Builder(ShortcutInfo info, LauncherApps launcherApps) {
            mId = info.getId();
            mUserName = info.getLabel();
            mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0));
            mUserHandle = info.getUserHandle();
            mPackageName = info.getPackage();
            mContactUri = getContactUri(info);
            mNotificationPolicyState = SHOW_CONVERSATIONS;
        }

        public Builder(ConversationChannel channel, LauncherApps launcherApps) {
            ShortcutInfo info = channel.getShortcutInfo();
            mId = info.getId();
            mUserName = info.getLabel();
            mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0));
            mUserHandle = info.getUserHandle();
            mPackageName = info.getPackage();
            mContactUri = getContactUri(info);
            mStatuses = channel.getStatuses();
            mLastInteractionTimestamp = channel.getLastEventTimestamp();
            mIsImportantConversation = channel.getNotificationChannel() != null
                    && channel.getNotificationChannel().isImportantConversation();
            mCanBypassDnd = channel.getNotificationChannel() != null
                    && channel.getNotificationChannel().canBypassDnd();
            mNotificationPolicyState = SHOW_CONVERSATIONS;
        }

        /** Returns the Contact's Uri if present. */
        public Uri getContactUri(ShortcutInfo info) {
            if (info.getPersons() == null || info.getPersons().length != 1) {
                return null;
            }
            // TODO(b/175584929): Update to use the Uri from PeopleService directly
            Person person = info.getPersons()[0];
            return person.getUri() == null ? null : Uri.parse(person.getUri());
        }

        /** Sets the ID for the tile. */
        public Builder setId(String id) {
            mId = id;
            return this;
        }

        /** Sets the user name. */
        public Builder setUserName(CharSequence userName) {
            mUserName = userName;
            return this;
        }

        /** Sets the icon shown for the user. */
        public Builder setUserIcon(Icon userIcon) {
            mUserIcon = userIcon;
            return this;
        }

        /** Sets the Uri associated with the user in Android Contacts database. */
        public Builder setContactUri(Uri uri) {
            mContactUri = uri;
            return this;
        }

        /** Sets the associated {@code userHandle}. */
        public Builder setUserHandle(UserHandle userHandle) {
            mUserHandle = userHandle;
            return this;
        }

        /** Sets the package shown that provided the information. */
        public Builder setPackageName(String packageName) {
            mPackageName = packageName;
            return this;
        }

        /** Sets the status text. */
        public Builder setBirthdayText(String birthdayText) {
            mBirthdayText = birthdayText;
            return this;
        }

        /** Sets the last interaction timestamp. */
        public Builder setLastInteractionTimestamp(long lastInteractionTimestamp) {
            mLastInteractionTimestamp = lastInteractionTimestamp;
            return this;
        }

        /** Sets whether the conversation is important. */
        public Builder setIsImportantConversation(boolean isImportantConversation) {
            mIsImportantConversation = isImportantConversation;
            return this;
        }

        /** Sets the associated notification's key. */
        public Builder setNotificationKey(String notificationKey) {
            mNotificationKey = notificationKey;
            return this;
        }

        /** Sets the associated notification's content. */
        public Builder setNotificationContent(CharSequence notificationContent) {
            mNotificationContent = notificationContent;
            return this;
        }

        /** Sets the associated notification's sender. */
        public Builder setNotificationSender(CharSequence notificationSender) {
            mNotificationSender = notificationSender;
            return this;
        }

        /** Sets the associated notification's category. */
        public Builder setNotificationCategory(String notificationCategory) {
            mNotificationCategory = notificationCategory;
            return this;
        }

        /** Sets the associated notification's data URI. */
        public Builder setNotificationDataUri(Uri notificationDataUri) {
            mNotificationDataUri = notificationDataUri;
            return this;
        }

        /** Sets the number of messages associated with the Tile. */
        public Builder setMessagesCount(int messagesCount) {
            mMessagesCount = messagesCount;
            return this;
        }

        /** Sets an intent to launch on click. */
        public Builder setIntent(Intent intent) {
            mIntent = intent;
            return this;
        }

        /** Sets the notification timestamp. */
        public Builder setNotificationTimestamp(long notificationTimestamp) {
            mNotificationTimestamp = notificationTimestamp;
            return this;
        }

        /** Sets the statuses. */
        public Builder setStatuses(List<ConversationStatus> statuses) {
            mStatuses = statuses;
            return this;
        }

        /** Sets whether the conversation channel can bypass DND. */
        public Builder setCanBypassDnd(boolean canBypassDnd) {
            mCanBypassDnd = canBypassDnd;
            return this;
        }

        /** Sets whether the package is suspended. */
        public Builder setIsPackageSuspended(boolean isPackageSuspended) {
            mIsPackageSuspended = isPackageSuspended;
            return this;
        }

        /** Sets whether the user has been quieted. */
        public Builder setIsUserQuieted(boolean isUserQuieted) {
            mIsUserQuieted = isUserQuieted;
            return this;
        }

        /** Sets the state of blocked notifications for the conversation. */
        public Builder setNotificationPolicyState(int notificationPolicyState) {
            mNotificationPolicyState = notificationPolicyState;
            return this;
        }

        /** Sets the contact's affinity. */
        public Builder setContactAffinity(float contactAffinity) {
            mContactAffinity = contactAffinity;
            return this;
        }

        /** Builds a {@link PeopleSpaceTile}. */
        @NonNull
        public PeopleSpaceTile build() {
            return new PeopleSpaceTile(this);
        }
    }

    public PeopleSpaceTile(Parcel in) {
        mId = in.readString();
        mUserName = in.readCharSequence();
        mUserIcon = in.readParcelable(Icon.class.getClassLoader(), android.graphics.drawable.Icon.class);
        mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
        mUserHandle = in.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class);
        mPackageName = in.readString();
        mBirthdayText = in.readString();
        mLastInteractionTimestamp = in.readLong();
        mIsImportantConversation = in.readBoolean();
        mNotificationKey = in.readString();
        mNotificationContent = in.readCharSequence();
        mNotificationSender = in.readCharSequence();
        mNotificationCategory = in.readString();
        mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
        mMessagesCount = in.readInt();
        mIntent = in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class);
        mNotificationTimestamp = in.readLong();
        mStatuses = new ArrayList<>();
        in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader(), android.app.people.ConversationStatus.class);
        mCanBypassDnd = in.readBoolean();
        mIsPackageSuspended = in.readBoolean();
        mIsUserQuieted = in.readBoolean();
        mNotificationPolicyState = in.readInt();
        mContactAffinity = in.readFloat();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mId);
        dest.writeCharSequence(mUserName);
        dest.writeParcelable(mUserIcon, flags);
        dest.writeParcelable(mContactUri, flags);
        dest.writeParcelable(mUserHandle, flags);
        dest.writeString(mPackageName);
        dest.writeString(mBirthdayText);
        dest.writeLong(mLastInteractionTimestamp);
        dest.writeBoolean(mIsImportantConversation);
        dest.writeString(mNotificationKey);
        dest.writeCharSequence(mNotificationContent);
        dest.writeCharSequence(mNotificationSender);
        dest.writeString(mNotificationCategory);
        dest.writeParcelable(mNotificationDataUri, flags);
        dest.writeInt(mMessagesCount);
        dest.writeParcelable(mIntent, flags);
        dest.writeLong(mNotificationTimestamp);
        dest.writeParcelableList(mStatuses, flags);
        dest.writeBoolean(mCanBypassDnd);
        dest.writeBoolean(mIsPackageSuspended);
        dest.writeBoolean(mIsUserQuieted);
        dest.writeInt(mNotificationPolicyState);
        dest.writeFloat(mContactAffinity);
    }

    public static final @android.annotation.NonNull
            Creator<PeopleSpaceTile> CREATOR = new Creator<PeopleSpaceTile>() {
                public PeopleSpaceTile createFromParcel(Parcel source) {
                    return new PeopleSpaceTile(source);
                }
                public PeopleSpaceTile[] newArray(int size) {
                    return new PeopleSpaceTile[size];
                }
            };

    /** Converts {@code drawable} to a {@link Icon}. */
    public static Icon convertDrawableToIcon(Drawable drawable) {
        if (drawable == null) {
            return null;
        }

        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            if (bitmapDrawable.getBitmap() != null) {
                return Icon.createWithBitmap(bitmapDrawable.getBitmap());
            }
        }

        Bitmap bitmap;
        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
            // Single color bitmap will be created of 1x1 pixel
        } else {
            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return Icon.createWithBitmap(bitmap);
    }
}
