/*
 * 自然写互动课堂应用开发SDK软件 V1.0
 * PenManager - Android端蓝牙点阵笔连接管理器
 *
 * 功能说明：
 * 1. BLE 5.0蓝牙扫描与自动连接
 * 2. GATT服务发现与特征值订阅
 * 3. 点阵笔数据实时接收与解析
 * 4. 多笔同时连接管理（最多支持60支）
 * 5. 连接状态监控与自动重连
 * 6. 电量/固件版本/设备信息查询
 */

package com.writech.sdk.android;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelUuid;
import android.util.Log;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 点阵笔蓝牙连接管理器
 * 负责BLE扫描、连接、数据接收的全生命周期管理
 */
public class PenManager {

    private static final String TAG = "WritechPenManager";

    /* 自然写点阵笔GATT服务UUID（自定义） */
    private static final UUID PEN_SERVICE_UUID =
        UUID.fromString("0000FFE0-0000-1000-8000-00805F9B34FB");

    /* 笔迹数据通知特征值UUID */
    private static final UUID STROKE_DATA_CHAR_UUID =
        UUID.fromString("0000FFE1-0000-1000-8000-00805F9B34FB");

    /* 笔控制指令写入特征值UUID */
    private static final UUID PEN_CONTROL_CHAR_UUID =
        UUID.fromString("0000FFE2-0000-1000-8000-00805F9B34FB");

    /* 设备信息特征值UUID（电量/固件版本） */
    private static final UUID DEVICE_INFO_CHAR_UUID =
        UUID.fromString("0000FFE3-0000-1000-8000-00805F9B34FB");

    /* CCCD描述符UUID，用于启用通知 */
    private static final UUID CCCD_UUID =
        UUID.fromString("00002902-0000-1000-8000-00805F9B34FB");

    /* 最大同时连接数 */
    private static final int MAX_CONNECTIONS = 60;

    /* 自动重连延迟（毫秒） */
    private static final long RECONNECT_DELAY_MS = 3000;

    /* 扫描超时时间（毫秒） */
    private static final long SCAN_TIMEOUT_MS = 30000;

    /* ========== 成员变量 ========== */

    private final Context mContext;
    private final BluetoothAdapter mBluetoothAdapter;
    private BluetoothLeScanner mScanner;

    /* 已连接的笔设备映射表（MAC地址 → GATT连接） */
    private final Map<String, BluetoothGatt> mConnectedPens = new ConcurrentHashMap<>();

    /* 等待重连的设备列表 */
    private final Map<String, Integer> mReconnectAttempts = new ConcurrentHashMap<>();

    /* 设备信息缓存（MAC地址 → 设备模型） */
    private final Map<String, PenDeviceInfo> mDeviceInfoCache = new ConcurrentHashMap<>();

    /* 数据回调监听器列表 */
    private final List<PenDataListener> mDataListeners = new CopyOnWriteArrayList<>();

    /* 连接状态监听器列表 */
    private final List<PenConnectionListener> mConnectionListeners = new CopyOnWriteArrayList<>();

    /* BLE操作专用线程 */
    private HandlerThread mBleThread;
    private Handler mBleHandler;

    /* 扫描状态标志 */
    private volatile boolean mIsScanning = false;

    /* ========== 内部数据结构 ========== */

    /** 笔设备信息缓存 */
    private static class PenDeviceInfo {
        String macAddress;           /* MAC地址 */
        String penName;              /* 笔名称 */
        String firmwareVersion;      /* 固件版本 */
        int batteryLevel;            /* 电量百分比 */
        long lastDataTimestamp;      /* 最后一次收到数据的时间 */
        boolean isWriting;           /* 是否正在书写 */
    }

    /* ========== 对外回调接口 ========== */

    /** 笔迹数据监听器 */
    public interface PenDataListener {
        /** 收到笔迹坐标数据 */
        void onStrokeData(String penMac, int x, int y, int pressure, long timestamp);
        /** 笔抬起事件（一笔结束） */
        void onPenUp(String penMac, long timestamp);
        /** 笔落下事件（一笔开始） */
        void onPenDown(String penMac, long timestamp);
    }

    /** 连接状态监听器 */
    public interface PenConnectionListener {
        void onPenConnected(String penMac, String penName);
        void onPenDisconnected(String penMac, int reason);
        void onPenDiscovered(String penMac, String penName, int rssi);
        void onBatteryUpdate(String penMac, int batteryPercent);
    }

    /* ========== 构造与初始化 ========== */

