/*
 * Copyright (C) 2022 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.wallpaper.model;

import static com.android.wallpaper.model.CreativeCategory.KEY_WALLPAPER_SAVE_CREATIVE_CATEGORY_WALLPAPER;

import android.annotation.Nullable;
import android.app.WallpaperInfo;
import android.content.ClipData;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.text.TextUtils;
import android.util.Log;

import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;

import com.android.wallpaper.asset.Asset;
import com.android.wallpaper.asset.CreativeWallpaperThumbAsset;

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

/**
 * Represents a creative live wallpaper component.
 */
public class CreativeWallpaperInfo extends LiveWallpaperInfo {

    public static final Creator<CreativeWallpaperInfo> CREATOR =
            new Creator<CreativeWallpaperInfo>() {
                @Override
                public CreativeWallpaperInfo createFromParcel(Parcel in) {
                    return new CreativeWallpaperInfo(in);
                }

                @Override
                public CreativeWallpaperInfo[] newArray(int size) {
                    return new CreativeWallpaperInfo[size];
                }
            };

    private Uri mConfigPreviewUri;
    private Uri mCleanPreviewUri;
    private Uri mDeleteUri;
    private Uri mThumbnailUri;
    private Uri mShareUri;
    private String mTitle;
    private String mAuthor;
    private String mDescription;
    private String mContentDescription;
    private boolean mIsCurrent;
    private String mGroupName;

    private static final String TAG = "CreativeWallpaperInfo";

    private ArrayList<WallpaperAction> mEffectsToggles = new ArrayList<>();
    private String mEffectsBottomSheetTitle;
    private String mEffectsBottomSheetSubtitle;
    private Uri mClearActionsUri;
    private Uri mEffectsUri = null;
    private String mCurrentlyAppliedEffectId = null;

    public CreativeWallpaperInfo(WallpaperInfo info, String title, @Nullable String author,
            @Nullable String description, String contentDescription, Uri configPreviewUri,
            Uri cleanPreviewUri, Uri deleteUri, Uri thumbnailUri, Uri shareUri, String groupName,
            boolean isCurrent) {
        this(info, /* visibleTitle= */ false, /* collectionId= */ null);
        mTitle = title;
        mAuthor = author;
        mDescription = description;
        mContentDescription = contentDescription;
        mConfigPreviewUri = configPreviewUri;
        mCleanPreviewUri = cleanPreviewUri;
        mDeleteUri = deleteUri;
        mThumbnailUri = thumbnailUri;
        mShareUri = shareUri;
        mIsCurrent = isCurrent;
        mGroupName = groupName;
    }

    public CreativeWallpaperInfo(WallpaperInfo info, boolean isCurrent) {
        this(info, false, null);
        mIsCurrent = isCurrent;
    }

    public CreativeWallpaperInfo(WallpaperInfo info, boolean visibleTitle,
            @Nullable String collectionId) {
        super(info, visibleTitle, collectionId);
    }

    protected CreativeWallpaperInfo(Parcel in) {
        super(in);
        mTitle = in.readString();
        mAuthor = in.readString();
        mDescription = in.readString();
        mContentDescription = in.readString();
        mConfigPreviewUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
        mCleanPreviewUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
        mDeleteUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
        mThumbnailUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
        mShareUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
        mIsCurrent = in.readBoolean();
        mGroupName = in.readString();
        mCurrentlyAppliedEffectId = in.readString();
        mEffectsUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
        mClearActionsUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
        mEffectsToggles = in.readArrayList(WallpaperAction.class.getClassLoader(),
                WallpaperAction.class);
        mEffectsBottomSheetTitle = in.readString();
        mEffectsBottomSheetSubtitle = in.readString();
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        super.writeToParcel(parcel, flags);
        parcel.writeString(mTitle);
        parcel.writeString(mAuthor);
        parcel.writeString(mDescription);
        parcel.writeString(mContentDescription);
        parcel.writeParcelable(mConfigPreviewUri, flags);
        parcel.writeParcelable(mCleanPreviewUri, flags);
        parcel.writeParcelable(mDeleteUri, flags);
        parcel.writeParcelable(mThumbnailUri, flags);
        parcel.writeParcelable(mShareUri, flags);
        parcel.writeBoolean(mIsCurrent);
        parcel.writeString(mGroupName);
        parcel.writeString(mCurrentlyAppliedEffectId);
        parcel.writeParcelable(mEffectsUri, flags);
        parcel.writeParcelable(mClearActionsUri, flags);
        parcel.writeList(mEffectsToggles);
        parcel.writeString(mEffectsBottomSheetTitle);
        parcel.writeString(mEffectsBottomSheetSubtitle);
    }

