/*
 * Copyright (C) 2024 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.systemui.tv.media;

import android.app.KeyguardManager;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaSessionManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerExemptionManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaSwitchingController;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.tv.res.R;
import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;

import javax.annotation.Nullable;
import javax.inject.Inject;

public class OutputDevicesFragment extends Fragment
        implements MediaSwitchingController.Callback, TvMediaOutputAdapter.PanelCallback {

    private static final String TAG = OutputDevicesFragment.class.getSimpleName();
    private static final boolean DEBUG = false;

    private TvMediaOutputController mMediaOutputController;
    private TvMediaOutputAdapter mAdapter;
    private RecyclerView mDevicesRecyclerView;

    private final MediaSessionManager mMediaSessionManager;
    private final LocalBluetoothManager mLocalBluetoothManager;
    private final ActivityStarter mActivityStarter;
    private final CommonNotifCollection mCommonNotifCollection;
    private final DialogTransitionAnimator mDialogTransitionAnimator;
    private final NearbyMediaDevicesManager mNearbyMediaDevicesManager;
    private final AudioManager mAudioManager;
    private final PowerExemptionManager mPowerExemptionManager;
    private final KeyguardManager mKeyguardManager;
    private final FeatureFlags mFeatureFlags;
    private final VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor;
    private final UserTracker mUserTracker;

    protected final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
    private String mActiveDeviceId;

    @Inject
    public OutputDevicesFragment(
            MediaSessionManager mediaSessionManager,
            @Nullable LocalBluetoothManager localBluetoothManager,
            ActivityStarter activityStarter,
            CommonNotifCollection commonNotifCollection,
            DialogTransitionAnimator dialogTransitionAnimator,
            NearbyMediaDevicesManager nearbyMediaDevicesManager,
            AudioManager audioManager,
            PowerExemptionManager powerExemptionManager,
            KeyguardManager keyguardManager,
            FeatureFlags featureFlags,
            VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor,
            UserTracker userTracker) {
        mMediaSessionManager = mediaSessionManager;
        mLocalBluetoothManager = localBluetoothManager;
        mActivityStarter = activityStarter;
        mCommonNotifCollection = commonNotifCollection;
        mDialogTransitionAnimator = dialogTransitionAnimator;
        mNearbyMediaDevicesManager = nearbyMediaDevicesManager;
        mAudioManager = audioManager;
        mPowerExemptionManager = powerExemptionManager;
        mKeyguardManager = keyguardManager;
        mFeatureFlags = featureFlags;
        mVolumePanelGlobalStateInteractor = volumePanelGlobalStateInteractor;
        mUserTracker = userTracker;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMediaOutputController =
                new TvMediaOutputController(
                        getContext(),
                        getContext().getPackageName(),
                        mMediaSessionManager,
                        mLocalBluetoothManager,
                        mActivityStarter,
                        mCommonNotifCollection,
                        mDialogTransitionAnimator,
                        mNearbyMediaDevicesManager,
                        mAudioManager,
                        mPowerExemptionManager,
                        mKeyguardManager,
                        mFeatureFlags,
                        mVolumePanelGlobalStateInteractor,
                        mUserTracker);
        mAdapter = new TvMediaOutputAdapter(getContext(), mMediaOutputController, this);
    }

    @Nullable
    @Override
    public View onCreateView(
            @NonNull LayoutInflater inflater,
            @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.media_output_main_fragment, null);

        mDevicesRecyclerView = view.requireViewById(R.id.device_list);
        mDevicesRecyclerView.setLayoutManager(new LayoutManagerWrapper(view.getContext()));
        mDevicesRecyclerView.setAdapter(mAdapter);

        mDevicesRecyclerView.addOnScrollListener(
                new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                        super.onScrolled(recyclerView, dx, dy);
                        Drawable foreground = FadingEdgeUtil.getForegroundDrawable(
                                recyclerView, requireContext());
                        if (foreground != recyclerView.getForeground()) {
                            recyclerView.setForeground(foreground);
                        }
                    }
                });

        int itemSpacingPx = getResources().getDimensionPixelSize(R.dimen.media_dialog_item_spacing);
        mDevicesRecyclerView.addItemDecoration(new SpacingDecoration(itemSpacingPx));

        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        mMediaOutputController.start(this);
    }

    @Override
    public void onStop() {
        mMediaOutputController.stop();
        super.onStop();
    }

    @Override
    public void onResume() {
        super.onResume();
        if (DEBUG) Log.d(TAG, "resuming OutputDevicesFragment");
        int position = mAdapter.getFocusPosition();
        mDevicesRecyclerView.getLayoutManager().scrollToPosition(position);
        // Ensure layout is complete before requesting focus.
        mDevicesRecyclerView.post(() -> {
            View itemToFocus = mDevicesRecyclerView.getLayoutManager().findViewByPosition(position);
            if (itemToFocus != null) {
                itemToFocus.requestFocus();
            }
        });
    }

    private void refresh(boolean deviceSetChanged) {
        if (DEBUG) Log.d(TAG, "refresh: deviceSetChanged " + deviceSetChanged);
        // If the dialog is going away or is already refreshing, do nothing.
        if (mMediaOutputController.isRefreshing()) {
            return;
        }
        mMediaOutputController.setRefreshing(true);
        mAdapter.updateItems();
    }

    @Override
    public void onMediaChanged() {
        // NOOP
    }

    @Override
    public void onMediaStoppedOrPaused() {
        // NOOP
    }

    @Override
    public void onRouteChanged() {
        mMainThreadHandler.post(() -> refresh(/* deviceSetChanged= */ false));
        MediaDevice activeDevice = mMediaOutputController.getCurrentConnectedMediaDevice();
        if (mActiveDeviceId != null && !mActiveDeviceId.equals(activeDevice.getId())) {
            mMediaOutputController.showVolumeDialog();
        }
        mActiveDeviceId = activeDevice.getId();
    }

    @Override
    public void onDeviceListChanged() {
        mMainThreadHandler.post(() -> refresh(/* deviceSetChanged= */ true));
        if (mActiveDeviceId == null
                && mMediaOutputController.getCurrentConnectedMediaDevice() != null) {
            mActiveDeviceId = mMediaOutputController.getCurrentConnectedMediaDevice().getId();
        }
    }

    @Override
    public void dismissDialog() {
        if (DEBUG) Log.d(TAG, "dismissDialog");
        if (getActivity() != null) {
            getActivity().finish();
        }
    }

    @Override
    public void openDeviceSettings(
            String uri, CharSequence title, CharSequence subtitle, String id) {
        FragmentManager fragmentManager = getParentFragmentManager();
        Bundle deviceInfo = new Bundle();
        deviceInfo.putString("uri", uri);
        deviceInfo.putCharSequence("title", title);
        deviceInfo.putCharSequence("subtitle", subtitle);
        deviceInfo.putString("deviceId", id);
        fragmentManager.setFragmentResult("deviceSettings", deviceInfo);
    }

    private class LayoutManagerWrapper extends LinearLayoutManager {
        LayoutManagerWrapper(Context context) {
            super(context);
        }

        @Override
        public void onLayoutCompleted(RecyclerView.State state) {
            super.onLayoutCompleted(state);
            mMediaOutputController.setRefreshing(false);
            mMediaOutputController.refreshDataSetIfNeeded();
        }
    }

    private static class SpacingDecoration extends RecyclerView.ItemDecoration {
        private final int mMarginPx;

        SpacingDecoration(int marginPx) {
            mMarginPx = marginPx;
        }

        @Override
        public void getItemOffsets(
                Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            if (parent.getChildAdapterPosition(view) == 0) {
                outRect.top = mMarginPx;
            }
            outRect.bottom = mMarginPx;
        }
    }
}
