/*
 * 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.ui.sidepanel;

import android.content.Context;
import android.content.SharedPreferences;
import android.media.tv.TvContract.Channels;
import android.os.Bundle;
import androidx.leanback.widget.VerticalGridView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.common.util.SharedPreferencesUtils;
import com.android.tv.data.ChannelImpl;
import com.android.tv.data.ChannelNumber;
import com.android.tv.data.api.Channel;
import com.android.tv.ui.OnRepeatedKeyInterceptListener;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class CustomizeChannelListFragment extends SideFragment {
    private static final int GROUP_BY_SOURCE = 0;
    private static final int GROUP_BY_HD_SD = 1;
    private static final String TRACKER_LABEL = "customize channel list";

    private static final String PREF_KEY_GROUP_SETTINGS = "pref_key_group_settigns";

    private final List<Channel> mChannels = new ArrayList<>();
    private long mInitialChannelId = Channel.INVALID_ID;
    private long mLastFocusedChannelId = Channel.INVALID_ID;

    private static Integer sGroupingType;
    private TvInputManagerHelper mInputManager;
    private ChannelImpl.DefaultComparator mChannelComparator;
    private boolean mGroupByFragmentRunning;

    private final List<Item> mItems = new ArrayList<>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mInputManager = getMainActivity().getTvInputManagerHelper();
        mInitialChannelId = getMainActivity().getCurrentChannelId();
        mChannelComparator = new ChannelImpl.DefaultComparator(getActivity(), mInputManager);
        if (sGroupingType == null) {
            SharedPreferences sharedPreferences =
                    getContext()
                            .getSharedPreferences(
                                    SharedPreferencesUtils.SHARED_PREF_UI_SETTINGS,
                                    Context.MODE_PRIVATE);
            sGroupingType = sharedPreferences.getInt(PREF_KEY_GROUP_SETTINGS, GROUP_BY_SOURCE);
        }
    }

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = super.onCreateView(inflater, container, savedInstanceState);
        VerticalGridView listView = (VerticalGridView) view.findViewById(R.id.side_panel_list);
        listView.setOnKeyInterceptListener(
                new OnRepeatedKeyInterceptListener(listView) {
                    @Override
                    public boolean onInterceptKeyEvent(KeyEvent event) {
                        // In order to send tune operation once for continuous channel up/down
                        // events,
                        // we only call the moveToChannel method on ACTION_UP event of channel
                        // switch keys.
                        if (event.getAction() == KeyEvent.ACTION_UP) {
                            switch (event.getKeyCode()) {
                                case KeyEvent.KEYCODE_DPAD_UP:
                                case KeyEvent.KEYCODE_DPAD_DOWN:
                                    if (mLastFocusedChannelId != Channel.INVALID_ID) {
                                        getMainActivity()
                                                .tuneToChannel(
                                                        getChannelDataManager()
                                                                .getChannel(mLastFocusedChannelId));
                                    }
                                    break;
                            }
                        }
                        return super.onInterceptKeyEvent(event);
                    }
                });

        if (!mGroupByFragmentRunning) {
            getMainActivity().startShrunkenTvView(false, true);

            int initialChannelPosition = INVALID_POSITION;
            int i = 0;
            for (Item item : mItems) {
                if (item instanceof ChannelItem
                        && ((ChannelItem) item).getChannel().getId() == mInitialChannelId) {
                    initialChannelPosition = i;
                    break;
                }
                ++i;
            }
            if (initialChannelPosition != INVALID_POSITION) {
                setSelectedPosition(initialChannelPosition);
            } else {
                setSelectedPosition(0);
            }
            mLastFocusedChannelId = mInitialChannelId;
            MainActivity tvActivity = getMainActivity();
            if (mLastFocusedChannelId != Channel.INVALID_ID
                    && mLastFocusedChannelId != tvActivity.getCurrentChannelId()) {
                tvActivity.tuneToChannel(getChannelDataManager().getChannel(mLastFocusedChannelId));
            }
        }
        mGroupByFragmentRunning = false;
        return view;
    }

    @Override
    public void onDestroyView() {
        getChannelDataManager().applyUpdatedValuesToDb();
        super.onDestroyView();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        getMainActivity().endShrunkenTvView();
    }

    @Override
    protected String getTitle() {
        return getString(R.string.side_panel_title_edit_channels_for_an_input);
    }

    @Override
    public String getTrackerLabel() {
        return TRACKER_LABEL;
    }

    @Override
    protected List<Item> getItemList() {
        mItems.clear();
        mChannels.clear();
        mChannels.addAll(getChannelDataManager().getChannelList());
        if (sGroupingType == GROUP_BY_SOURCE) {
            addItemForGroupBySource(mItems);
        } else {
            // GROUP_BY_HD_SD
            addItemForGroupByHdSd(mItems);
        }
        return mItems;
    }

    private void cleanUpOneChannelGroupItem(List<Item> items) {
        Iterator<Item> iter = items.iterator();
        while (iter.hasNext()) {
            Item item = iter.next();
            if (item instanceof SelectGroupItem) {
                SelectGroupItem selectGroupItem = (SelectGroupItem) item;
                if (selectGroupItem.mChannelItemsInGroup.size() == 1) {
                    selectGroupItem.mChannelItemsInGroup.get(0).mSelectGroupItem = null;
                    iter.remove();
                }
            }
        }
    }

    private void addItemForGroupBySource(List<Item> items) {
        items.add(new GroupBySubMenu(getString(R.string.edit_channels_group_by_sources)));
        SelectGroupItem selectGroupItem = null;
        ArrayList<Channel> channels = new ArrayList<>(mChannels);
        Collections.sort(channels, mChannelComparator);

        String inputId = null;
        for (Channel channel : channels) {
            if (!channel.getInputId().equals(inputId)) {
                inputId = channel.getInputId();
                String inputLabel =
                        Utils.loadLabel(getActivity(), mInputManager.getTvInputInfo(inputId));
                items.add(new DividerItem(inputLabel));
                selectGroupItem = new SelectGroupItem();
                items.add(selectGroupItem);
            }
            ChannelItem channelItem = new ChannelItem(channel, selectGroupItem);
            items.add(channelItem);
            selectGroupItem.addChannelItem(channelItem);
        }
        cleanUpOneChannelGroupItem(items);
    }

    private void addItemForGroupByHdSd(List<Item> items) {
        items.add(new GroupBySubMenu(getString(R.string.edit_channels_group_by_hd_sd)));
        SelectGroupItem selectGroupItem = null;
        ArrayList<Channel> channels = new ArrayList<>(mChannels);
        Collections.sort(
                channels,
                (Channel lhs, Channel rhs) -> {
                    boolean lhsHd = isHdChannel(lhs);
                    boolean rhsHd = isHdChannel(rhs);
                    if (lhsHd == rhsHd) {
                        return ChannelNumber.compare(
                                lhs.getDisplayNumber(), rhs.getDisplayNumber());
                    } else {
                        return lhsHd ? -1 : 1;
                    }
                });

        Boolean isHdGroup = null;
        for (Channel channel : channels) {
            boolean isHd = isHdChannel(channel);
            if (isHdGroup == null || isHd != isHdGroup) {
                isHdGroup = isHd;
                items.add(
                        new DividerItem(
                                isHd
                                        ? getString(R.string.edit_channels_group_divider_for_hd)
                                        : getString(R.string.edit_channels_group_divider_for_sd)));
                selectGroupItem = new SelectGroupItem();
                items.add(selectGroupItem);
            }
            ChannelItem channelItem = new ChannelItem(channel, selectGroupItem);
            items.add(channelItem);
            selectGroupItem.addChannelItem(channelItem);
        }
        cleanUpOneChannelGroupItem(items);
    }

    private static boolean isHdChannel(Channel channel) {
        String videoFormat = channel.getVideoFormat();
        return videoFormat != null
                && (Channels.VIDEO_FORMAT_720P.equals(videoFormat)
                        || Channels.VIDEO_FORMAT_1080I.equals(videoFormat)
                        || Channels.VIDEO_FORMAT_1080P.equals(videoFormat)
                        || Channels.VIDEO_FORMAT_2160P.equals(videoFormat)
                        || Channels.VIDEO_FORMAT_4320P.equals(videoFormat));
    }

    private class SelectGroupItem extends ActionItem {
        private final List<ChannelItem> mChannelItemsInGroup = new ArrayList<>();
        private TextView mTextView;
        private boolean mAllChecked;

        public SelectGroupItem() {
            super(null);
        }

        private void addChannelItem(ChannelItem channelItem) {
            mChannelItemsInGroup.add(channelItem);
        }

        @Override
        protected void onBind(View view) {
            super.onBind(view);
            mTextView = (TextView) view.findViewById(R.id.title);
        }

        @Override
        protected void onUpdate() {
            super.onUpdate();
            mAllChecked = true;
            for (ChannelItem channelItem : mChannelItemsInGroup) {
                if (!channelItem.getChannel().isBrowsable()) {
                    mAllChecked = false;
                    break;
                }
            }
            mTextView.setText(
                    getString(
                            mAllChecked
                                    ? R.string.edit_channels_item_deselect_group
                                    : R.string.edit_channels_item_select_group));
        }

        @Override
        protected void onSelected() {
            for (ChannelItem channelItem : mChannelItemsInGroup) {
                Channel channel = channelItem.getChannel();
                if (channel.isBrowsable() == mAllChecked) {
                    getChannelDataManager().updateBrowsable(channel.getId(), !mAllChecked, true);
                    channelItem.notifyUpdated();
                }
            }
            getChannelDataManager().notifyChannelBrowsableChanged();
            mAllChecked = !mAllChecked;
            mTextView.setText(
                    getString(
                            mAllChecked
                                    ? R.string.edit_channels_item_deselect_group
                                    : R.string.edit_channels_item_select_group));
        }
    }

    private class ChannelItem extends ChannelCheckItem {
        private SelectGroupItem mSelectGroupItem;

        public ChannelItem(Channel channel, SelectGroupItem selectGroupItem) {
            super(channel, getChannelDataManager(), getProgramDataManager());
            mSelectGroupItem = selectGroupItem;
        }

        @Override
        protected void onUpdate() {
            super.onUpdate();
            setChecked(getChannel().isBrowsable());
        }

        @Override
        protected void onSelected() {
            super.onSelected();
            getChannelDataManager().updateBrowsable(getChannel().getId(), isChecked());
            if (mSelectGroupItem != null) {
                mSelectGroupItem.notifyUpdated();
            }
        }

        @Override
        protected void onFocused() {
            super.onFocused();
            mLastFocusedChannelId = getChannel().getId();
        }
    }

    public static class GroupByFragment extends SideFragment {
        @Override
        protected String getTitle() {
            return getString(R.string.side_panel_title_group_by);
        }

        @Override
        public String getTrackerLabel() {
            return GroupBySubMenu.TRACKER_LABEL;
        }

        @Override
        protected List<Item> getItemList() {
            List<Item> items = new ArrayList<>();
            items.add(
                    new RadioButtonItem(getString(R.string.edit_channels_group_by_sources)) {
                        @Override
                        protected void onSelected() {
                            super.onSelected();
                            setGroupingType(GROUP_BY_SOURCE);
                            closeFragment();
                        }
                    });
            items.add(
                    new RadioButtonItem(getString(R.string.edit_channels_group_by_hd_sd)) {
                        @Override
                        protected void onSelected() {
                            super.onSelected();
                            setGroupingType(GROUP_BY_HD_SD);
                            closeFragment();
                        }
                    });
            ((RadioButtonItem) items.get(sGroupingType)).setChecked(true);
            return items;
        }

        private void setGroupingType(int groupingType) {
            sGroupingType = groupingType;
            SharedPreferences sharedPreferences =
                    getContext()
                            .getSharedPreferences(
                                    SharedPreferencesUtils.SHARED_PREF_UI_SETTINGS,
                                    Context.MODE_PRIVATE);
            sharedPreferences.edit().putInt(PREF_KEY_GROUP_SETTINGS, groupingType).apply();
        }
    }

    private class GroupBySubMenu extends SubMenuItem {
        private static final String TRACKER_LABEL = "Group by";

        public GroupBySubMenu(String description) {
            super(
                    getString(R.string.edit_channels_item_group_by),
                    description,
                    getMainActivity().getOverlayManager().getSideFragmentManager());
        }

        @Override
        protected SideFragment getFragment() {
            return new GroupByFragment();
        }

        @Override
        protected void onSelected() {
            mGroupByFragmentRunning = true;
            super.onSelected();
        }
    }
}