    /**
     * Creates a new {@link ActivityResultContract} used to request the settings Activity overlay
     * for this wallpaper.
     *
     * @param intent settings intent
     */
    public static ActivityResultContract<Void, Integer> getContract(Intent intent) {
        return new ActivityResultContract<Void, Integer>() {
            @NonNull
            @Override
            public Intent createIntent(@NonNull Context context, Void unused) {
                return intent;
            }

            @Override
            public Integer parseResult(int i, @Nullable Intent intent) {
                return i;
            }
        };
    }

    /**
     * Loads the current wallpaper's effects.
     *
     * @param context context of the current android component
     * @return an array list of WallpaperAction data objects
     * for the currently previewing wallpaper
     */
    @Nullable
    public ArrayList<WallpaperAction> getWallpaperEffects(Context context) {
        if (mEffectsUri == null) {
            return null;
        }
        mEffectsToggles.clear();
        // TODO (269350033): Move content provider query off the main thread.
        try (ContentProviderClient effectsClient =
                     context.getContentResolver().acquireContentProviderClient(
                             mEffectsUri.getAuthority())) {
            try (Cursor effectsCursor = effectsClient.query(mEffectsUri, /* projection= */ null,
                    /* selection= */ null, /* selectionArgs= */ null, /* sortOrder= */ null)) {
                if (effectsCursor == null) {
                    return null;
                }
                while (effectsCursor.moveToNext()) {
                    Uri effectsToggleUri = Uri.parse(
                            effectsCursor.getString(effectsCursor.getColumnIndex(
                                    WallpaperInfoContract.WALLPAPER_EFFECTS_TOGGLE_URI)));
                    String effectsButtonLabel = effectsCursor.getString(
                            effectsCursor.getColumnIndex(
                                    WallpaperInfoContract.WALLPAPER_EFFECTS_BUTTON_LABEL));
                    String effectsId = effectsCursor.getString(
                            effectsCursor.getColumnIndex(
                                    WallpaperInfoContract.WALLPAPER_EFFECTS_TOGGLE_ID));
                    mEffectsToggles.add(new WallpaperAction(effectsButtonLabel,
                            effectsToggleUri, effectsId, /* toggled= */ false));
                }
                return mEffectsToggles;
            }
        } catch (Exception e) {
            Log.e(TAG, "Read wallpaper effects with exception.", e);
        }
        return null;
    }

    @Override
    public String getTitle(Context context) {
        if (mVisibleTitle) {
            return mTitle;
        }
        return null;
    }

    @Override
    public List<String> getAttributions(Context context) {
        PackageManager packageManager = context.getPackageManager();
        CharSequence labelCharSeq = mInfo.loadLabel(packageManager);
        List<String> attributions = new ArrayList<>();
        attributions.add(labelCharSeq == null ? null : labelCharSeq.toString());
        attributions.add(mAuthor == null ? "" : mAuthor);
        attributions.add(mDescription == null ? "" : mDescription);

        return attributions;
    }

    @Override
    public String getContentDescription(Context context) {
        return mContentDescription;
    }

    @Override
    public Asset getThumbAsset(Context context) {
        if (mThumbAsset == null) {
            mThumbAsset = new CreativeWallpaperThumbAsset(context, mInfo, mThumbnailUri);
        }
        return mThumbAsset;
    }

    @Override
    public String getGroupName(Context context) {
        return mGroupName;
    }

    /**
     * Calls the config URI to initialize the preview for this wallpaper.
     */
    public void initializeWallpaperPreview(Context context) {
        if (mConfigPreviewUri != null) {
            context.getContentResolver().update(mConfigPreviewUri, new ContentValues(), null);
        }
    }

    /**
     * Calls the clean URI to de-initialize the preview for this wallpaper.
     */
    public void cleanUpWallpaperPreview(Context context) {
        if (mCleanPreviewUri != null) {
            context.getContentResolver().update(mCleanPreviewUri, new ContentValues(), null);
        }
    }

