package llc.arma.ble.data

import android.Manifest
import android.app.Application
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothProfile
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.core.app.ActivityCompat
import llc.arma.ble.domain.Result
import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.model.Ble
import java.util.UUID

class WriteBeaconCallback(
    private val app: Application,
    private var request: Ble.Beacon.WriteRequest,
    private val onResult: (Result<Unit, BleException>) -> Unit
) : BluetoothGattCallback() {

    private var flashed = false

    override fun onConnectionStateChange(
        gatt: BluetoothGatt,
        status: Int,
        newState: Int
    ) {
        super.onConnectionStateChange(gatt, status, newState)

        if(checkPermission()) {

            if(status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {

                gatt.discoverServices()

            } else {

                onResult(Result.failure(BleException.UnexpectedResponse))

            }

        } else {

            onResult(Result.failure(BleException.PermissionDenied))

        }

    }

    override fun onServicesDiscovered(
        gatt: BluetoothGatt,
        status: Int
    ) {
        super.onServicesDiscovered(gatt, status)
        onCycle(gatt, status)

    }

    private fun onCycle(
        gatt: BluetoothGatt,
        status: Int
    ){

        if(request.tx != null) {

            var uuid: Pair<UUID, ByteArray>? = null

            uuid = request.tx?.let {

                this.request = request.copy(
                    tx = null
                )

                Pair(
                    txWriteUUID,
                    byteArrayOf(
                        when (it) {
                            Ble.BleState.TX.MINUS_40 -> -40
                            Ble.BleState.TX.MINUS_20 -> -20
                            Ble.BleState.TX.MINUS_16 -> -16
                            Ble.BleState.TX.MINUS_12 -> -12
                            Ble.BleState.TX.MINUS_8 -> -8
                            Ble.BleState.TX.MINUS_4 -> -4
                            Ble.BleState.TX.ZERO -> 0
                            Ble.BleState.TX.PLUS_3 -> 3
                            Ble.BleState.TX.PLUS_4 -> 4
                        }
                    )
                )

            } ?: uuid

            uuid?.let { uuid ->

                gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull {
                    it.uuid == uuid.first
                }?.let {

                    gatt.writeCharacteristic(it, uuid.second)

                    return

                }

            }

            onResult(Result.failure(BleException.UnexpectedResponse))

        } else {

            if(flashed.not()){

                flashed = true

                gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull {
                    it.uuid == flashWriteUUID
                }?.let {

                    gatt.writeCharacteristic(it, byteArrayOf(9))

                    return

                }

                onResult(Result.failure(BleException.UnexpectedResponse))

            } else {

                onResult(Result.success(Unit))

            }

        }

    }

    override fun onCharacteristicWrite(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        status: Int
    ) {

        Log.d("beacon", "onCharacteristicWrite $status")

        super.onCharacteristicWrite(gatt, characteristic, status)

        if(checkPermission()) {

            if(status == BluetoothGatt.GATT_SUCCESS || flashed) {

                onCycle(gatt, status)

            } else {

                onResult(Result.failure(BleException.UnexpectedResponse))

            }

        } else {

            onResult(Result.failure(BleException.PermissionDenied))

        }

    }

    fun BluetoothGatt.writeCharacteristic(
        characteristic: BluetoothGattCharacteristic,
        data: ByteArray
    ): Result<Unit, BleException> {

        return if(checkPermission()){

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
            }else{

                characteristic.writeType
                characteristic.value = data
                writeCharacteristic(characteristic)
            }

            Result.success(Unit)

        } else {
            Result.failure(BleException.PermissionDenied)
        }

    }

    fun checkPermission(): Boolean {

        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) ==
                    PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) ==
                    PackageManager.PERMISSION_GRANTED
        } else {
            return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) ==
                    PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) ==
                    PackageManager.PERMISSION_GRANTED
        }
    }

}