/*
 * 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.media.tv.TvContract;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.android.tv.R;
import com.android.tv.TvSingletons;
import com.android.tv.data.api.Channel;
import com.android.tv.data.api.Program;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.ui.DvrUiHelper;

/** A class for details content. */
public class DetailsContent {
    /** Constant for invalid time. */
    public static final long INVALID_TIME = -1;

    private CharSequence mTitle;
    private long mStartTimeUtcMillis;
    private long mEndTimeUtcMillis;
    private String mDescription;
    private String mLogoImageUri;
    private String mBackgroundImageUri;
    private boolean mUsingChannelLogo;
    private boolean mShowErrorMessage;

    static DetailsContent createFromRecordedProgram(
            Context context, RecordedProgram recordedProgram) {
        return new DetailsContent.Builder()
                .setChannelId(recordedProgram.getChannelId())
                .setProgramTitle(recordedProgram.getTitle())
                .setSeasonNumber(recordedProgram.getSeasonNumber())
                .setEpisodeNumber(recordedProgram.getEpisodeNumber())
                .setStartTimeUtcMillis(recordedProgram.getStartTimeUtcMillis())
                .setEndTimeUtcMillis(recordedProgram.getEndTimeUtcMillis())
                .setDescription(
                        TextUtils.isEmpty(recordedProgram.getLongDescription())
                                ? recordedProgram.getDescription()
                                : recordedProgram.getLongDescription())
                .setPosterArtUri(recordedProgram.getPosterArtUri())
                .setThumbnailUri(recordedProgram.getThumbnailUri())
                .build(context);
    }

    public static DetailsContent createFromProgram(Context context, Program program) {
        return new DetailsContent.Builder()
                .setChannelId(program.getChannelId())
                .setProgramTitle(program.getTitle())
                .setSeasonNumber(program.getSeasonNumber())
                .setEpisodeNumber(program.getEpisodeNumber())
                .setStartTimeUtcMillis(program.getStartTimeUtcMillis())
                .setEndTimeUtcMillis(program.getEndTimeUtcMillis())
                .setDescription(
                        TextUtils.isEmpty(program.getLongDescription())
                                ? program.getDescription()
                                : program.getLongDescription())
                .setPosterArtUri(program.getPosterArtUri())
                .setThumbnailUri(program.getThumbnailUri())
                .build(context);
    }

    static DetailsContent createFromSeriesRecording(
            Context context, SeriesRecording seriesRecording) {
        return new DetailsContent.Builder()
                .setChannelId(seriesRecording.getChannelId())
                .setTitle(seriesRecording.getTitle())
                .setDescription(
                        TextUtils.isEmpty(seriesRecording.getLongDescription())
                                ? seriesRecording.getDescription()
                                : seriesRecording.getLongDescription())
                .setPosterArtUri(seriesRecording.getPosterUri())
                .setThumbnailUri(seriesRecording.getPhotoUri())
                .build(context);
    }

    static DetailsContent createFromScheduledRecording(
            Context context, ScheduledRecording scheduledRecording) {
        Channel channel =
                TvSingletons.getSingletons(context)
                        .getChannelDataManager()
                        .getChannel(scheduledRecording.getChannelId());
        String description;
        if (scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
            description = getErrorMessage(context, scheduledRecording);
        } else {
            description =
                    !TextUtils.isEmpty(scheduledRecording.getProgramDescription())
                            ? scheduledRecording.getProgramDescription()
                            : scheduledRecording.getProgramLongDescription();
        }
        if (TextUtils.isEmpty(description)) {
            description = channel != null ? channel.getDescription() : null;
        }
        return new DetailsContent.Builder()
                .setChannelId(scheduledRecording.getChannelId())
                .setProgramTitle(scheduledRecording.getProgramTitle())
                .setSeasonNumber(scheduledRecording.getSeasonNumber())
                .setEpisodeNumber(scheduledRecording.getEpisodeNumber())
                .setStartTimeUtcMillis(scheduledRecording.getStartTimeMs())
                .setEndTimeUtcMillis(scheduledRecording.getEndTimeMs())
                .setDescription(description)
                .setPosterArtUri(scheduledRecording.getProgramPosterArtUri())
                .setThumbnailUri(scheduledRecording.getProgramThumbnailUri())
                .setShowErrorMessage(
                        scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED)
                .build(context);
    }