    /**
     * Returns true if this wallpaper can be deleted.
     */
    public boolean canBeDeleted() {
        return mDeleteUri != null && !TextUtils.isEmpty(mDeleteUri.toString());
    }

    @Override
    public boolean isApplied(@Nullable WallpaperInfo currentHomeWallpaper,
            @Nullable WallpaperInfo currentLockWallpaper) {
        return super.isApplied(currentHomeWallpaper, currentLockWallpaper) && mIsCurrent;
    }

    /**
     * Requests the content provider to delete this wallpaper.
     */
    public void requestDelete(Context context) {
        context.getContentResolver().delete(mDeleteUri, null, null);
    }

    public void setEffectsToggles(ArrayList<WallpaperAction> effectsToggles) {
        mEffectsToggles = effectsToggles;
    }

    public ArrayList<WallpaperAction> getEffectsToggles() {
        return mEffectsToggles;
    }

    public String getEffectsBottomSheetTitle() {
        return mEffectsBottomSheetTitle;
    }

    public void setEffectsBottomSheetTitle(String effectsBottomSheetTitle) {
        mEffectsBottomSheetTitle = effectsBottomSheetTitle;
    }

    /**
     * Returns the URI that can be used to save a creative category wallpaper.
     * @return the save wallpaper URI
     */
    public Uri getSaveWallpaperUriForCreativeWallpaper() {
        Bundle metaData = this.getWallpaperComponent().getServiceInfo().metaData;
        if (metaData == null || !metaData.containsKey(
                KEY_WALLPAPER_SAVE_CREATIVE_CATEGORY_WALLPAPER)) {
            return null;
        }
        String keyForCreativeCategoryWallpaper = (String) metaData.get(
                KEY_WALLPAPER_SAVE_CREATIVE_CATEGORY_WALLPAPER);
        return Uri.parse(keyForCreativeCategoryWallpaper);
    }

    public String getEffectsBottomSheetSubtitle() {
        return mEffectsBottomSheetSubtitle;
    }

    public void setEffectsBottomSheetSubtitle(String effectsBottomSheetSubtitle) {
        mEffectsBottomSheetSubtitle = effectsBottomSheetSubtitle;
    }

    public Uri getClearActionsUri() {
        return mClearActionsUri;
    }

    public void setClearActionsUri(Uri clearActionsUri) {
        mClearActionsUri = clearActionsUri;
    }

    public Uri getEffectsUri() {
        return mEffectsUri;
    }

    public void setEffectsUri(Uri effectsUri) {
        mEffectsUri = effectsUri;
    }

    public String getCurrentlyAppliedEffectId() {
        return mCurrentlyAppliedEffectId;
    }

    public void setCurrentlyAppliedEffectId(String currentlyAppliedEffectId) {
        mCurrentlyAppliedEffectId = currentlyAppliedEffectId;
    }
    /**
     * Returns true if this wallpaper can be shared.
     */
    public boolean canBeShared() {
        return mShareUri != null && !TextUtils.isEmpty(mShareUri.toString());
    }

    /**
     * Gets the share wallpaper image intent.
     */
    public Intent getShareIntent() {
        Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.putExtra(Intent.EXTRA_STREAM, mShareUri);
        shareIntent.setType("image/*");
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        shareIntent.setClipData(ClipData.newRawUri(null, mShareUri));
        return Intent.createChooser(shareIntent, null);
    }

    /**
     * This method returns whether wallpaper effects are supported for this wallpaper.
     * @return boolean
     */
    public boolean doesSupportWallpaperEffects() {
        return (mClearActionsUri != null && !TextUtils.isEmpty(mEffectsBottomSheetTitle));
    }

    /**
     * Triggers the content provider call to clear all effects upon the current.
     * wallpaper.
     */
    public void clearEffects(Context context) {
        if (doesSupportWallpaperEffects() && mClearActionsUri != null) {
            context.getContentResolver().update(mClearActionsUri, new ContentValues(), null);
        }
    }

    /**
     * Triggers the content provider call to apply the selected effect upon the current
     * wallpaper.
     */
    public void applyEffect(Context context, Uri applyEffectUri) {
        if (doesSupportWallpaperEffects()) {
            context.getContentResolver().update(applyEffectUri, new ContentValues(), null);
        }
    }

