package com.common.bluetooth.service;

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import com.common.bluetooth.callback.BtScanCallBack;
import com.common.bluetooth.interfaces.IBluetoothSearch;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 蓝牙扫描服务封装类
 *
 * @author wangym
 * @since 2021-10-19
 */
public class BluetoothLeSearcher implements IBluetoothSearch {
    private static final String TAG = "BluetoothLeSearcher";

    private final BluetoothAdapter mBluetoothAdapter;

    private final Handler mHandler;

    private final Handler mAlertHandler;

    private BtScanCallBack mScanCallback;

    private final Context mContext;

    private AtomicBoolean mScanning = new AtomicBoolean(false);

    BluetoothLeSearcher(Context context, BluetoothAdapter adapter, Handler worker) {
        mContext = context;
        mBluetoothAdapter = adapter;
        mHandler = worker;

        HandlerThread thread = new HandlerThread("bluetooth searcher handler");
        thread.start();
        mAlertHandler = new Handler(thread.getLooper());
    }

    private BtScanCallBack wrapCallBack(final BtScanCallBack callBack) {
        return new BtScanCallBack() {
            @Override
            public void onComplete() {
                runOn(callBack::onComplete);
            }

            @Override
            public void onError(String msg) {
                runOn(() -> callBack.onError(msg));
            }

            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                runOn(() -> callBack.onResult(result));
            }

            @Override
            public void onBatchScanResults(List<ScanResult> results) {
                super.onBatchScanResults(results);
            }
        };
    }

    /**
     * 停止扫描
     */
    protected void stopLeScan() {
        if (mScanning.get()) {
            mScanning.set(false);
            mScanCallback.onComplete();

            mAlertHandler.removeCallbacksAndMessages(null);
            mBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);

            mScanCallback = null;
        }
    }

    /**
     * 停止BLE设备扫描
     */
    public void stopScan() {
        runOn(this::stopScan);
    }

    private void runOn(Runnable runnable) {
        mHandler.post(runnable);
    }

    @Override
    public void startScan(long delayTime, @Nullable BtScanCallBack callBack) {
        runOn(() -> {
            int permissionCheck1 = ContextCompat.checkSelfPermission(mContext,
                    Manifest.permission.ACCESS_COARSE_LOCATION);

            int permissionCheck2 = ContextCompat.checkSelfPermission(mContext,
                    Manifest.permission.ACCESS_FINE_LOCATION);

            if ((permissionCheck1 | permissionCheck2) != PackageManager.PERMISSION_GRANTED
                    && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                String err = "Cannot have location permission";
                Log.e(TAG, err);
                callBack.onError(err);
                return;
            }

            if (mScanning.get()) {
                stopLeScan();
            }

            mScanCallback = wrapCallBack(callBack);

            // Stops scanning after a pre-defined scan period.
            // 预先定义停止蓝牙扫描的时间（因为蓝牙扫描需要消耗较多的电量）
            mAlertHandler.removeCallbacksAndMessages(null);
            mAlertHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopLeScan();
                }
            }, delayTime);

            mScanning.set(true);

            // 定义一个回调接口供扫描结束处理
            // 指定扫描特定的支持service的蓝牙设备
            // call startLeScan(UUID[],    BluetoothAdapter.LeScanCallback)
            // 可以使用rssi计算蓝牙设备的距离
            // 计算公式：
            // d = 10^((abs(RSSI) - A) / (10 * n))
            // 其中：
            // d - 计算所得距离
            // RSSI - 接收信号强度（负值）
            // A - 射端和接收端相隔1米时的信号强度
            // n - 环境衰减因子
            mBluetoothAdapter.getBluetoothLeScanner().startScan(mScanCallback);
        });
    }

    @Override
    public boolean isScanning() {
        return mScanning.get();
    }
}
