/*
 * Copyright (C) 2020 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.customization.picker;

import android.app.Activity;
import android.app.WallpaperColors;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.RenderEffect;
import android.graphics.Shader.TileMode;
import android.service.wallpaper.WallpaperService;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;

import com.android.wallpaper.model.LiveWallpaperInfo;
import com.android.wallpaper.model.WallpaperInfo;
import com.android.wallpaper.util.ResourceUtils;
import com.android.wallpaper.util.ScreenSizeCalculator;
import com.android.wallpaper.util.SizeCalculator;
import com.android.wallpaper.util.VideoWallpaperUtils;
import com.android.wallpaper.util.WallpaperConnection;
import com.android.wallpaper.util.WallpaperConnection.WallpaperConnectionListener;
import com.android.wallpaper.util.WallpaperSurfaceCallback;
import com.android.wallpaper.widget.WallpaperColorsLoader;

/** A class to load the wallpaper to the view. */
public class WallpaperPreviewer implements LifecycleObserver {

    private final Rect mPreviewLocalRect = new Rect();
    private final Rect mPreviewGlobalRect = new Rect();
    private final int[] mLivePreviewLocation = new int[2];

    private final Activity mActivity;
    private final ImageView mHomePreview;
    private final SurfaceView mWallpaperSurface;
    @Nullable private final ImageView mFadeInScrim;

    private WallpaperSurfaceCallback mWallpaperSurfaceCallback;
    private WallpaperInfo mWallpaper;
    private WallpaperConnection mWallpaperConnection;
    @Nullable private WallpaperColorsListener mWallpaperColorsListener;

    /** Interface for getting {@link WallpaperColors} from wallpaper. */
    public interface WallpaperColorsListener {
        /** Gets called when wallpaper color is available or updated. */
        void onWallpaperColorsChanged(WallpaperColors colors);
    }

    public WallpaperPreviewer(Lifecycle lifecycle, Activity activity, ImageView homePreview,
                              SurfaceView wallpaperSurface) {
        this(lifecycle, activity, homePreview, wallpaperSurface, null);
    }

    public WallpaperPreviewer(Lifecycle lifecycle, Activity activity, ImageView homePreview,
                              SurfaceView wallpaperSurface, @Nullable ImageView fadeInScrim) {
        lifecycle.addObserver(this);

        mActivity = activity;
        mHomePreview = homePreview;
        mWallpaperSurface = wallpaperSurface;
        mFadeInScrim = fadeInScrim;
        mWallpaperSurfaceCallback = new WallpaperSurfaceCallback(activity, mHomePreview,
                mWallpaperSurface, this::setUpWallpaperPreview);
        mWallpaperSurface.setZOrderMediaOverlay(true);
        mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);