    private static String getErrorMessage(Context context, ScheduledRecording recording) {
        int reason =
                recording.getFailedReason() == null
                        ? ScheduledRecording.FAILED_REASON_OTHER
                        : recording.getFailedReason();
        switch (reason) {
            case ScheduledRecording.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED:
                return context.getString(R.string.dvr_recording_failed_not_started);
            case ScheduledRecording.FAILED_REASON_RESOURCE_BUSY:
                return context.getString(R.string.dvr_recording_failed_resource_busy);
            case ScheduledRecording.FAILED_REASON_INPUT_UNAVAILABLE:
                return context.getString(
                        R.string.dvr_recording_failed_input_unavailable, recording.getInputId());
            case ScheduledRecording.FAILED_REASON_INPUT_DVR_UNSUPPORTED:
                return context.getString(R.string.dvr_recording_failed_input_dvr_unsupported);
            case ScheduledRecording.FAILED_REASON_INSUFFICIENT_SPACE:
                return context.getString(R.string.dvr_recording_failed_insufficient_space);
            case ScheduledRecording.FAILED_REASON_OTHER: // fall through
            case ScheduledRecording.FAILED_REASON_NOT_FINISHED: // fall through
            case ScheduledRecording.FAILED_REASON_SCHEDULER_STOPPED: // fall through
            case ScheduledRecording.FAILED_REASON_INVALID_CHANNEL: // fall through
            case ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT: // fall through
            case ScheduledRecording.FAILED_REASON_CONNECTION_FAILED: // fall through
            default:
                return context.getString(R.string.dvr_recording_failed_system_failure, reason);
        }
    }

    private DetailsContent() {}

    /** Returns title. */
    public CharSequence getTitle() {
        return mTitle;
    }

    /** Returns start time. */
    public long getStartTimeUtcMillis() {
        return mStartTimeUtcMillis;
    }

    /** Returns end time. */
    public long getEndTimeUtcMillis() {
        return mEndTimeUtcMillis;
    }

    /** Returns description. */
    public String getDescription() {
        return mDescription;
    }

    /** Returns Logo image URI as a String. */
    public String getLogoImageUri() {
        return mLogoImageUri;
    }

    /** Returns background image URI as a String. */
    public String getBackgroundImageUri() {
        return mBackgroundImageUri;
    }

    /** Returns if image URIs are from its channels' logo. */
    public boolean isUsingChannelLogo() {
        return mUsingChannelLogo;
    }

    /** Returns if the error message should be shown. */
    public boolean shouldShowErrorMessage() {
        return mShowErrorMessage;
    }

    /** Copies other details content. */
    public void copyFrom(DetailsContent other) {
        if (this == other) {
            return;
        }
        mTitle = other.mTitle;
        mStartTimeUtcMillis = other.mStartTimeUtcMillis;
        mEndTimeUtcMillis = other.mEndTimeUtcMillis;
        mDescription = other.mDescription;
        mLogoImageUri = other.mLogoImageUri;
        mBackgroundImageUri = other.mBackgroundImageUri;
        mUsingChannelLogo = other.mUsingChannelLogo;
        mShowErrorMessage = other.mShowErrorMessage;
    }

    /** A class for building details content. */
    public static final class Builder {
        private final DetailsContent mDetailsContent;

        private long mChannelId;
        private String mProgramTitle;
        private String mSeasonNumber;
        private String mEpisodeNumber;
        private String mPosterArtUri;
        private String mThumbnailUri;

        public Builder() {
            mDetailsContent = new DetailsContent();
            mDetailsContent.mStartTimeUtcMillis = INVALID_TIME;
            mDetailsContent.mEndTimeUtcMillis = INVALID_TIME;
        }

        /** Sets title. */
        public Builder setTitle(CharSequence title) {
            mDetailsContent.mTitle = title;
            return this;
        }

        /** Sets start time. */
        public Builder setStartTimeUtcMillis(long startTimeUtcMillis) {
            mDetailsContent.mStartTimeUtcMillis = startTimeUtcMillis;
            return this;
        }

