/*
 * Copyright (C) 2015 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.tv.menu;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.palette.graphics.Palette;
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.data.api.Channel;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.images.BitmapUtils;
import com.android.tv.util.images.ImageLoader;
import java.util.Objects;

/** A view to render an app link card. */
public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
    private static final String TAG = MenuView.TAG;
    private static final boolean DEBUG = MenuView.DEBUG;

    private final int mCardImageWidth;
    private final int mCardImageHeight;
    private final int mIconWidth;
    private final int mIconHeight;
    private final int mIconPadding;
    private final int mIconColorFilter;
    private final Drawable mDefaultDrawable;

    private ImageView mImageView;
    private TextView mAppInfoView;
    private View mMetaViewHolder;
    private Channel mChannel;
    private Intent mIntent;
    private final PackageManager mPackageManager;
    private final TvInputManagerHelper mTvInputManagerHelper;

    public AppLinkCardView(Context context) {
        this(context, null);
    }

    public AppLinkCardView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AppLinkCardView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        mCardImageWidth = getResources().getDimensionPixelSize(R.dimen.card_image_layout_width);
        mCardImageHeight = getResources().getDimensionPixelSize(R.dimen.card_image_layout_height);
        mIconWidth = getResources().getDimensionPixelSize(R.dimen.app_link_card_icon_width);
        mIconHeight = getResources().getDimensionPixelSize(R.dimen.app_link_card_icon_height);
        mIconPadding = getResources().getDimensionPixelOffset(R.dimen.app_link_card_icon_padding);
        mPackageManager = context.getPackageManager();
        mTvInputManagerHelper = ((MainActivity) context).getTvInputManagerHelper();
        mIconColorFilter = getResources().getColor(R.color.app_link_card_icon_color_filter, null);
        mDefaultDrawable = getResources().getDrawable(R.drawable.ic_recent_thumbnail_default, null);
    }

    /** Returns the intent which will be started once this card is clicked. */
    public Intent getIntent() {
        return mIntent;
    }

    @Override
    public void onBind(ChannelsRowItem item, boolean selected) {
        Channel newChannel = item.getChannel();
        boolean channelChanged = !Objects.equals(mChannel, newChannel);
        String previousPosterArtUri = mChannel == null ? null : mChannel.getAppLinkPosterArtUri();
        boolean posterArtChanged =
                previousPosterArtUri == null
                        || newChannel.getAppLinkPosterArtUri() == null
                        || !TextUtils.equals(
                                previousPosterArtUri, newChannel.getAppLinkPosterArtUri());
        mChannel = newChannel;
        if (DEBUG) {
            Log.d(
                    TAG,
                    "onBind(channelName="
                            + mChannel.getDisplayName()
                            + ", selected="
                            + selected
                            + ")");
        }
        ApplicationInfo appInfo = mTvInputManagerHelper.getTvInputAppInfo(mChannel.getInputId());
        if (channelChanged) {
            int linkType = mChannel.getAppLinkType(getContext());
            mIntent = mChannel.getAppLinkIntent(getContext());

            CharSequence appLabel;
            switch (linkType) {
                case Channel.APP_LINK_TYPE_CHANNEL:
                    setText(mChannel.getAppLinkText());
                    mAppInfoView.setVisibility(VISIBLE);
                    mAppInfoView.setCompoundDrawablePadding(mIconPadding);
                    mAppInfoView.setCompoundDrawablesRelative(null, null, null, null);
                    appLabel =
                            mTvInputManagerHelper.getTvInputApplicationLabel(mChannel.getInputId());
                    if (appLabel != null) {
                        mAppInfoView.setText(appLabel);
                    } else {
                        new AsyncTask<Void, Void, CharSequence>() {
                            private final String mLoadTvInputId = mChannel.getInputId();

                            @Override
                            protected CharSequence doInBackground(Void... params) {
                                if (appInfo != null) {
                                    return mPackageManager.getApplicationLabel(appInfo);
                                }
                                return null;
                            }

                            @Override
                            protected void onPostExecute(CharSequence appLabel) {
                                mTvInputManagerHelper.setTvInputApplicationLabel(
                                        mLoadTvInputId, appLabel);
                                if (mLoadTvInputId.equals(mChannel.getInputId())
                                        || !isAttachedToWindow()) {
                                    return;
                                }
                                mAppInfoView.setText(appLabel);
                            }
                        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                    }
                    if (!TextUtils.isEmpty(mChannel.getAppLinkIconUri())) {
                        mChannel.loadBitmap(
                                getContext(),
                                Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON,
                                mIconWidth,
                                mIconHeight,
                                createChannelLogoCallback(
                                        this, mChannel, Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON));
                    } else if (appInfo.icon != 0) {
                        Drawable appIcon =
                                mTvInputManagerHelper.getTvInputApplicationIcon(
                                        mChannel.getInputId());
                        if (appIcon != null) {
                            BitmapUtils.setColorFilterToDrawable(mIconColorFilter, appIcon);
                            appIcon.setBounds(0, 0, mIconWidth, mIconHeight);
                            mAppInfoView.setCompoundDrawablesRelative(appIcon, null, null, null);
                        } else {
                            new AsyncTask<Void, Void, Drawable>() {
                                private final String mLoadTvInputId = mChannel.getInputId();

                                @Override
                                protected Drawable doInBackground(Void... params) {
                                    return mPackageManager.getApplicationIcon(appInfo);
                                }

                                @Override
                                protected void onPostExecute(Drawable appIcon) {
                                    mTvInputManagerHelper.setTvInputApplicationIcon(
                                            mLoadTvInputId, appIcon);
                                    if (!mLoadTvInputId.equals(mChannel.getInputId())
                                            || !isAttachedToWindow()) {
                                        return;
                                    }
                                    BitmapUtils.setColorFilterToDrawable(mIconColorFilter, appIcon);
                                    appIcon.setBounds(0, 0, mIconWidth, mIconHeight);
                                    mAppInfoView.setCompoundDrawablesRelative(
                                            appIcon, null, null, null);
                                }
                            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                        }
                    }
                    break;
                case Channel.APP_LINK_TYPE_APP:
                    appLabel =
                            mTvInputManagerHelper.getTvInputApplicationLabel(mChannel.getInputId());
                    if (appLabel != null) {
                        setText(
                                getContext()
                                        .getString(
                                                R.string.channels_item_app_link_app_launcher,
                                                appLabel));
                    } else {
                        new AsyncTask<Void, Void, CharSequence>() {
                            private final String mLoadTvInputId = mChannel.getInputId();

                            @Override
                            protected CharSequence doInBackground(Void... params) {
                                if (appInfo != null) {
                                    return mPackageManager.getApplicationLabel(appInfo);
                                }
                                return null;
                            }

                            @Override
                            protected void onPostExecute(CharSequence appLabel) {
                                mTvInputManagerHelper.setTvInputApplicationLabel(
                                        mLoadTvInputId, appLabel);
                                if (!mLoadTvInputId.equals(mChannel.getInputId())
                                        || !isAttachedToWindow()) {
                                    return;
                                }
                                setText(
                                        getContext()
                                                .getString(
                                                        R.string
                                                                .channels_item_app_link_app_launcher,
                                                        appLabel));
                            }
                        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                    }
                    mAppInfoView.setVisibility(GONE);
                    break;
                default:
                    mAppInfoView.setVisibility(GONE);
                    Log.d(TAG, "Should not be here.");
            }

            if (mChannel.getAppLinkColor() == 0) {
                mMetaViewHolder.setBackgroundResource(R.color.channel_card_meta_background);
            } else {
                mMetaViewHolder.setBackgroundColor(mChannel.getAppLinkColor());
            }
        }
        if (posterArtChanged) {
            mImageView.setImageDrawable(mDefaultDrawable);
            mImageView.setForeground(null);
            if (!TextUtils.isEmpty(mChannel.getAppLinkPosterArtUri())) {
                mChannel.loadBitmap(
                        getContext(),
                        Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART,
                        mCardImageWidth,
                        mCardImageHeight,
                        createChannelLogoCallback(
                                this, mChannel, Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART));
            } else {
                setCardImageWithBanner(appInfo);
            }
        }
        super.onBind(item, selected);
    }

    private static ImageLoader.ImageLoaderCallback<AppLinkCardView> createChannelLogoCallback(
            AppLinkCardView cardView, final Channel channel, final int type) {
        return new ImageLoader.ImageLoaderCallback<AppLinkCardView>(cardView) {
            @Override
            public void onBitmapLoaded(AppLinkCardView cardView, @Nullable Bitmap bitmap) {
                // mChannel can be changed before the image load finished.
                if (!cardView.mChannel.hasSameReadOnlyInfo(channel)) {
                    return;
                }
                cardView.updateChannelLogo(bitmap, type);
            }
        };
    }

    private void updateChannelLogo(@Nullable Bitmap bitmap, int type) {
        if (type == Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON) {
            BitmapDrawable drawable = null;
            if (bitmap != null) {
                drawable = new BitmapDrawable(getResources(), bitmap);
                if (bitmap.getWidth() > bitmap.getHeight()) {
                    drawable.setBounds(
                            0, 0, mIconWidth, mIconWidth * bitmap.getHeight() / bitmap.getWidth());
                } else {
                    drawable.setBounds(
                            0,
                            0,
                            mIconHeight * bitmap.getWidth() / bitmap.getHeight(),
                            mIconHeight);
                }
            }
            BitmapUtils.setColorFilterToDrawable(mIconColorFilter, drawable);
            mAppInfoView.setCompoundDrawablesRelative(drawable, null, null, null);
        } else if (type == Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART) {
            if (bitmap == null) {
                setCardImageWithBanner(
                        mTvInputManagerHelper.getTvInputAppInfo(mChannel.getInputId()));
            } else {
                mImageView.setImageBitmap(bitmap);
                mImageView.setForeground(getContext().getDrawable(R.drawable.card_image_gradient));
                if (mChannel.getAppLinkColor() == 0) {
                    extractAndSetMetaViewBackgroundColor(bitmap);
                }
            }
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mImageView = (ImageView) findViewById(R.id.image);
        mAppInfoView = (TextView) findViewById(R.id.app_info);
        mMetaViewHolder = findViewById(R.id.app_link_text_holder);
    }

    // Try to set the card image with following order:
    // 1) Provided poster art image, 2) Activity banner, 3) Activity icon, 4) Application banner,
    // 5) Application icon, and 6) default image.
    private void setCardImageWithBanner(ApplicationInfo appInfo) {
        new AsyncTask<Void, Void, Drawable>() {
            private String mLoadTvInputId = mChannel.getInputId();

            @Override
            protected Drawable doInBackground(Void... params) {
                Drawable banner = null;
                if (mIntent != null) {
                    try {
                        banner = mPackageManager.getActivityBanner(mIntent);
                        if (banner == null) {
                            banner = mPackageManager.getActivityIcon(mIntent);
                        }
                    } catch (PackageManager.NameNotFoundException e) {
                        // do nothing.
                    }
                }
                return banner;
            }

            @Override
            protected void onPostExecute(Drawable banner) {
                if (mLoadTvInputId.equals(mChannel.getInputId()) || !isAttachedToWindow()) {
                    return;
                }
                if (banner != null) {
                    setCardImageWithBannerInternal(banner);
                } else {
                    setCardImageWithApplicationInfoBanner(appInfo);
                }
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    private void setCardImageWithApplicationInfoBanner(ApplicationInfo appInfo) {
        Drawable appBanner =
                mTvInputManagerHelper.getTvInputApplicationBanner(mChannel.getInputId());
        if (appBanner != null) {
            setCardImageWithBannerInternal(appBanner);
        } else {
            new AsyncTask<Void, Void, Drawable>() {
                private final String mLoadTvInputId = mChannel.getInputId();

                @Override
                protected Drawable doInBackground(Void... params) {
                    Drawable banner = null;
                    if (appInfo != null) {
                        if (appInfo.banner != 0) {
                            banner = mPackageManager.getApplicationBanner(appInfo);
                        }
                        if (banner == null && appInfo.icon != 0) {
                            banner = mPackageManager.getApplicationIcon(appInfo);
                        }
                    }
                    return banner;
                }

                @Override
                protected void onPostExecute(Drawable banner) {
                    mTvInputManagerHelper.setTvInputApplicationBanner(mLoadTvInputId, banner);
                    if (!TextUtils.equals(mLoadTvInputId, mChannel.getInputId())
                            || !isAttachedToWindow()) {
                        return;
                    }
                    setCardImageWithBannerInternal(banner);
                }
            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }
    }

    private void setCardImageWithBannerInternal(Drawable banner) {
        if (banner == null) {
            mImageView.setImageDrawable(mDefaultDrawable);
            mImageView.setBackgroundResource(R.color.channel_card);
        } else {
            Bitmap bitmap =
                    Bitmap.createBitmap(mCardImageWidth, mCardImageHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            banner.setBounds(0, 0, mCardImageWidth, mCardImageHeight);
            banner.draw(canvas);
            mImageView.setImageDrawable(banner);
            mImageView.setForeground(getContext().getDrawable(R.drawable.card_image_gradient));
            if (mChannel.getAppLinkColor() == 0) {
                extractAndSetMetaViewBackgroundColor(bitmap);
            }
        }
    }

    private void extractAndSetMetaViewBackgroundColor(Bitmap bitmap) {
        new Palette.Builder(bitmap)
                .generate(
                        new Palette.PaletteAsyncListener() {
                            @Override
                            public void onGenerated(Palette palette) {
                                mMetaViewHolder.setBackgroundColor(
                                        palette.getDarkVibrantColor(
                                                getResources()
                                                        .getColor(
                                                                R.color
                                                                        .channel_card_meta_background,
                                                                null)));
                            }
                        });
    }
}