        View rootView = homePreview.getRootView();
        rootView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom,
                                       int oldLeft, int oldTop, int oldRight, int oldBottom) {
                updatePreviewCardRadius();
                rootView.removeOnLayoutChangeListener(this);
            }
        });
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    @MainThread
    public void onResume() {
        if (mWallpaperConnection != null) {
            mWallpaperConnection.setVisibility(true);
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    @MainThread
    public void onPause() {
        if (mWallpaperConnection != null) {
            mWallpaperConnection.setVisibility(false);
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    @MainThread
    public void onStop() {
        if (mWallpaperConnection != null) {
            mWallpaperConnection.disconnect();
            mWallpaperConnection = null;
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    @MainThread
    public void onDestroy() {
        if (mWallpaperConnection != null) {
            mWallpaperConnection.disconnect();
            mWallpaperConnection = null;
        }

        mWallpaperSurfaceCallback.cleanUp();
        mWallpaperSurface.getHolder().removeCallback(mWallpaperSurfaceCallback);
        Surface surface = mWallpaperSurface.getHolder().getSurface();
        if (surface != null) {
            surface.release();
        }
    }

    /**
     * Sets a wallpaper to be shown on preview screen.
     *
     * @param wallpaperInfo the wallpaper to preview
     * @param listener the listener for getting the wallpaper color of {@param wallpaperInfo}
     */
    public void setWallpaper(WallpaperInfo wallpaperInfo,
                             @Nullable WallpaperColorsListener listener) {
        mWallpaper = wallpaperInfo;
        mWallpaperColorsListener = listener;
        if (mFadeInScrim != null && VideoWallpaperUtils.needsFadeIn(wallpaperInfo)) {
            mFadeInScrim.animate().cancel();
            mFadeInScrim.setAlpha(1f);
            mFadeInScrim.setVisibility(View.VISIBLE);
        }
        setUpWallpaperPreview();
    }

    private void setUpWallpaperPreview() {
        ImageView homeImageWallpaper = mWallpaperSurfaceCallback.getHomeImageWallpaper();
        if (mWallpaper != null && homeImageWallpaper != null) {
            homeImageWallpaper.post(() -> {
                if (mActivity == null || mActivity.isDestroyed()) {
                    return;
                }
                boolean renderInImageWallpaperSurface = !(mWallpaper instanceof LiveWallpaperInfo);
                mWallpaper.getThumbAsset(mActivity.getApplicationContext())
                        .loadPreviewImage(mActivity,
                                renderInImageWallpaperSurface ? homeImageWallpaper : mHomePreview,
                                ResourceUtils.getColorAttr(
                                        mActivity, android.R.attr.colorSecondary),
                                /* offsetToStart= */ true);
                if (mWallpaper instanceof LiveWallpaperInfo) {
                    ImageView preview = homeImageWallpaper;
                    if (VideoWallpaperUtils.needsFadeIn(mWallpaper) && mFadeInScrim != null) {
                        preview = mFadeInScrim;
                        preview.setRenderEffect(
                                RenderEffect.createBlurEffect(150f, 150f, TileMode.CLAMP));
                    }
                    mWallpaper.getThumbAsset(mActivity.getApplicationContext())
                            .loadPreviewImage(
                                    mActivity,
                                    preview,
                                    ResourceUtils.getColorAttr(
                                            mActivity, android.R.attr.colorSecondary),
                                    /* offsetToStart= */ true);
                    setUpLiveWallpaperPreview(mWallpaper);
                } else {
                    // Ensure live wallpaper connection is disconnected.
                    if (mWallpaperConnection != null) {
                        mWallpaperConnection.disconnect();
                        mWallpaperConnection = null;
                    }

                    // Load wallpaper color for static wallpaper.
                    if (mWallpaperColorsListener != null) {
                        WallpaperColorsLoader.getWallpaperColors(
                                mActivity,
                                mWallpaper.getThumbAsset(mActivity),
                                mWallpaperColorsListener::onWallpaperColorsChanged);
                    }
                }
            });
        }
    }

    private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper) {
        if (mActivity == null || mActivity.isFinishing()) {
            return;
        }

        if (mWallpaperConnection != null) {
            mWallpaperConnection.disconnect();
        }
        if (WallpaperConnection.isPreviewAvailable()) {
            mHomePreview.getLocationOnScreen(mLivePreviewLocation);
            mPreviewGlobalRect.set(0, 0, mHomePreview.getMeasuredWidth(),
                    mHomePreview.getMeasuredHeight());
            mPreviewLocalRect.set(mPreviewGlobalRect);
            mPreviewGlobalRect.offset(mLivePreviewLocation[0], mLivePreviewLocation[1]);

            mWallpaperConnection = new WallpaperConnection(
                    getWallpaperIntent(homeWallpaper.getWallpaperComponent()), mActivity,
                    new WallpaperConnectionListener() {
                        @Override
                        public void onWallpaperColorsChanged(WallpaperColors colors,
                                int displayId) {
                            if (mWallpaperColorsListener != null) {
                                mWallpaperColorsListener.onWallpaperColorsChanged(colors);
                            }
                        }

                        @Override
                        public void onEngineShown() {
                            if (mFadeInScrim != null && VideoWallpaperUtils.needsFadeIn(
                                    homeWallpaper)) {
                                mFadeInScrim.animate().alpha(0.0f)
                                        .setDuration(VideoWallpaperUtils.TRANSITION_MILLIS)
                                        .withEndAction(
                                                () -> mFadeInScrim.setVisibility(View.INVISIBLE));
                            }
                        }
                    }, mWallpaperSurface);

            mWallpaperConnection.setVisibility(true);
            mHomePreview.post(() -> {
                if (mWallpaperConnection != null && !mWallpaperConnection.connect()) {
                    mWallpaperConnection = null;
                }
            });
        } else {
            // Load wallpaper color from the thumbnail.
            if (mWallpaperColorsListener != null) {
                WallpaperColorsLoader.getWallpaperColors(
                        mActivity,
                        mWallpaper.getThumbAsset(mActivity),
                        mWallpaperColorsListener::onWallpaperColorsChanged);
            }
        }
    }

    /** Updates the preview card view corner radius to match the device corner radius. */
    private void updatePreviewCardRadius() {
        final float screenAspectRatio =
                ScreenSizeCalculator.getInstance().getScreenAspectRatio(mActivity);
        CardView cardView = (CardView) mHomePreview.getParent();
        final int cardWidth = (int) (cardView.getMeasuredHeight() / screenAspectRatio);
        ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
        layoutParams.width = cardWidth;
        cardView.setLayoutParams(layoutParams);
        cardView.setRadius(SizeCalculator.getPreviewCornerRadius(mActivity, cardWidth));
    }

    private static Intent getWallpaperIntent(android.app.WallpaperInfo info) {
        return new Intent(WallpaperService.SERVICE_INTERFACE)
                .setClassName(info.getPackageName(), info.getServiceName());
    }
}