        /** Sets end time. */
        public Builder setEndTimeUtcMillis(long endTimeUtcMillis) {
            mDetailsContent.mEndTimeUtcMillis = endTimeUtcMillis;
            return this;
        }

        /** Sets description. */
        public Builder setDescription(String description) {
            mDetailsContent.mDescription = description;
            return this;
        }

        /** Sets logo image URI as a String. */
        public Builder setLogoImageUri(String logoImageUri) {
            mDetailsContent.mLogoImageUri = logoImageUri;
            return this;
        }

        /** Sets background image URI as a String. */
        public Builder setBackgroundImageUri(String backgroundImageUri) {
            mDetailsContent.mBackgroundImageUri = backgroundImageUri;
            return this;
        }

        private Builder setProgramTitle(String programTitle) {
            mProgramTitle = programTitle;
            return this;
        }

        private Builder setSeasonNumber(String seasonNumber) {
            mSeasonNumber = seasonNumber;
            return this;
        }

        private Builder setEpisodeNumber(String episodeNumber) {
            mEpisodeNumber = episodeNumber;
            return this;
        }

        private Builder setChannelId(long channelId) {
            mChannelId = channelId;
            return this;
        }

        private Builder setPosterArtUri(String posterArtUri) {
            mPosterArtUri = posterArtUri;
            return this;
        }

        private Builder setThumbnailUri(String thumbnailUri) {
            mThumbnailUri = thumbnailUri;
            return this;
        }

        private Builder setShowErrorMessage(boolean showErrorMessage) {
            mDetailsContent.mShowErrorMessage = showErrorMessage;
            return this;
        }

        private void createStyledTitle(Context context, Channel channel) {
            CharSequence title =
                    DvrUiHelper.getStyledTitleWithEpisodeNumber(
                            context,
                            mProgramTitle,
                            mSeasonNumber,
                            mEpisodeNumber,
                            R.style.text_appearance_card_view_episode_number);
            if (TextUtils.isEmpty(title)) {
                mDetailsContent.mTitle =
                        channel != null
                                ? channel.getDisplayName()
                                : context.getResources().getString(R.string.no_program_information);
            } else {
                mDetailsContent.mTitle = title;
            }
        }

        private void createImageUris(@Nullable Channel channel) {
            mDetailsContent.mLogoImageUri = null;
            mDetailsContent.mBackgroundImageUri = null;
            mDetailsContent.mUsingChannelLogo = false;
            if (!TextUtils.isEmpty(mPosterArtUri) && !TextUtils.isEmpty(mThumbnailUri)) {
                mDetailsContent.mLogoImageUri = mPosterArtUri;
                mDetailsContent.mBackgroundImageUri = mThumbnailUri;
            } else if (!TextUtils.isEmpty(mPosterArtUri)) {
                // thumbnailUri is empty
                mDetailsContent.mLogoImageUri = mPosterArtUri;
                mDetailsContent.mBackgroundImageUri = mPosterArtUri;
            } else if (!TextUtils.isEmpty(mThumbnailUri)) {
                // posterArtUri is empty
                mDetailsContent.mLogoImageUri = mThumbnailUri;
                mDetailsContent.mBackgroundImageUri = mThumbnailUri;
            }
            if (TextUtils.isEmpty(mDetailsContent.mLogoImageUri) && channel != null) {
                String channelLogoUri = TvContract.buildChannelLogoUri(channel.getId()).toString();
                mDetailsContent.mLogoImageUri = channelLogoUri;
                mDetailsContent.mBackgroundImageUri = channelLogoUri;
                mDetailsContent.mUsingChannelLogo = true;
            }
        }

        /** Builds details content. */
        public DetailsContent build(Context context) {
            Channel channel =
                    TvSingletons.getSingletons(context)
                            .getChannelDataManager()
                            .getChannel(mChannelId);
            if (mDetailsContent.mTitle == null) {
                createStyledTitle(context, channel);
            }
            if (mDetailsContent.mBackgroundImageUri == null
                    && mDetailsContent.mLogoImageUri == null) {
                createImageUris(channel);
            }
            DetailsContent detailsContent = new DetailsContent();
            detailsContent.copyFrom(mDetailsContent);
            return detailsContent;
        }
    }
}
