/*
 * Copyright (C) 2016 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.dvr.ui.browse;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.tv.TvContentRating;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import androidx.leanback.app.DetailsFragment;
import androidx.leanback.widget.ArrayObjectAdapter;
import androidx.leanback.widget.ClassPresenterSelector;
import androidx.leanback.widget.DetailsOverviewRow;
import androidx.leanback.widget.DetailsOverviewRowPresenter;
import androidx.leanback.widget.OnActionClickedListener;
import androidx.leanback.widget.PresenterSelector;
import androidx.leanback.widget.SparseArrayObjectAdapter;
import androidx.leanback.widget.VerticalGridView;
import android.text.TextUtils;
import android.widget.Toast;
import com.android.tv.R;
import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.api.Channel;
import com.android.tv.dialog.PinDialogFragment;
import com.android.tv.dialog.PinDialogFragment.OnPinCheckedListener;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.parental.ParentalControlSettings;
import com.android.tv.ui.DetailsActivity;
import com.android.tv.util.ToastUtils;
import com.android.tv.util.images.ImageLoader;
import com.google.common.collect.ImmutableList;
import java.io.File;

abstract class DvrDetailsFragment extends DetailsFragment {
    private static final int LOAD_LOGO_IMAGE = 1;
    private static final int LOAD_BACKGROUND_IMAGE = 2;

    protected DetailsViewBackgroundHelper mBackgroundHelper;
    private ArrayObjectAdapter mRowsAdapter;
    private DetailsOverviewRow mDetailsOverview;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!onLoadRecordingDetails(getArguments())) {
            getActivity().finish();
            return;
        }
        mBackgroundHelper = new DetailsViewBackgroundHelper(getActivity());
        setupAdapter();
        onCreateInternal();
    }

    @Override
    public void onStart() {
        super.onStart();
        // TODO: remove the workaround of b/30401180.
        VerticalGridView container =
                (VerticalGridView) getActivity().findViewById(R.id.container_list);
        // Need to manually modify offset. Please refer DetailsFragment.setVerticalGridViewLayout.
        container.setItemAlignmentOffset(0);
        container.setWindowAlignmentOffset(
                getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top));
    }

    private void setupAdapter() {
        DetailsOverviewRowPresenter rowPresenter =
                new DetailsOverviewRowPresenter(new DetailsContentPresenter(getActivity()));
        rowPresenter.setBackgroundColor(
                getResources().getColor(R.color.common_tv_background, null));
        rowPresenter.setSharedElementEnterTransition(
                getActivity(), DetailsActivity.SHARED_ELEMENT_NAME);
        rowPresenter.setOnActionClickedListener(onCreateOnActionClickedListener());
        mRowsAdapter = new ArrayObjectAdapter(onCreatePresenterSelector(rowPresenter));
        setAdapter(mRowsAdapter);
    }

    /** Returns details views' rows adapter. */
    protected ArrayObjectAdapter getRowsAdapter() {
        return mRowsAdapter;
    }

    /** Sets details overview. */
    protected void setDetailsOverviewRow(DetailsContent detailsContent) {
        mDetailsOverview = new DetailsOverviewRow(detailsContent);
        mDetailsOverview.setActionsAdapter(onCreateActionsAdapter());
        mRowsAdapter.add(mDetailsOverview);
        onLoadLogoAndBackgroundImages(detailsContent);
    }

    /** Creates and returns presenter selector will be used by rows adaptor. */
    protected PresenterSelector onCreatePresenterSelector(
            DetailsOverviewRowPresenter rowPresenter) {
        ClassPresenterSelector presenterSelector = new ClassPresenterSelector();
        presenterSelector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
        return presenterSelector;
    }

    /**
     * Does customized initialization of subclasses. Since {@link #onCreate(Bundle)} might finish
     * activity early when it cannot fetch valid recordings, subclasses' onCreate method should not
     * do anything after calling {@link #onCreate(Bundle)}. If there's something subclasses have to
     * do after the super class did onCreate, it should override this method and put the codes here.
     */
    protected void onCreateInternal() {}

    /** Updates actions of details overview. */
    protected void updateActions() {
        mDetailsOverview.setActionsAdapter(onCreateActionsAdapter());
    }

    /**
     * Loads recording details according to the arguments the fragment got.
     *
     * @return false if cannot find valid recordings, else return true. If the return value is
     *     false, the detail activity and fragment will be ended.
     */
    abstract boolean onLoadRecordingDetails(Bundle args);

    /** Creates actions users can interact with and their adaptor for this fragment. */
    abstract SparseArrayObjectAdapter onCreateActionsAdapter();

    /**
     * Creates actions listeners to implement the behavior of the fragment after users click some
     * action buttons.
     */
    abstract OnActionClickedListener onCreateOnActionClickedListener();

    /** Loads logo and background images for detail fragments. */
    protected void onLoadLogoAndBackgroundImages(DetailsContent detailsContent) {
        Drawable logoDrawable = null;
        Drawable backgroundDrawable = null;
        if (TextUtils.isEmpty(detailsContent.getLogoImageUri())) {
            logoDrawable =
                    getContext().getResources().getDrawable(R.drawable.dvr_default_poster, null);
            mDetailsOverview.setImageDrawable(logoDrawable);
        }
        if (TextUtils.isEmpty(detailsContent.getBackgroundImageUri())) {
            backgroundDrawable =
                    getContext().getResources().getDrawable(R.drawable.dvr_default_poster, null);
            mBackgroundHelper.setBackground(backgroundDrawable);
        }
        if (logoDrawable != null && backgroundDrawable != null) {
            return;
        }
        if (logoDrawable == null
                && backgroundDrawable == null
                && detailsContent
                        .getLogoImageUri()
                        .equals(detailsContent.getBackgroundImageUri())) {
            ImageLoader.loadBitmap(
                    getContext(),
                    detailsContent.getLogoImageUri(),
                    new MyImageLoaderCallback(
                            this, LOAD_LOGO_IMAGE | LOAD_BACKGROUND_IMAGE, getContext()));
            return;
        }
        if (logoDrawable == null) {
            int imageWidth = getResources().getDimensionPixelSize(R.dimen.dvr_details_poster_width);
            int imageHeight =
                    getResources().getDimensionPixelSize(R.dimen.dvr_details_poster_height);
            ImageLoader.loadBitmap(
                    getContext(),
                    detailsContent.getLogoImageUri(),
                    imageWidth,
                    imageHeight,
                    new MyImageLoaderCallback(this, LOAD_LOGO_IMAGE, getContext()));
        }
        if (backgroundDrawable == null) {
            ImageLoader.loadBitmap(
                    getContext(),
                    detailsContent.getBackgroundImageUri(),
                    new MyImageLoaderCallback(this, LOAD_BACKGROUND_IMAGE, getContext()));
        }
    }

    protected void startPlayback(RecordedProgram recordedProgram, long seekTimeMs) {
        if (CommonUtils.isInBundledPackageSet(recordedProgram.getPackageName())
                && !isDataUriAccessible(recordedProgram.getDataUri())) {
            // Since cleaning RecordedProgram from forgotten storage will take some time,
            // ignore playback until cleaning is finished.
            ToastUtils.show(
                    getContext(),
                    getContext().getResources().getString(R.string.dvr_toast_recording_deleted),
                    Toast.LENGTH_SHORT);
            return;
        }
        long programId = recordedProgram.getId();
        ParentalControlSettings parental =
                TvSingletons.getSingletons(getActivity())
                        .getTvInputManagerHelper()
                        .getParentalControlSettings();
        if (!parental.isParentalControlsEnabled()) {
            DvrUiHelper.startPlaybackActivity(getContext(), programId, seekTimeMs, false);
            return;
        }
        ChannelDataManager channelDataManager =
                TvSingletons.getSingletons(getActivity()).getChannelDataManager();
        Channel channel = channelDataManager.getChannel(recordedProgram.getChannelId());
        if (channel != null && channel.isLocked()) {
            checkPinToPlay(recordedProgram, seekTimeMs);
            return;
        }
        ImmutableList<TvContentRating> ratings = recordedProgram.getContentRatings();
        TvContentRating blockRatings = parental.getBlockedRating(ratings);
        if (blockRatings != null) {
            checkPinToPlay(recordedProgram, seekTimeMs);
        } else {
            DvrUiHelper.startPlaybackActivity(getContext(), programId, seekTimeMs, false);
        }
    }

    private boolean isDataUriAccessible(Uri dataUri) {
        if (dataUri == null || dataUri.getPath() == null) {
            return false;
        }
        try {
            File recordedProgramPath = new File(dataUri.getPath());
            if (recordedProgramPath.exists()) {
                return true;
            }
        } catch (SecurityException e) {
        }
        return false;
    }

    private void checkPinToPlay(RecordedProgram recordedProgram, long seekTimeMs) {
        SoftPreconditions.checkState(getActivity() instanceof DetailsActivity);
        if (getActivity() instanceof DetailsActivity) {
            ((DetailsActivity) getActivity())
                    .setOnPinCheckListener(
                            new OnPinCheckedListener() {
                                @Override
                                public void onPinChecked(boolean checked, int type, String rating) {
                                    ((DetailsActivity) getActivity()).setOnPinCheckListener(null);
                                    if (checked
                                            && type
                                                    == PinDialogFragment
                                                            .PIN_DIALOG_TYPE_UNLOCK_PROGRAM) {
                                        DvrUiHelper.startPlaybackActivity(
                                                getContext(),
                                                recordedProgram.getId(),
                                                seekTimeMs,
                                                true);
                                    }
                                }
                            });
            PinDialogFragment.create(PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM)
                    .show(getActivity().getFragmentManager(), PinDialogFragment.DIALOG_TAG);
        }
    }

    private static class MyImageLoaderCallback
            extends ImageLoader.ImageLoaderCallback<DvrDetailsFragment> {
        private final Context mContext;
        private final int mLoadType;

        public MyImageLoaderCallback(DvrDetailsFragment fragment, int loadType, Context context) {
            super(fragment);
            mLoadType = loadType;
            mContext = context;
        }

        @Override
        public void onBitmapLoaded(DvrDetailsFragment fragment, @Nullable Bitmap bitmap) {
            Drawable drawable;
            int loadType = mLoadType;
            if (bitmap == null) {
                Resources res = mContext.getResources();
                drawable = res.getDrawable(R.drawable.dvr_default_poster, null);
                if ((loadType & LOAD_BACKGROUND_IMAGE) != 0 && !fragment.isDetached()) {
                    loadType &= ~LOAD_BACKGROUND_IMAGE;
                    fragment.mBackgroundHelper.setBackgroundColor(
                            res.getColor(R.color.dvr_detail_default_background));
                    fragment.mBackgroundHelper.setScrim(
                            res.getColor(R.color.dvr_detail_default_background_scrim));
                }
            } else {
                drawable = new BitmapDrawable(mContext.getResources(), bitmap);
            }
            if (!fragment.isDetached()) {
                if ((loadType & LOAD_LOGO_IMAGE) != 0) {
                    fragment.mDetailsOverview.setImageDrawable(drawable);
                }
                if ((loadType & LOAD_BACKGROUND_IMAGE) != 0) {
                    fragment.mBackgroundHelper.setBackground(drawable);
                }
            }
        }
    }
}