    /**
     * 创建笔管理器实例
     * @param context  Android上下文（需要蓝牙权限）
     */
    public PenManager(Context context) {
        mContext = context.getApplicationContext();
        BluetoothManager btManager =
            (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = btManager.getAdapter();

        /* 创建BLE操作专用后台线程 */
        mBleThread = new HandlerThread("WritechBLE");
        mBleThread.start();
        mBleHandler = new Handler(mBleThread.getLooper());

        Log.i(TAG, "PenManager初始化完成，蓝牙状态: "
              + (mBluetoothAdapter.isEnabled() ? "已开启" : "未开启"));
    }

    /** 注册笔迹数据监听器 */
    public void addDataListener(PenDataListener listener) {
        if (listener != null && !mDataListeners.contains(listener)) {
            mDataListeners.add(listener);
        }
    }

    /** 移除笔迹数据监听器 */
    public void removeDataListener(PenDataListener listener) {
        mDataListeners.remove(listener);
    }

    /** 注册连接状态监听器 */
    public void addConnectionListener(PenConnectionListener listener) {
        if (listener != null && !mConnectionListeners.contains(listener)) {
            mConnectionListeners.add(listener);
        }
    }

    /* ========== BLE扫描 ========== */

    /**
     * 开始扫描附近的自然写点阵笔
     * 使用低延迟模式扫描BLE设备，按服务UUID过滤
     */
    public void startScan() {
        if (mIsScanning) {
            Log.w(TAG, "扫描已在进行中，忽略重复请求");
            return;
        }

        if (!mBluetoothAdapter.isEnabled()) {
            Log.e(TAG, "蓝牙未开启，无法扫描");
            return;
        }

        mScanner = mBluetoothAdapter.getBluetoothLeScanner();
        if (mScanner == null) {
            Log.e(TAG, "获取BLE扫描器失败");
            return;
        }

        /* 构建扫描过滤器：仅扫描包含自然写服务UUID的设备 */
        ScanFilter filter = new ScanFilter.Builder()
            .setServiceUuid(new ParcelUuid(PEN_SERVICE_UUID))
            .build();
        List<ScanFilter> filters = Collections.singletonList(filter);

        /* 低延迟扫描设置（耗电较高，适合主动扫描场景） */
        ScanSettings settings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
            .build();

        mScanner.startScan(filters, settings, mScanCallback);
        mIsScanning = true;

        /* 设置扫描超时，避免长时间扫描耗电 */
        mBleHandler.postDelayed(this::stopScan, SCAN_TIMEOUT_MS);

        Log.i(TAG, "开始扫描自然写点阵笔...");
    }

    /** 停止BLE扫描 */
    public void stopScan() {
        if (mIsScanning && mScanner != null) {
            mScanner.stopScan(mScanCallback);
            mIsScanning = false;
            Log.i(TAG, "停止扫描");
        }
    }

    /** BLE扫描回调 */
    private final ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            BluetoothDevice device = result.getDevice();
            String mac = device.getAddress();
            String name = device.getName();
            int rssi = result.getRssi();

            if (name == null || name.isEmpty()) {
                name = "WritechPen-" + mac.substring(mac.length() - 5);
            }

            /* 通知上层发现了新的笔设备 */
            for (PenConnectionListener listener : mConnectionListeners) {
                listener.onPenDiscovered(mac, name, rssi);
            }

            Log.d(TAG, "发现笔设备: " + name + " [" + mac + "] RSSI=" + rssi);
        }

