// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include <QtCore/QLoggingCategory>
#include <QtCore/qrandom.h>
#include "localdevicebroadcastreceiver_p.h"
#include "android/jni_android_p.h"

QT_BEGIN_NAMESPACE

Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)

const char *scanModes[] = {"SCAN_MODE_NONE", "SCAN_MODE_CONNECTABLE", "SCAN_MODE_CONNECTABLE_DISCOVERABLE"};
const char *bondModes[] = {"BOND_NONE", "BOND_BONDING", "BOND_BONDED"};

LocalDeviceBroadcastReceiver::LocalDeviceBroadcastReceiver(QObject *parent) :
    AndroidBroadcastReceiver(parent), previousScanMode(0)
{
    addAction(valueForStaticField<QtJniTypes::BluetoothDevice, JavaNames::ActionBondStateChanged>());
    addAction(valueForStaticField<QtJniTypes::BluetoothAdapter, JavaNames::ActionScanModeChanged>());
    addAction(valueForStaticField<QtJniTypes::BluetoothDevice, JavaNames::ActionAclConnected>());
    addAction(valueForStaticField<QtJniTypes::BluetoothDevice, JavaNames::ActionAclDisconnected>());

    //cache integer values for host & bonding mode
    //don't use the java fields directly but refer to them by name
    for (uint i = 0; i < (sizeof(hostModePreset)/sizeof(hostModePreset[0])); i++) {
        hostModePreset[i] = QJniObject::getStaticField<jint>(
                                             QtJniTypes::className<QtJniTypes::BluetoothAdapter>(),
                                             scanModes[i]);
    }

    for (uint i = 0; i < (sizeof(bondingModePreset)/sizeof(bondingModePreset[0])); i++) {
        bondingModePreset[i] = QJniObject::getStaticField<jint>(
                                              QtJniTypes::className<QtJniTypes::BluetoothDevice>(),
                                              bondModes[i]);
    }
}

void LocalDeviceBroadcastReceiver::onReceive(JNIEnv *env, jobject context, jobject intent)
{
    Q_UNUSED(context);
    Q_UNUSED(env);

    QJniObject intentObject(intent);
    const QString action = intentObject.callMethod<jstring>("getAction").toString();
    qCDebug(QT_BT_ANDROID) << QStringLiteral("LocalDeviceBroadcastReceiver::onReceive() - event: %1").arg(action);

    if (action == valueForStaticField<QtJniTypes::BluetoothAdapter,
                                      JavaNames::ActionScanModeChanged>().toString()) {

        const QJniObject extrasBundle =
                intentObject.callMethod<QtJniTypes::Bundle>("getExtras");
        const QJniObject keyExtra = valueForStaticField<QtJniTypes::BluetoothAdapter,
                                                        JavaNames::ExtraScanMode>();

        int extra = extrasBundle.callMethod<jint>("getInt", keyExtra.object<jstring>());

        if (previousScanMode != extra) {
            previousScanMode = extra;

            if (extra == hostModePreset[0])
                emit hostModeStateChanged(QBluetoothLocalDevice::HostPoweredOff);
            else if (extra == hostModePreset[1])
                emit hostModeStateChanged(QBluetoothLocalDevice::HostConnectable);
            else if (extra == hostModePreset[2])
                emit hostModeStateChanged(QBluetoothLocalDevice::HostDiscoverable);
            else
                qCWarning(QT_BT_ANDROID) << "Unknown Host State";
        }
    } else if (action == valueForStaticField<QtJniTypes::BluetoothDevice,
                                                        JavaNames::ActionBondStateChanged>().toString()) {
        //get BluetoothDevice
        QJniObject keyExtra = valueForStaticField<QtJniTypes::BluetoothDevice,
                                                  JavaNames::ExtraDevice>();
        const QJniObject bluetoothDevice =
                intentObject.callMethod<QtJniTypes::Parcelable>("getParcelableExtra",
                                                                keyExtra.object<jstring>());

        //get new bond state
        keyExtra = valueForStaticField<QtJniTypes::BluetoothDevice,
                                       JavaNames::ExtraBondState>();
        const QJniObject extrasBundle =
                intentObject.callMethod<QtJniTypes::Bundle>("getExtras");
        int bondState = extrasBundle.callMethod<jint>("getInt", keyExtra.object<jstring>());

        QBluetoothAddress address(bluetoothDevice.callMethod<jstring>("getAddress").toString());
        if (address.isNull())
                return;

        if (bondState == bondingModePreset[0])
            emit pairingStateChanged(address, QBluetoothLocalDevice::Unpaired);
        else if (bondState == bondingModePreset[1])
            ; //we ignore this as Qt doesn't have equivalent API value
        else if (bondState == bondingModePreset[2])
            emit pairingStateChanged(address, QBluetoothLocalDevice::Paired);
        else
            qCWarning(QT_BT_ANDROID) << "Unknown BOND_STATE_CHANGED value:" << bondState;

    } else if (action == valueForStaticField<QtJniTypes::BluetoothDevice,
                                             JavaNames::ActionAclConnected>().toString() ||
               action == valueForStaticField<QtJniTypes::BluetoothDevice,
                                             JavaNames::ActionAclDisconnected>().toString()) {

        const QString connectEvent = valueForStaticField<QtJniTypes::BluetoothDevice,
                                                         JavaNames::ActionAclConnected>().toString();
        const bool isConnectEvent =
                action == connectEvent ? true : false;

        //get BluetoothDevice
        const QJniObject keyExtra = valueForStaticField<QtJniTypes::BluetoothDevice,
                                                        JavaNames::ExtraDevice>();
        QJniObject bluetoothDevice =
                intentObject.callMethod<QtJniTypes::Parcelable>("getParcelableExtra",
                                                                keyExtra.object<jstring>());

        QBluetoothAddress address(bluetoothDevice.callMethod<jstring>("getAddress").toString());
        if (address.isNull())
            return;

        emit connectDeviceChanges(address, isConnectEvent);
    }
}

QT_END_NAMESPACE
