/*
 * Copyright (C) 2023 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.settings.connecteddevice.audiosharing.audiostreams;

import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.ActionButtonsPreference;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class AudioStreamButtonController extends BasePreferenceController
        implements DefaultLifecycleObserver {
    private static final String TAG = "AudioStreamButtonController";
    private static final String KEY = "audio_stream_button";
    private static final int SOURCE_ORIGIN_REPOSITORY = SourceOriginForLogging.REPOSITORY.ordinal();
    private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
            new AudioStreamsBroadcastAssistantCallback() {
                @Override
                public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
                    super.onSourceRemoved(sink, sourceId, reason);
                    updateButton();
                }

                @Override
                public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {
                    super.onSourceRemoveFailed(sink, sourceId, reason);
                    updateButton();
                    mMetricsFeatureProvider.action(
                            mContext, SettingsEnums.ACTION_AUDIO_STREAM_LEAVE_FAILED);
                }

                @Override
                public void onReceiveStateChanged(
                        BluetoothDevice sink,
                        int sourceId,
                        BluetoothLeBroadcastReceiveState state) {
                    super.onReceiveStateChanged(sink, sourceId, state);
                    if (AudioStreamsHelper.isConnected(state)) {
                        updateButton();
                        mMetricsFeatureProvider.action(
                                mContext,
                                SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED,
                                SOURCE_ORIGIN_REPOSITORY);
                    }
                }

                @Override
                public void onSourceAddFailed(
                        BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) {
                    super.onSourceAddFailed(sink, source, reason);
                    updateButton();
                    mMetricsFeatureProvider.action(
                            mContext,
                            SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_OTHER,
                            SOURCE_ORIGIN_REPOSITORY);
                }

                @Override
                public void onSourceLost(int broadcastId) {
                    super.onSourceLost(broadcastId);
                    updateButton();
                }
            };

    private final AudioStreamsRepository mAudioStreamsRepository =
            AudioStreamsRepository.getInstance();
    private final Executor mExecutor;
    private final AudioStreamsHelper mAudioStreamsHelper;
    private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
    private final MetricsFeatureProvider mMetricsFeatureProvider;
    private @Nullable ActionButtonsPreference mPreference;
    private int mBroadcastId = -1;

    public AudioStreamButtonController(Context context, String preferenceKey) {
        super(context, preferenceKey);
        mExecutor = Executors.newSingleThreadExecutor();
        mAudioStreamsHelper = new AudioStreamsHelper(Utils.getLocalBtManager(context));
        mLeBroadcastAssistant = mAudioStreamsHelper.getLeBroadcastAssistant();
        mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
    }

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        if (mLeBroadcastAssistant == null) {
            Log.w(TAG, "onStart(): LeBroadcastAssistant is null!");
            return;
        }
        mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        if (mLeBroadcastAssistant == null) {
            Log.w(TAG, "onStop(): LeBroadcastAssistant is null!");
            return;
        }
        mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
    }

    @Override
    public final void displayPreference(PreferenceScreen screen) {
        mPreference = screen.findPreference(getPreferenceKey());
        updateButton();
        super.displayPreference(screen);
    }

    private void updateButton() {
        if (mPreference == null) {
            Log.w(TAG, "updateButton(): preference is null!");
            return;
        }
        boolean isConnected =
                mAudioStreamsHelper.getAllConnectedSources().stream()
                        .map(BluetoothLeBroadcastReceiveState::getBroadcastId)
                        .anyMatch(connectedBroadcastId -> connectedBroadcastId == mBroadcastId);

        View.OnClickListener onClickListener;

        if (isConnected) {
            onClickListener =
                    unused ->
                            ThreadUtils.postOnBackgroundThread(
                                    () -> {
                                        mAudioStreamsHelper.removeSource(mBroadcastId);
                                        mMetricsFeatureProvider.action(
                                                mContext,
                                                SettingsEnums
                                                        .ACTION_AUDIO_STREAM_LEAVE_BUTTON_CLICK);
                                        ThreadUtils.postOnMainThread(
                                                () -> {
                                                    if (mPreference != null) {
                                                        mPreference.setButton1Enabled(false);
                                                    }
                                                });
                                    });
            ThreadUtils.postOnMainThread(
                    () -> {
                        if (mPreference != null) {
                            mPreference.setButton1Enabled(true);
                            mPreference
                                    .setButton1Text(R.string.audio_streams_disconnect)
                                    .setButton1Icon(
                                            com.android.settings.R.drawable.ic_settings_close)
                                    .setButton1OnClickListener(onClickListener);
                        }
                    });
        } else {
            onClickListener =
                    unused ->
                            ThreadUtils.postOnBackgroundThread(
                                    () -> {
                                        var metadata =
                                                mAudioStreamsRepository.getSavedMetadata(
                                                        mContext, mBroadcastId);
                                        if (metadata != null) {
                                            mAudioStreamsHelper.addSource(metadata);
                                            mMetricsFeatureProvider.action(
                                                    mContext,
                                                    SettingsEnums.ACTION_AUDIO_STREAM_JOIN,
                                                    SOURCE_ORIGIN_REPOSITORY);
                                            ThreadUtils.postOnMainThread(
                                                    () -> {
                                                        if (mPreference != null) {
                                                            mPreference.setButton1Enabled(false);
                                                        }
                                                    });
                                        }
                                    });
            ThreadUtils.postOnMainThread(
                    () -> {
                        if (mPreference != null) {
                            mPreference.setButton1Enabled(true);
                            mPreference
                                    .setButton1Text(R.string.audio_streams_connect)
                                    .setButton1Icon(com.android.settings.R.drawable.ic_add_24dp)
                                    .setButton1OnClickListener(onClickListener);
                        }
                    });
        }
    }

    @Override
    public int getAvailabilityStatus() {
        return AVAILABLE;
    }

    @Override
    public String getPreferenceKey() {
        return KEY;
    }

    /** Initialize with broadcast id */
    void init(int broadcastId) {
        mBroadcastId = broadcastId;
    }
}