        @Override
        public void onScanFailed(int errorCode) {
            mIsScanning = false;
            Log.e(TAG, "BLE扫描失败，错误码: " + errorCode);
        }
    };

    /* ========== BLE连接管理 ========== */

    /**
     * 连接指定MAC地址的点阵笔
     * @param macAddress  设备MAC地址
     */
    public void connectPen(String macAddress) {
        if (mConnectedPens.size() >= MAX_CONNECTIONS) {
            Log.w(TAG, "已达最大连接数 " + MAX_CONNECTIONS + "，拒绝新连接");
            return;
        }

        if (mConnectedPens.containsKey(macAddress)) {
            Log.w(TAG, "设备已连接: " + macAddress);
            return;
        }

        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(macAddress);
        /* 使用TRANSPORT_LE确保走BLE通道，autoConnect=false立即连接 */
        device.connectGatt(mContext, false, mGattCallback, BluetoothDevice.TRANSPORT_LE);
        Log.i(TAG, "正在连接笔设备: " + macAddress);
    }

    /** 断开指定笔的连接 */
    public void disconnectPen(String macAddress) {
        BluetoothGatt gatt = mConnectedPens.remove(macAddress);
        if (gatt != null) {
            gatt.disconnect();
            gatt.close();
            mReconnectAttempts.remove(macAddress);
            Log.i(TAG, "已断开笔设备: " + macAddress);
        }
    }

    /** 断开所有已连接的笔 */
    public void disconnectAll() {
        for (Map.Entry<String, BluetoothGatt> entry : mConnectedPens.entrySet()) {
            entry.getValue().disconnect();
            entry.getValue().close();
        }
        mConnectedPens.clear();
        mReconnectAttempts.clear();
        Log.i(TAG, "已断开所有笔设备");
    }

    /** 获取当前已连接的笔数量 */
    public int getConnectedCount() {
        return mConnectedPens.size();
    }

    /** 获取所有已连接笔的MAC地址列表 */
    public List<String> getConnectedPenMacs() {
        return new ArrayList<>(mConnectedPens.keySet());
    }

    /* ========== GATT回调处理 ========== */

    /**
     * GATT连接/数据回调
     * 处理连接状态变化、服务发现、数据通知等所有BLE事件
     */
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String mac = gatt.getDevice().getAddress();

            if (newState == BluetoothProfile.STATE_CONNECTED) {
                /* 连接成功，开始发现GATT服务 */
                mConnectedPens.put(mac, gatt);
                mReconnectAttempts.remove(mac);
                gatt.discoverServices();

                String name = gatt.getDevice().getName();
                for (PenConnectionListener listener : mConnectionListeners) {
                    listener.onPenConnected(mac, name != null ? name : "Unknown");
                }
                Log.i(TAG, "笔设备连接成功: " + mac + "，正在发现服务...");

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                /* 连接断开，尝试自动重连 */
                mConnectedPens.remove(mac);
                gatt.close();

                for (PenConnectionListener listener : mConnectionListeners) {
                    listener.onPenDisconnected(mac, status);
                }
                Log.w(TAG, "笔设备断开: " + mac + "，状态码: " + status);

                /* 自动重连逻辑（最多尝试5次） */
                scheduleReconnect(mac);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status != BluetoothGatt.GATT_SUCCESS) {
                Log.e(TAG, "GATT服务发现失败: " + status);
                return;
            }

            /* 查找自然写笔迹数据服务 */
            BluetoothGattService penService = gatt.getService(PEN_SERVICE_UUID);
            if (penService == null) {
                Log.e(TAG, "未找到自然写笔服务，设备可能不兼容");
                return;
            }

            /* 订阅笔迹数据通知特征值 */
            BluetoothGattCharacteristic strokeChar =
                penService.getCharacteristic(STROKE_DATA_CHAR_UUID);
            if (strokeChar != null) {
                gatt.setCharacteristicNotification(strokeChar, true);

                /* 写入CCCD描述符启用通知 */
                BluetoothGattDescriptor cccd = strokeChar.getDescriptor(CCCD_UUID);
                if (cccd != null) {
                    cccd.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    gatt.writeDescriptor(cccd);
                }
                Log.i(TAG, "已订阅笔迹数据通知");
            }

            /* 读取设备信息（电量、固件版本） */
            BluetoothGattCharacteristic infoChar =
                penService.getCharacteristic(DEVICE_INFO_CHAR_UUID);
            if (infoChar != null) {
                mBleHandler.postDelayed(() -> gatt.readCharacteristic(infoChar), 500);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                             BluetoothGattCharacteristic characteristic) {
            String mac = gatt.getDevice().getAddress();
            UUID charUuid = characteristic.getUuid();

            if (STROKE_DATA_CHAR_UUID.equals(charUuid)) {
                /* 收到笔迹数据通知，解析并分发 */
                byte[] data = characteristic.getValue();
                parseAndDispatchStrokeData(mac, data);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                          BluetoothGattCharacteristic characteristic,
                                          int status) {
            if (status != BluetoothGatt.GATT_SUCCESS) return;

            String mac = gatt.getDevice().getAddress();
            UUID charUuid = characteristic.getUuid();

            if (DEVICE_INFO_CHAR_UUID.equals(charUuid)) {
                /* 解析设备信息数据 */
                byte[] data = characteristic.getValue();
                parseDeviceInfo(mac, data);
            }
        }
    };

    /* ========== 数据解析与分发 ========== */

    /**
     * 解析BLE收到的笔迹数据帧并分发给监听器
     * 数据格式（7字节紧凑编码）：
     * [0-1] X坐标高16位  [2-3] Y坐标高16位
     * [4] X低4位|Y低4位  [5] 压力高8位  [6] 压力低4位|标志
     */
    private void parseAndDispatchStrokeData(String penMac, byte[] data) {
        if (data == null || data.length < 7) {
            return;
        }

        long timestamp = System.currentTimeMillis();

        /* 检查帧类型标志（最低2位） */
        int flags = data[6] & 0x03;

        if (flags == 0x01) {
            /* 笔落下事件 */
            for (PenDataListener listener : mDataListeners) {
                listener.onPenDown(penMac, timestamp);
            }
            return;
        }

        if (flags == 0x02) {
            /* 笔抬起事件 */
            for (PenDataListener listener : mDataListeners) {
                listener.onPenUp(penMac, timestamp);
            }
            return;
        }

        /* 坐标数据帧（flags == 0x00） */
        int xHigh = ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
        int xLow = (data[4] >> 4) & 0x0F;
        int x = (xHigh << 4) | xLow;

        int yHigh = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
        int yLow = data[4] & 0x0F;
        int y = (yHigh << 4) | yLow;

        int pHigh = data[5] & 0xFF;
        int pLow = (data[6] >> 4) & 0x0F;
        int pressure = (pHigh << 4) | pLow;

        /* 更新设备状态 */
        PenDeviceInfo info = mDeviceInfoCache.get(penMac);
        if (info != null) {
            info.lastDataTimestamp = timestamp;
            info.isWriting = true;
        }

        /* 分发到所有监听器 */
        for (PenDataListener listener : mDataListeners) {
            listener.onStrokeData(penMac, x, y, pressure, timestamp);
        }
    }

    /** 解析设备信息特征值数据 */
    private void parseDeviceInfo(String penMac, byte[] data) {
        if (data == null || data.length < 4) return;

        PenDeviceInfo info = mDeviceInfoCache.get(penMac);
        if (info == null) {
            info = new PenDeviceInfo();
            info.macAddress = penMac;
            mDeviceInfoCache.put(penMac, info);
        }

        /* 第一字节：电量百分比 */
        info.batteryLevel = data[0] & 0xFF;

        /* 第2-4字节：固件版本 major.minor.patch */
        info.firmwareVersion = (data[1] & 0xFF) + "." + (data[2] & 0xFF)
                               + "." + (data[3] & 0xFF);

        /* 通知电量更新 */
        for (PenConnectionListener listener : mConnectionListeners) {
            listener.onBatteryUpdate(penMac, info.batteryLevel);
        }

        Log.i(TAG, "设备信息 [" + penMac + "] 电量:" + info.batteryLevel
              + "% 固件:" + info.firmwareVersion);
    }

    /* ========== 自动重连 ========== */

    /** 安排自动重连（指数退避） */
    private void scheduleReconnect(String macAddress) {
        Integer attempts = mReconnectAttempts.getOrDefault(macAddress, 0);
        if (attempts >= 5) {
            Log.w(TAG, "设备 " + macAddress + " 重连次数已达上限，放弃重连");
            mReconnectAttempts.remove(macAddress);
            return;
        }

        mReconnectAttempts.put(macAddress, attempts + 1);

        /* 指数退避：3s, 6s, 12s, 24s, 48s */
        long delay = RECONNECT_DELAY_MS * (1L << attempts);

        mBleHandler.postDelayed(() -> {
            if (!mConnectedPens.containsKey(macAddress)) {
                Log.i(TAG, "尝试重连设备: " + macAddress + "（第" + (attempts + 1) + "次）");
                connectPen(macAddress);
            }
        }, delay);
    }

    /* ========== 控制指令发送 ========== */

    /**
     * 向笔发送控制指令
     * @param macAddress  目标笔MAC
     * @param command     指令字节数组
     * @return 是否发送成功
     */
    public boolean sendCommand(String macAddress, byte[] command) {
        BluetoothGatt gatt = mConnectedPens.get(macAddress);
        if (gatt == null) {
            Log.w(TAG, "设备未连接，无法发送指令: " + macAddress);
            return false;
        }

        BluetoothGattService service = gatt.getService(PEN_SERVICE_UUID);
        if (service == null) return false;

        BluetoothGattCharacteristic controlChar =
            service.getCharacteristic(PEN_CONTROL_CHAR_UUID);
        if (controlChar == null) return false;

        controlChar.setValue(command);
        controlChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
        return gatt.writeCharacteristic(controlChar);
    }

    /** 查询笔电量 */
    public int getBatteryLevel(String macAddress) {
        PenDeviceInfo info = mDeviceInfoCache.get(macAddress);
        return info != null ? info.batteryLevel : -1;
    }

    /* ========== 资源释放 ========== */

    /** 释放PenManager资源 */
    public void destroy() {
        stopScan();
        disconnectAll();
        mDataListeners.clear();
        mConnectionListeners.clear();
        mDeviceInfoCache.clear();

        if (mBleThread != null) {
            mBleThread.quitSafely();
            mBleThread = null;
        }
        Log.i(TAG, "PenManager资源已释放");
    }
}