    /**
     * Creates an object of CreativeWallpaperInfo from the given cursor object.
     *
     * @param wallpaperInfo contains relevant metadata information about creative-category wallpaper
     * @param cursor contains relevant info to create an object of CreativeWallpaperInfo
     * @return an object of type CreativeWallpaperInfo
     */
    @NonNull
    public static CreativeWallpaperInfo buildFromCursor(WallpaperInfo wallpaperInfo,
            Cursor cursor) {
        String wallpaperTitle = cursor.getString(
                cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_TITLE));
        String wallpaperAuthor = null;
        if (cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_AUTHOR) >= 0) {
            wallpaperAuthor = cursor.getString(
                    cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_AUTHOR));
        }
        String wallpaperDescription = null;
        if (cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_DESCRIPTION) >= 0) {
            wallpaperDescription = cursor.getString(
                    cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_DESCRIPTION));
        }
        String wallpaperContentDescription = null;
        int wallpaperContentDescriptionIndex = cursor.getColumnIndex(
                WallpaperInfoContract.WALLPAPER_CONTENT_DESCRIPTION);
        if (wallpaperContentDescriptionIndex >= 0) {
            wallpaperContentDescription = cursor.getString(
                    wallpaperContentDescriptionIndex);
        }
        Uri thumbnailUri = Uri.parse(cursor.getString(cursor.getColumnIndex(
                WallpaperInfoContract.WALLPAPER_THUMBNAIL)));
        Uri configPreviewUri = Uri.parse(cursor.getString(cursor.getColumnIndex(
                WallpaperInfoContract.WALLPAPER_CONFIG_PREVIEW_URI)));
        Uri cleanPreviewUri = Uri.parse(cursor.getString(cursor.getColumnIndex(
                WallpaperInfoContract.WALLPAPER_CLEAN_PREVIEW_URI)));
        Uri deleteUri = Uri.parse(cursor.getString(
                cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_DELETE_URI)));
        Uri shareUri = Uri.parse(cursor.getString(cursor.getColumnIndex(
                WallpaperInfoContract.WALLPAPER_SHARE_URI)));
        String groupName = cursor.getString(
                cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_GROUP_NAME));
        int isCurrentApplied = cursor.getInt(
                cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_IS_APPLIED));

        return new CreativeWallpaperInfo(wallpaperInfo, wallpaperTitle, wallpaperAuthor,
                wallpaperDescription, wallpaperContentDescription, configPreviewUri,
                cleanPreviewUri, deleteUri, thumbnailUri, shareUri, groupName, /* isCurrent= */
                (isCurrentApplied == 1));
    }

    /**
     * Saves a wallpaper of type of CreativeWallpaperInfo for a particular destination.
     * @param context context of the calling activity
     * @param destination depicts the destination of the wallpaper being saved
     * @return CreativeWallpaperInfo object that has been saved
     */
    @Override
    public CreativeWallpaperInfo saveWallpaper(Context context, int destination) {
        if (context == null) {
            Log.w(TAG, "Context is null!!");
            return null;
        }

        Uri saveWallpaperUri = getSaveWallpaperUriForCreativeWallpaper();
        if (saveWallpaperUri == null) {
            Log.w(TAG, "Missing save wallpaper uri in  " + this.getWallpaperComponent()
                    .getServiceName());
            return null;
        }
        return CreativeCategory.saveCreativeCategoryWallpaper(
                context, this, saveWallpaperUri, destination);
    }

    public void setConfigPreviewUri(Uri configPreviewUri) {
        mConfigPreviewUri = configPreviewUri;
    }

    public Uri getConfigPreviewUri() {
        return mConfigPreviewUri;
    }

    public Uri getCleanPreviewUri() {
        return mCleanPreviewUri;
    }

    public Uri getDeleteUri() {
        return mDeleteUri;
    }

    public Uri getThumbnailUri() {
        return mThumbnailUri;
    }

    public Uri getShareUri() {
        return mShareUri;
    }

    public String getTitle() {
        return mTitle;
    }

    public String getAuthor() {
        return mAuthor;
    }

    public String getDescription() {
        return mDescription;
    }

    public String getContentDescription() {
        return mContentDescription;
    }

    public boolean isCurrent() {
        return mIsCurrent;
    }

    public String getGroupName() {
        return mGroupName;
    }
}
