/*
 * 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;

import android.graphics.drawable.Drawable;
import android.media.tv.TvInputInfo;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.leanback.widget.GuidanceStylist.Guidance;
import androidx.leanback.widget.GuidedAction;

import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.api.Channel;
import com.android.tv.data.api.Program;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.recorder.ConflictChecker;
import com.android.tv.dvr.recorder.ConflictChecker.OnUpcomingConflictChangeListener;
import com.android.tv.util.Utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
    private static final String TAG = "DvrConflictFragment";
    private static final boolean DEBUG = false;

    private static final int ACTION_DELETE_CONFLICT = 1;
    private static final int ACTION_CANCEL = 2;
    private static final int ACTION_VIEW_SCHEDULES = 3;

    // The program count which will be listed in the description. This is the number of the
    // program strings in R.plurals.dvr_program_conflict_dialog_description_many.
    private static final int LISTED_PROGRAM_COUNT = 2;

    protected List<ScheduledRecording> mConflicts;

    void setConflicts(List<ScheduledRecording> conflicts) {
        mConflicts = conflicts;
    }

    List<ScheduledRecording> getConflicts() {
        return mConflicts;
    }

    @Override
    public int onProvideTheme() {
        return R.style.Theme_TV_Dvr_Conflict_GuidedStep;
    }

    @Override
    public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
        actions.add(
                new GuidedAction.Builder(getContext())
                        .clickAction(GuidedAction.ACTION_ID_OK)
                        .build());
        actions.add(
                new GuidedAction.Builder(getContext())
                        .id(ACTION_VIEW_SCHEDULES)
                        .title(R.string.dvr_action_view_schedules)
                        .build());
    }

    @Override
    public void onTrackedGuidedActionClicked(GuidedAction action) {
        if (action.getId() == ACTION_VIEW_SCHEDULES) {
            DvrUiHelper.startSchedulesActivityForOneTimeRecordingConflict(
                    getContext(), getConflicts());
        }
        dismissDialog();
        // Finish the Recording setting Activity on dismissal.
        if (getActivity() instanceof DvrRecordingSettingsActivity) {
            getActivity().finish();
        }
    }

    @Override
    public String getTrackerLabelForGuidedAction(GuidedAction action) {
        long actionId = getId();
        if (actionId == ACTION_VIEW_SCHEDULES) {
            return "view-schedules";
        } else {
            return super.getTrackerLabelForGuidedAction(action);
        }
    }

    String getConflictDescription() {
        List<String> titles = new ArrayList<>();
        HashSet<String> titleSet = new HashSet<>();
        for (ScheduledRecording schedule : getConflicts()) {
            String scheduleTitle = getScheduleTitle(schedule);
            if (scheduleTitle != null && !titleSet.contains(scheduleTitle)) {
                titles.add(scheduleTitle);
                titleSet.add(scheduleTitle);
            }
        }
        switch (titles.size()) {
            case 0:
                Log.i(
                        TAG,
                        "Conflict has been resolved by any reason. Maybe input might have"
                                + " been deleted.");
                return null;
            case 1:
                return getResources()
                        .getString(
                                R.string.dvr_program_conflict_dialog_description_1, titles.get(0));
            case 2:
                return getResources()
                        .getString(
                                R.string.dvr_program_conflict_dialog_description_2,
                                titles.get(0),
                                titles.get(1));
            case 3:
                return getResources()
                        .getString(
                                R.string.dvr_program_conflict_dialog_description_3,
                                titles.get(0),
                                titles.get(1));
            default:
                return getResources()
                        .getQuantityString(
                                R.plurals.dvr_program_conflict_dialog_description_many,
                                titles.size() - LISTED_PROGRAM_COUNT,
                                titles.get(0),
                                titles.get(1),
                                titles.size() - LISTED_PROGRAM_COUNT);
        }
    }

    @Nullable
    private String getScheduleTitle(ScheduledRecording schedule) {
        if (schedule.getType() == ScheduledRecording.TYPE_TIMED) {
            Channel channel =
                    TvSingletons.getSingletons(getContext())
                            .getChannelDataManager()
                            .getChannel(schedule.getChannelId());
            if (channel != null) {
                return channel.getDisplayName();
            } else {
                return null;
            }
        } else {
            return schedule.getProgramTitle();
        }
    }

    /** A fragment to show the program conflict. */
    public static class DvrProgramConflictFragment extends DvrConflictFragment {
        private Program mProgram;

        @Override
        public View onCreateView(
                LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            Bundle args = getArguments();
            if (args != null) {
                mProgram = args.getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM);
            }
            SoftPreconditions.checkArgument(mProgram != null);
            TvInputInfo input = Utils.getTvInputInfoForProgram(getContext(), mProgram);
            SoftPreconditions.checkNotNull(input);
            List<ScheduledRecording> conflicts = null;
            if (input != null) {
                conflicts =
                        TvSingletons.getSingletons(getContext())
                                .getDvrManager()
                                .getConflictingSchedules(mProgram);
            }
            if (conflicts == null) {
                conflicts = Collections.emptyList();
            }
            if (conflicts.isEmpty()) {
                dismissDialog();
            }
            setConflicts(conflicts);
            return super.onCreateView(inflater, container, savedInstanceState);
        }

        @NonNull
        @Override
        public Guidance onCreateGuidance(Bundle savedInstanceState) {
            String title = getResources().getString(R.string.dvr_program_conflict_dialog_title);
            String descriptionPrefix =
                    getString(
                            R.string.dvr_program_conflict_dialog_description_prefix,
                            mProgram.getTitle());
            String description = getConflictDescription();
            if (description == null) {
                dismissDialog();
            }
            Drawable icon = getResources().getDrawable(R.drawable.quantum_ic_error_white_48, null);
            return new Guidance(title, descriptionPrefix + " " + description, null, icon);
        }

        @Override
        public String getTrackerPrefix() {
            return "DvrProgramConflictFragment";
        }
    }

    /** A fragment to show the channel recording conflict. */
    public static class DvrChannelRecordConflictFragment extends DvrConflictFragment {
        private Channel mChannel;
        private long mStartTimeMs;
        private long mEndTimeMs;

        @Override
        public View onCreateView(
                LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            Bundle args = getArguments();
            long channelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID);
            mChannel =
                    TvSingletons.getSingletons(getContext())
                            .getChannelDataManager()
                            .getChannel(channelId);
            SoftPreconditions.checkArgument(mChannel != null);
            TvInputInfo input = Utils.getTvInputInfoForChannelId(getContext(), mChannel.getId());
            SoftPreconditions.checkNotNull(input);
            List<ScheduledRecording> conflicts = null;
            if (input != null) {
                mStartTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_START_TIME_MS);
                mEndTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_END_TIME_MS);
                conflicts =
                        TvSingletons.getSingletons(getContext())
                                .getDvrManager()
                                .getConflictingSchedules(
                                        mChannel.getId(), mStartTimeMs, mEndTimeMs);
            }
            if (conflicts == null) {
                conflicts = Collections.emptyList();
            }
            if (conflicts.isEmpty()) {
                dismissDialog();
            }
            setConflicts(conflicts);
            return super.onCreateView(inflater, container, savedInstanceState);
        }

        @NonNull
        @Override
        public Guidance onCreateGuidance(Bundle savedInstanceState) {
            String title = getResources().getString(R.string.dvr_channel_conflict_dialog_title);
            String descriptionPrefix =
                    getString(
                            R.string.dvr_channel_conflict_dialog_description_prefix,
                            mChannel.getDisplayName());
            String description = getConflictDescription();
            if (description == null) {
                dismissDialog();
            }
            Drawable icon = getResources().getDrawable(R.drawable.quantum_ic_error_white_48, null);
            return new Guidance(title, descriptionPrefix + " " + description, null, icon);
        }

        @Override
        public String getTrackerPrefix() {
            return "DvrChannelRecordConflictFragment";
        }
    }

    /**
     * A fragment to show the channel watching conflict.
     *
     * <p>This fragment is automatically closed when there are no upcoming conflicts.
     */
    public static class DvrChannelWatchConflictFragment extends DvrConflictFragment
            implements OnUpcomingConflictChangeListener {
        private long mChannelId;

        @Override
        public View onCreateView(
                LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            Bundle args = getArguments();
            if (args != null) {
                mChannelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID);
            }
            SoftPreconditions.checkArgument(mChannelId != Channel.INVALID_ID);
            ConflictChecker checker = ((MainActivity) getContext()).getDvrConflictChecker();
            List<ScheduledRecording> conflicts = null;
            if (checker != null) {
                checker.addOnUpcomingConflictChangeListener(this);
                conflicts = checker.getUpcomingConflicts();
                if (DEBUG) Log.d(TAG, "onCreateView: upcoming conflicts: " + conflicts);
                if (conflicts.isEmpty()) {
                    dismissDialog();
                }
            }
            if (conflicts == null) {
                if (DEBUG) Log.d(TAG, "onCreateView: There's no conflict.");
                conflicts = Collections.emptyList();
            }
            if (conflicts.isEmpty()) {
                dismissDialog();
            }
            setConflicts(conflicts);
            return super.onCreateView(inflater, container, savedInstanceState);
        }

        @NonNull
        @Override
        public Guidance onCreateGuidance(Bundle savedInstanceState) {
            String title =
                    getResources().getString(R.string.dvr_epg_channel_watch_conflict_dialog_title);
            String description =
                    getResources()
                            .getString(R.string.dvr_epg_channel_watch_conflict_dialog_description);
            return new Guidance(title, description, null, null);
        }

        @Override
        public void onCreateActions(
                @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
            actions.add(
                    new GuidedAction.Builder(getContext())
                            .id(ACTION_DELETE_CONFLICT)
                            .title(R.string.dvr_action_delete_schedule)
                            .build());
            actions.add(
                    new GuidedAction.Builder(getContext())
                            .id(ACTION_CANCEL)
                            .title(R.string.dvr_action_record_program)
                            .build());
        }

        @Override
        public void onTrackedGuidedActionClicked(GuidedAction action) {
            if (action.getId() == ACTION_CANCEL) {
                ConflictChecker checker = ((MainActivity) getContext()).getDvrConflictChecker();
                if (checker != null) {
                    checker.setCheckedConflictsForChannel(mChannelId, getConflicts());
                }
            } else if (action.getId() == ACTION_DELETE_CONFLICT) {
                for (ScheduledRecording schedule : mConflicts) {
                    if (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
                        getDvrManager().stopRecording(schedule);
                    } else {
                        getDvrManager().removeScheduledRecording(schedule);
                    }
                }
            }
            super.onGuidedActionClicked(action);
        }

        @Override
        public String getTrackerPrefix() {
            return "DvrChannelWatchConflictFragment";
        }

        @Override
        public String getTrackerLabelForGuidedAction(GuidedAction action) {
            long actionId = action.getId();
            if (actionId == ACTION_CANCEL) {
                return "cancel";
            } else if (actionId == ACTION_DELETE_CONFLICT) {
                return "delete";
            } else {
                return super.getTrackerLabelForGuidedAction(action);
            }
        }

        @Override
        public void onDetach() {
            ConflictChecker checker = ((MainActivity) getContext()).getDvrConflictChecker();
            if (checker != null) {
                checker.removeOnUpcomingConflictChangeListener(this);
            }
            super.onDetach();
        }

        @Override
        public void onUpcomingConflictChange() {
            ConflictChecker checker = ((MainActivity) getContext()).getDvrConflictChecker();
            if (checker == null || checker.getUpcomingConflicts().isEmpty()) {
                if (DEBUG) Log.d(TAG, "onUpcomingConflictChange: There's no conflict.");
                dismissDialog();
            }
        }
    }
}
