package com.google.android.mobly.snippet.bundled.bluetooth.profiles;

import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.android.mobly.snippet.Snippet;
import com.google.android.mobly.snippet.bundled.bluetooth.BluetoothAdapterSnippet;
import com.google.android.mobly.snippet.bundled.bluetooth.PairingBroadcastReceiver;
import com.google.android.mobly.snippet.bundled.utils.JsonSerializer;
import com.google.android.mobly.snippet.bundled.utils.Utils;
import com.google.android.mobly.snippet.rpc.Rpc;
import com.google.android.mobly.snippet.rpc.RpcMinSdk;
import com.google.common.base.Ascii;
import java.util.ArrayList;

public class BluetoothHearingAidSnippet implements Snippet {
    private static class BluetoothHearingAidSnippetException extends Exception {
        private static final long serialVersionUID = 1;

        BluetoothHearingAidSnippetException(String msg) {
            super(msg);
        }
    }

    private static final int TIMEOUT_SEC = 60;

    private final Context context;
    private static boolean isHearingAidProfileReady = false;
    private static BluetoothHearingAid hearingAidProfile;
    private final JsonSerializer jsonSerializer = new JsonSerializer();

    @TargetApi(Build.VERSION_CODES.Q)
    public BluetoothHearingAidSnippet() {
        context = InstrumentationRegistry.getInstrumentation().getContext();
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        bluetoothAdapter.getProfileProxy(
                context, new HearingAidServiceListener(), BluetoothProfile.HEARING_AID);
        Utils.waitUntil(() -> isHearingAidProfileReady, TIMEOUT_SEC);
    }

    @TargetApi(Build.VERSION_CODES.Q)
    private static class HearingAidServiceListener implements BluetoothProfile.ServiceListener {
        @Override
        public void onServiceConnected(int var1, BluetoothProfile profile) {
            hearingAidProfile = (BluetoothHearingAid) profile;
            isHearingAidProfileReady = true;
        }

        @Override
        public void onServiceDisconnected(int var1) {
            isHearingAidProfileReady = false;
        }
    }

    @TargetApi(Build.VERSION_CODES.Q)
    @RpcMinSdk(Build.VERSION_CODES.Q)
    @Rpc(description = "Connects to a paired or discovered device with HA profile.")
    public void btHearingAidConnect(String deviceAddress) throws Throwable {
        BluetoothDevice device = BluetoothAdapterSnippet.getKnownDeviceByAddress(deviceAddress);
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
        context.registerReceiver(new PairingBroadcastReceiver(context), filter);
        Utils.invokeByReflection(hearingAidProfile, "connect", device);
        if (!Utils.waitUntil(
                () ->
                        hearingAidProfile.getConnectionState(device)
                                == BluetoothHearingAid.STATE_CONNECTED,
                TIMEOUT_SEC)) {
            throw new BluetoothHearingAidSnippetException(
                    String.format(
                            "Failed to connect to device %s|%s with HA profile within %d sec.",
                            device.getName(), device.getAddress(), TIMEOUT_SEC));
        }
    }

    @Rpc(description = "Disconnects a device from HA profile.")
    public void btHearingAidDisconnect(String deviceAddress) throws Throwable {
        BluetoothDevice device = getConnectedBluetoothDevice(deviceAddress);
        Utils.invokeByReflection(hearingAidProfile, "disconnect", device);
        if (!Utils.waitUntil(
                () ->
                        hearingAidProfile.getConnectionState(device)
                                == BluetoothHearingAid.STATE_DISCONNECTED,
                TIMEOUT_SEC)) {
            throw new BluetoothHearingAidSnippetException(
                    String.format(
                            "Failed to disconnect to device %s|%s with HA profile within %d sec.",
                            device.getName(), device.getAddress(), TIMEOUT_SEC));
        }
    }

    @Rpc(description = "Gets all the devices currently connected via HA profile.")
    public ArrayList<Bundle> btHearingAidGetConnectedDevices() {
        return jsonSerializer.serializeBluetoothDeviceList(hearingAidProfile.getConnectedDevices());
    }

    private static BluetoothDevice getConnectedBluetoothDevice(String deviceAddress)
            throws BluetoothHearingAidSnippetException {
        for (BluetoothDevice device : hearingAidProfile.getConnectedDevices()) {
            if (Ascii.equalsIgnoreCase(device.getAddress(), deviceAddress)) {
                return device;
            }
        }
        throw new BluetoothHearingAidSnippetException(String.format(
                "No device with address %s is connected via HA Profile.", deviceAddress));
    }

    @Override
    public void shutdown() {}
}