/*
 * Copyright (c) 2024-2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ohos.ace.plugin.bluetoothplugin;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.os.ParcelUuid;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import ohos.ace.adapter.ALog;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class BluetoothHelper {
    private static final String LOG_TAG = "BluetoothHelper";
    private static final int INVALID_LENGTH = 0;
    private static final int CHARACTERISTIC_PROPERTIE_BROADCAST = 0x01;
    private static final int CHARACTERISTIC_PROPERTIE_READ = 0x02;
    private static final int CHARACTERISTIC_PROPERTIE_WRITE = 0x08;
    private static final int CHARACTERISTIC_PROPERTIE_NOTIFY = 0x10;
    private static final int CHARACTERISTIC_PROPERTIE_INDICATE = 0x20;
    private static final int CHARACTERISTIC_PROPERTIE_EXTENDED_PROPERTIES = 0x80;
    private static final String UUID_CHARACTERISTIC_EXTENDED_PROPERTIES = "00002900-0000-1000-8000-00805F9B34FB";
    private static final String UUID_CHARACTERISTIC_USER_DESCRIPTION = "00002901-0000-1000-8000-00805F9B34FB";
    private static final String UUID_CLIENT_CHARACTERISTIC_CONFIGURATION = "00002902-0000-1000-8000-00805F9B34FB";
    private static final String UUID_SERVER_CHARACTERISTIC_CONFIGURATION = "00002903-0000-1000-8000-00805F9B34FB";
    private static final String UUID_CHARACTERISTIC_FROMAT = "00002904-0000-1000-8000-00805F9B34FB";
    private static final String UUID_CHARACTERISTIC_AGGREGATE_FROMAT = "00002905-0000-1000-8000-00805F9B34FB";
    private static final int ANDROID_PERMISSION_READ = 1;
    private static final int ANDROID_PERMISSION_WRITE = 2;
    private static final int PERMISSION_READ = 1;
    private static final int PERMISSION_WRITE = 16;
    private static final int MAX_PERMISSIONS = 16;
    private static final int MIN_PERMISSIONS = 0;
    private static final int MAX_PROPERTIE = 255;
    private static final int MIN_PROPERTIE = 0;

    public static <T> T getValueFromJson(JSONObject jsonObject, String key, Class<T> valueType) {
        if (jsonObject == null) {
            ALog.e(LOG_TAG, "jsonObject is null");
            return null;
        }
        try {
            if (jsonObject.has(key)) {
                Object value = jsonObject.get(key);
                if (valueType.isInstance(value)) {
                    return valueType.cast(value);
                } else if (valueType == Integer.class && value instanceof Number) {
                    return valueType.cast(((Number) value).intValue());
                } else if (valueType == Long.class && value instanceof Number) {
                    return valueType.cast(((Number) value).longValue());
                } else if (valueType == Double.class && value instanceof Number) {
                    return valueType.cast(((Number) value).doubleValue());
                } else if (valueType == Boolean.class && value instanceof Boolean) {
                    return valueType.cast(value);
                } else if (valueType == JSONArray.class && value instanceof JSONArray) {
                    return valueType.cast(value);
                } else if (valueType == String.class && value instanceof String) {
                    return valueType.cast(value);
                } else {
                    ALog.e(LOG_TAG, "The given ValueType : " + key + " does not exist");
                    return null;
                }
            } else {
                ALog.e(LOG_TAG, "JsonData do not has " + key);
                return null;
            }
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "CreateScanFilters failed; err is " + e);
            return null;
        }
    }

    public static byte[] convertJsonArrayToByteArray(JSONObject jsonObject, String key) {
        try {
            JSONArray dataJson = getValueFromJson(jsonObject, key, JSONArray.class);
            if (dataJson == null) {
                ALog.e(LOG_TAG, key + " dataJson is null");
                return null;
            }
            byte[] data = new byte[dataJson.length()];
            for (int i = 0; i < dataJson.length(); i++) {
                data[i] = (byte) dataJson.getInt(i);
            }
            return data;
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "convertJsonArrayToByteArray failed; err is " + e);
            return null;
        }
    }

    public static ScanFilter getBluetoothScanFilter(String stringFilter) {
        String tempData = null;
        ScanFilter.Builder filterBuilder = new ScanFilter.Builder();
        try {
            JSONObject jsonObject = new JSONObject(stringFilter);
            String deviceName = getValueFromJson(jsonObject, "name", String.class);
            if (deviceName != null && !(deviceName.trim().isEmpty())) {
                filterBuilder.setDeviceName(deviceName);
            }
            String deviceAddress = getValueFromJson(jsonObject, "deviceId", String.class);
            if (deviceAddress != null && !(deviceAddress.trim().isEmpty())) {
                filterBuilder.setDeviceAddress(deviceAddress);
            }
            tempData = getValueFromJson(jsonObject, "serviceUuid", String.class);
            ParcelUuid serviceUuid = (tempData == null) ? null : ParcelUuid.fromString(tempData);
            tempData = getValueFromJson(jsonObject, "serviceUuidMask", String.class);
            ParcelUuid uuidMask = (tempData == null) ? null : ParcelUuid.fromString(tempData);
            if (serviceUuid != null || uuidMask != null) {
                filterBuilder.setServiceUuid(serviceUuid, uuidMask);
            }
            tempData = getValueFromJson(jsonObject, "serviceSolicitationUuid", String.class);
            ParcelUuid serviceSolicitationUuid = (tempData == null) ? null : ParcelUuid.fromString(tempData);
            tempData = getValueFromJson(jsonObject, "serviceSolicitationUuidMask", String.class);
            ParcelUuid serviceSolicitationUuidMask = (tempData == null) ? null : ParcelUuid.fromString(tempData);
            if (serviceSolicitationUuid != null || serviceSolicitationUuidMask != null) {
                Class<?> builderClass = Class.forName("android.bluetooth.le.ScanFilter$Builder");
                Method setServiceSolicitationUuid = builderClass.getMethod("setServiceSolicitationUuid",
                    ParcelUuid.class, ParcelUuid.class);
                setServiceSolicitationUuid.invoke(filterBuilder, serviceSolicitationUuid,
                    serviceSolicitationUuidMask);
            }
            int manufacturerId = getValueFromJson(jsonObject, "manufactureId", Integer.class);
            byte[] manufacturerData = convertJsonArrayToByteArray(jsonObject, "manufactureData");
            byte[] manufacturerDataMask = convertJsonArrayToByteArray(jsonObject, "manufactureDataMask");
            if (manufacturerId != 0 && (manufacturerData != null || manufacturerDataMask != null)) {
                filterBuilder.setManufacturerData(manufacturerId, manufacturerData, manufacturerDataMask);
            } else if (manufacturerId == 0 && (manufacturerData != null || manufacturerDataMask != null)) {
                return null;
            }
            byte[] serviceData = convertJsonArrayToByteArray(jsonObject, "serviceData");
            byte[] serviceDataMask = convertJsonArrayToByteArray(jsonObject, "serviceDataMask");
            if (serviceUuid != null || serviceData != null || serviceDataMask != null) {
                filterBuilder.setServiceData(serviceUuid, serviceData, serviceDataMask);
            }
            ScanFilter filter = filterBuilder.build();
            return filter;
        } catch (JSONException | NoSuchMethodException | IllegalAccessException | IllegalArgumentException |
            InvocationTargetException | SecurityException | ClassNotFoundException e) {
            ALog.e(LOG_TAG, "createScanFilters failed; err is " + e.getClass().getSimpleName() + e);
            return null;
        }
    }

    public static List<ScanFilter> getBluetoothScanFilters(String[] stringFilters) {
        List<ScanFilter> filters = new ArrayList<>();
        for (String stringFilter : stringFilters) {
            ScanFilter filter = getBluetoothScanFilter(stringFilter);
            if (filter == null) {
                return null;
            }
            filters.add(filter);
        }
        return filters;
    }

    public static ScanSettings getBluetoothLeScanSettings(String stringSettings) {
        try {
            JSONObject jsonObject = new JSONObject(stringSettings);
            long reportDelayMillis = getValueFromJson(jsonObject, "reportDelayMillis", Long.class);
            int scanMode = getValueFromJson(jsonObject, "scanMode", Integer.class);
            boolean legacy = getValueFromJson(jsonObject, "legacy", Boolean.class);
            int phy = getValueFromJson(jsonObject, "phy", Integer.class);
            int callbackType = getValueFromJson(jsonObject, "callbackType", Integer.class);
            if (reportDelayMillis < 0 || 
               (phy != BluetoothDevice.PHY_LE_1M && phy != ScanSettings.PHY_LE_ALL_SUPPORTED) ||
               (scanMode < ScanSettings.SCAN_MODE_OPPORTUNISTIC || scanMode > ScanSettings.SCAN_MODE_LOW_LATENCY) ||
               (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES &&
                callbackType != ScanSettings.CALLBACK_TYPE_FIRST_MATCH &&
                callbackType != ScanSettings.CALLBACK_TYPE_MATCH_LOST)) {
                return null;
            }

            ScanSettings settings = new ScanSettings.Builder()
                .setReportDelay(reportDelayMillis)
                .setScanMode(scanMode)
                .setLegacy(legacy)
                .setPhy(phy)
                .setCallbackType(callbackType)
                .build();
            return settings;
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "getBluetoothLeScanSettings failed; err is " + e);
            return null;
        }
    }

    public static String encapsulationBluetoothLeScanResults(ScanResult result) {
        try {
            String addr = result.getDevice().getAddress();
            if (addr == null || addr.isEmpty()) {
                addr = "";
            }
            int rssi = result.getRssi();
            boolean connectable = result.isConnectable();
            String name = result.getDevice().getName();
            if (name == null || name.isEmpty()) {
                name = "";
            }
            byte[] payload = result.getScanRecord().getBytes();
            JSONArray array = new JSONArray();
            for (int i = 0; i < payload.length; i++) {
                array.put(payload[i]);
            }

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("addr", addr);
            jsonObject.put("rssi", rssi);
            jsonObject.put("connectable", connectable);
            jsonObject.put("name", name);
            jsonObject.put("payload", array);
            return jsonObject.toString();
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "encapsulationBluetoothLeScanResults failed; err is " + e);
            return "";
        }
    }

    public static String convertVectorToJString(Vector<String> vector) {
        JSONArray jsonArray = new JSONArray(vector);
        return jsonArray.toString();
    }

    public static AdvertiseSettings getBluetoothAdvertiseSettings(String advertiseStrings) {
        try {
            JSONObject jsonObject = new JSONObject(advertiseStrings);
            boolean connectable = getValueFromJson(jsonObject, "connectable", Boolean.class);
            AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setConnectable(connectable)
                .build();
            return settings;
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "getBluetoothAdvertiseSettings failed; err is " + e);
            return null;
        }
    }

    public static AdvertiseData getBluetoothAdvertiseData(String advDataStrings) {
        try {
            AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
            JSONObject jsonObject = new JSONObject(advDataStrings);
            JSONArray serviceUuidArray = getValueFromJson(jsonObject, "serviceUuids", JSONArray.class);
            if (serviceUuidArray != null) {
                for (int i = 0; i < serviceUuidArray.length(); ++i) {
                    UUID uuid = UUID.fromString(serviceUuidArray.getString(i));
                    dataBuilder.addServiceUuid(new ParcelUuid(uuid));
                }
            }
            JSONArray manufactureDataArray = getValueFromJson(jsonObject, "manufactureData", JSONArray.class);
            if (manufactureDataArray != null) {
                for(int i = 0; i < manufactureDataArray.length(); ++i) {
                    int manufactureId = getValueFromJson(
                        manufactureDataArray.getJSONObject(i), "manufactureId", Integer.class);
                    byte[] manufactureValue = {};
                    if (manufactureDataArray.getJSONObject(i).has("manufactureValue")) {
                        manufactureValue = convertJsonArrayToByteArray(
                            manufactureDataArray.getJSONObject(i), "manufactureValue");
                    }
                    dataBuilder.addManufacturerData(manufactureId, manufactureValue);
                }
            }
            JSONArray ServiceDataArray = getValueFromJson(jsonObject, "serviceData", JSONArray.class);
            if (ServiceDataArray != null) {
                for(int i = 0; i < ServiceDataArray.length(); ++i) {
                    String serviceUuid = getValueFromJson(
                        ServiceDataArray.getJSONObject(i), "serviceUuid", String.class);
                    byte[] serviceValue = {};
                    if (ServiceDataArray.getJSONObject(i).has("serviceValue")) {
                        serviceValue = convertJsonArrayToByteArray(
                            ServiceDataArray.getJSONObject(i), "serviceValue");
                    }
                    UUID uuid = UUID.fromString(serviceUuid);
                    dataBuilder.addServiceData(new ParcelUuid(uuid), serviceValue);
                }
            }
            boolean includeDeviceName = getValueFromJson(jsonObject, "includeDeviceName", Boolean.class);
            dataBuilder.setIncludeDeviceName(includeDeviceName);
            return dataBuilder.build();
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "getBluetoothAdvertiseData failed, try-catch err is " + e);
            return null;
        }
    }

    public static boolean hasScanResponseData(String scanResponseStrings) {
        try {
            JSONObject jsonObject = new JSONObject(scanResponseStrings);
            JSONArray serviceUuidArray = getValueFromJson(jsonObject, "serviceUuids", JSONArray.class);
            JSONArray manufactureDataArray = getValueFromJson(jsonObject, "manufactureData", JSONArray.class);
            JSONArray ServiceDataArray = getValueFromJson(jsonObject, "serviceData", JSONArray.class);
            if (serviceUuidArray == null && manufactureDataArray == null && ServiceDataArray == null) {
                return false;
            }
            return true;
        } catch (JSONException e) {
            return false;
        }
    }

    public static BluetoothGattCharacteristic getBluetoothGattCharacteristic(String characteristicString,
        String serviceUuid, BluetoothGatt gatt) {
        try {
            JSONObject jsonObject = new JSONObject(characteristicString);
            String characteristicUuidString = getValueFromJson(jsonObject, "characteristicUuid", String.class);
            UUID characteristicUuid = UUID.fromString(characteristicUuidString);
            UUID uuid = UUID.fromString(serviceUuid);
            BluetoothGattService bluetoothGattService = gatt.getService(uuid);
            BluetoothGattCharacteristic bluetoothGattCharacteristic;
            return (bluetoothGattService == null ||
                   (bluetoothGattCharacteristic = bluetoothGattService.getCharacteristic(characteristicUuid)) == null)
                    ? null
                    : bluetoothGattCharacteristic;
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "getBluetoothGattCharacteristic failed, try-catch err is " + e);
            return null;
        }
    }

    public static byte[] getCharacteristicValue(String characteristicString) {
        try {
            byte[] characteristicValue = {};
            JSONObject jsonObject = new JSONObject(characteristicString);
            if (jsonObject.has("characteristicValue")) {
                characteristicValue = convertJsonArrayToByteArray(jsonObject, "characteristicValue");
            }
            return characteristicValue;
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "getCharacteristicValue failed, try-catch err is " + e);
            return null;
        }
    }

    public static String convertGattServiceToJSONString(BluetoothGattService service) {
        try {
            if (service == null) {
                ALog.e(LOG_TAG, "BluetoothGattService Object to be converted is null");
                return "";
            }
            JSONObject jsonObject = new JSONObject();
            String uuid = service.getUuid().toString();
            int type = service.getType();

            jsonObject.put("uuid", uuid);
            jsonObject.put("type", type);
            return jsonObject.toString();
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "Json data encapsulation error; error is : " + e);
            return "";
        }
    }

    public static String convertBluetoothDeviceToJString(BluetoothDevice device) {
        if (device == null) {
            ALog.e(LOG_TAG, "Device Object to be converted is null");
            return "";
        }
        try {
            String addr = device.getAddress();
            int transport = BluetoothDevice.TRANSPORT_LE;
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("addr", addr);
            jsonObject.put("transport", transport);
            return jsonObject.toString();
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "Json data encapsulation error; error is : " + e);
            return "";
        }
    }

    public static String convertCharacteristicToJSONString(BluetoothGattCharacteristic characteristic) {
        try {
            if (characteristic == null) {
                ALog.e(LOG_TAG, "Characteristic Object to be converted is null");
                return "";
            }
            JSONObject jsonObject = new JSONObject();
            String uuid = characteristic.getUuid().toString();
            int properties = characteristic.getProperties();
            int permissions = characteristic.getPermissions();
            byte[] value = {};
            int length = INVALID_LENGTH;
            if (characteristic.getValue() != null) {
                value = characteristic.getValue();
                length = value.length;
            }
            JSONArray array = new JSONArray();
            for (int i = 0; i < value.length; i++) {
                array.put(value[i]);
            }

            jsonObject.put("uuid", uuid);
            BluetoothGattService service = characteristic.getService();
            if (service != null) {
                jsonObject.put("serviceUuid", service.getUuid().toString());
            }
            jsonObject.put("properties", properties);
            jsonObject.put("permissions", permissions);
            jsonObject.put("value", array);
            jsonObject.put("length", length);
            return jsonObject.toString();
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "Json data encapsulation error; error is : " + e);
            return "";
        }
    }

    public static String convertDescriptorToJSONString(BluetoothGattDescriptor descriptor) {
        try {
            if (descriptor == null) {
                ALog.e(LOG_TAG, "descriptor Object to be converted is null.");
                return "";
            }
            JSONObject jsonObject = new JSONObject();
            UUID uuid = descriptor.getUuid();
            int permissions = descriptor.getPermissions();
            byte[] value = {};
            int length = INVALID_LENGTH;
            if (descriptor.getValue() != null) {
                value = descriptor.getValue();
                length = value.length;
            } else {
                ALog.e(LOG_TAG, "descriptor value is null.");
            }
            JSONArray array = new JSONArray();
            for (int i = 0; i < value.length; i++) {
                array.put(value[i]);
            }

            jsonObject.put("uuid", uuid.toString());
            BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
            if (characteristic != null) {
                jsonObject.put("characteristicUuid", characteristic.getUuid().toString());
                BluetoothGattService service = characteristic.getService();
                if (service != null) {
                    jsonObject.put("serviceUuid", service.getUuid().toString());
                }
            }
            jsonObject.put("permissions", permissions);
            jsonObject.put("value", array);
            jsonObject.put("length", length);
            return jsonObject.toString();
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "Json data encapsulation error; error is : " + e);
            return "";
        }
    }

    public static String convertCharacteristicToJString(BluetoothGattCharacteristic characteristic, int handle) {
        try {
            if (characteristic == null) {
                ALog.e(LOG_TAG, "Characteristic Object to be converted is null");
                return "";
            }
            JSONObject jsonObject = new JSONObject();
            String uuid = characteristic.getUuid().toString();
            byte[] value = {};
            int length = INVALID_LENGTH;
            if (characteristic.getValue() != null) {
                value = characteristic.getValue();
                length = value.length;
            }
            String valueString = new String(value, StandardCharsets.UTF_8);
            jsonObject.put("uuid", uuid);
            jsonObject.put("handle", handle);
            jsonObject.put("value", valueString);
            jsonObject.put("length", length);
            return jsonObject.toString();
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "Json data encapsulation error; error is : " + e);
            return "";
        }
    }

    public static String convertDescriptorToJSONString(BluetoothGattDescriptor descriptor, int handle) {
        return convertDescriptorToJSONString(descriptor, handle, null);
    }

    /**
     * Converts a BluetoothGattDescriptor to a JSON string.
     *
     * @param descriptor The BluetoothGattDescriptor to convert.
     * @param handle     The attribute handle for the descriptor.
     * @param rawValue   The raw byte value of the descriptor, or null to use the
     *                   descriptor's current value.
     * @return A JSON string representation of the descriptor.
     */
    public static String convertDescriptorToJSONString(BluetoothGattDescriptor descriptor, int handle,
            byte[] rawValue) {
        try {
            if (descriptor == null) {
                ALog.e(LOG_TAG, "Descriptor Object to be converted is null");
                return "";
            }
            JSONObject jsonObject = new JSONObject();
            String uuid = descriptor.getUuid().toString();
            int permissions = descriptor.getPermissions();
            byte[] value = {};
            int length = INVALID_LENGTH;
            if (rawValue != null) {
                value = rawValue;
                length = value.length;
            } else if (descriptor.getValue() != null) {
                value = descriptor.getValue();
                length = value.length;
            }
            String valueString = new String(value, StandardCharsets.UTF_8);
            jsonObject.put("uuid", uuid);
            jsonObject.put("handle", handle);
            jsonObject.put("permissions", permissions);
            jsonObject.put("value", valueString);
            jsonObject.put("length", length);
            return jsonObject.toString();
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "convert descriptor To JSONString failed; err is " + e);
            return "";
        }
    }

    public static int addServiceToCustomGattServer(String jsonString, CustomGattServer structure) {
        try {
            JSONObject jsonObject = new JSONObject(jsonString);
            String serviceUuid = getValueFromJson(jsonObject, "serviceUuid", String.class);
            boolean isPrimary = getValueFromJson(jsonObject, "isPrimary", Boolean.class);
            int handle = getValueFromJson(jsonObject, "handle", Integer.class);
            int endHandle = getValueFromJson(jsonObject, "endHandle", Integer.class);
            JSONArray characterArray = getValueFromJson(jsonObject, "characteristics", JSONArray.class);
            UUID uuid = UUID.fromString(serviceUuid);
            if (!checkServiceJsonIsLegality(structure, serviceUuid, characterArray)) {
                ALog.e(LOG_TAG, "addServiceToCustomGattServer failed; service Json is not legally");
                return BluetoothErrorCode.BT_ERR_INTERNAL_ERROR.getId();
            }
            BluetoothGattService service = new BluetoothGattService(uuid,
                (isPrimary ? BluetoothGattService.SERVICE_TYPE_PRIMARY : BluetoothGattService.SERVICE_TYPE_SECONDARY));
            int errCode = BluetoothErrorCode.BT_NO_ERROR.getId();
            if (service == null) {
                errCode = BluetoothErrorCode.BT_ERR_INTERNAL_ERROR.getId();
            } else {
                HashMap<Integer, BluetoothGattCharacteristic> characteristicMap =
                getCharacteristicMap(characterArray, structure);
                if (characteristicMap == null) {
                    errCode = BluetoothErrorCode.BT_ERR_INTERNAL_ERROR.getId();
                } else {
                    for (HashMap.Entry<Integer, BluetoothGattCharacteristic> entry : characteristicMap.entrySet()) {
                        Integer characteristicHandle = entry.getKey();
                        BluetoothGattCharacteristic characteristic = entry.getValue();
                        boolean success = service.addCharacteristic(characteristic);
                        if (success) {
                            structure.addBluetoothGattCharacter(characteristicHandle, characteristic);
                        }
                    }
                    structure.addBluetoothGattService(handle, service);
                    structure.addBluetoothHandleInfo(handle, endHandle);
                }
            }
            return errCode;
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "addServiceToCustomGattServer failed; err is " + e);
            return BluetoothErrorCode.BT_ERR_INTERNAL_ERROR.getId();
        }
    }

    public static boolean checkServiceJsonIsLegality(CustomGattServer structure, String serviceUuid,
        JSONArray characterArray) {
        BluetoothGattServer bluetoothGattServer = structure.getBluetoothGattServer();
        if (bluetoothGattServer == null || structure.serviceIsExist(serviceUuid) ||
            !checkCharacterJsonIsLegality(characterArray)) {
            ALog.e(LOG_TAG, "checkServiceJsonIsLegality failed, bluetoothGattServer is null or service is exist");
            return false;
        }
        return true;
    }

    public static boolean checkCharacterJsonIsLegality(JSONArray characterArray) {
        if (characterArray == null) {
            return true;
        }
        boolean res = true;
        try {
            for (int i = 0; i < characterArray.length(); ++i) {
                if (!characterArray.getJSONObject(i).has("characteristicValue")) {
                    res = false;
                    break;
                }
                int properties = getValueFromJson(characterArray.getJSONObject(i), "properties", Integer.class);
                int permissions = convertPermission(
                    getValueFromJson(characterArray.getJSONObject(i), "permissions", Integer.class));
                JSONArray descriptorArray =
                    getValueFromJson(characterArray.getJSONObject(i), "descriptors", JSONArray.class);
                if (properties > MAX_PROPERTIE || properties < MIN_PROPERTIE || permissions > MAX_PERMISSIONS ||
                    permissions < MIN_PERMISSIONS) {
                    res = false;
                    break;
                }
                if (((properties & CHARACTERISTIC_PROPERTIE_READ) != 0 &&
                     (permissions & ANDROID_PERMISSION_READ) == 0) ||
                    ((properties & CHARACTERISTIC_PROPERTIE_WRITE) != 0 &&
                     (permissions & ANDROID_PERMISSION_WRITE) == 0)) {
                    res = false;
                    break;
                } else if (!checkDescriptorJsonIsLegality(properties, descriptorArray)) {
                    res = false;
                    break;
                }
            }
            return res;
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "checkCharacterJsonIsLegality error; error is : " + e);
            return false;
        }
    }

    public static boolean checkDescriptorJsonIsLegality(int properties, JSONArray descriptorArray) {
        if (descriptorArray == null) {
            return true;
        }
        boolean res = true;
        try {
            for (int i = 0; i < descriptorArray.length(); ++i) {
                if (!descriptorArray.getJSONObject(i).has("descriptorValue")) {
                    res = false;
                    break;
                }
                List<String> descriptorUuidList = getDescriptorUuidList(descriptorArray);
                int permissions = convertPermission(
                    getValueFromJson(descriptorArray.getJSONObject(i), "permissions", Integer.class));
                if (permissions > MAX_PERMISSIONS || permissions < MIN_PERMISSIONS) {
                    res = false;
                    break;
                }
                if ((properties & CHARACTERISTIC_PROPERTIE_BROADCAST) != 0 &&
                    Collections.frequency(descriptorUuidList, UUID_SERVER_CHARACTERISTIC_CONFIGURATION) != 1) {
                    res = false;
                    break;
                }
                if (((properties & CHARACTERISTIC_PROPERTIE_NOTIFY) != 0 ||
                    (properties & CHARACTERISTIC_PROPERTIE_INDICATE) != 0) &&
                    Collections.frequency(descriptorUuidList, UUID_CLIENT_CHARACTERISTIC_CONFIGURATION) != 1) {
                    res = false;
                    break;
                }
                if ((properties & CHARACTERISTIC_PROPERTIE_EXTENDED_PROPERTIES) != 0 &&
                    Collections.frequency(descriptorUuidList, UUID_CHARACTERISTIC_EXTENDED_PROPERTIES) != 1) {
                    res = false;
                    break;
                }
                if (Collections.frequency(descriptorUuidList, UUID_CHARACTERISTIC_USER_DESCRIPTION) > 1) {
                    res = false;
                    break;
                }
                if ((Collections.frequency(descriptorUuidList, UUID_CHARACTERISTIC_FROMAT) > 1) &&
                    (Collections.frequency(descriptorUuidList, UUID_CHARACTERISTIC_AGGREGATE_FROMAT) != 1)) {
                    res = false;
                    break;
                }
            }
            return res;
        } catch (JSONException | NullPointerException e) {
            ALog.e(LOG_TAG, "checkDescriptorJsonIsLegality error; error is : " + e);
            return false;
        }
    }

    public static int convertPermission(int permission) {
        int res = 0;
        if ((permission & PERMISSION_READ) != 0) {
            res |= ANDROID_PERMISSION_READ;
        }
        if ((permission & PERMISSION_WRITE) != 0) {
            res |= ANDROID_PERMISSION_WRITE;
        }
        return res;
    }

    public static HashMap<Integer, BluetoothGattCharacteristic> getCharacteristicMap(
        JSONArray characterArray, CustomGattServer structure) {
        HashMap<Integer, BluetoothGattCharacteristic> characterMap = new HashMap<>();
        if (characterArray == null || characterMap == null) {
            return characterMap;
        }
        try {
            for (int i = 0; i < characterArray.length(); ++i) {
                String characterUuidString = getValueFromJson(
                    characterArray.getJSONObject(i), "characteristicUuid", String.class);
                byte[] characteristicValue = {};
                if (characterArray.getJSONObject(i).has("characteristicValue")) {
                    characteristicValue = convertJsonArrayToByteArray(
                        characterArray.getJSONObject(i), "characteristicValue");
                }
                int characteristicHandle = getValueFromJson(
                    characterArray.getJSONObject(i), "characterHandle", Integer.class);
                int characteristicPermissions = getValueFromJson(
                    characterArray.getJSONObject(i), "permissions", Integer.class);
                JSONArray descriptorArray = getValueFromJson(
                    characterArray.getJSONObject(i), "descriptors", JSONArray.class);
                int properties = getValueFromJson(characterArray.getJSONObject(i), "properties", Integer.class);
                UUID characterUuid = UUID.fromString(characterUuidString);
                BluetoothGattCharacteristic characteristic =
                    new BluetoothGattCharacteristic(characterUuid, properties, characteristicPermissions);
                if (characteristic == null) {
                    continue;
                }
                characteristic.setValue(characteristicValue);
                HashMap<Integer, BluetoothGattDescriptor> descriptorMap;
                if (descriptorArray != null && (descriptorMap = getDescriptorMap(descriptorArray)) != null) {
                    for (HashMap.Entry<Integer, BluetoothGattDescriptor> entry : descriptorMap.entrySet()) {
                        Integer desHandle = entry.getKey();
                        BluetoothGattDescriptor descriptor = entry.getValue();
                        boolean success = characteristic.addDescriptor(descriptor);
                        if (success) {
                            structure.addBluetoothGattDescriptor(desHandle, descriptor);
                        }
                    }
                }
                characterMap.put(characteristicHandle, characteristic);
            }
            return characterMap;
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "getCharacteristicMap failed; err is " + e);
            return new HashMap<Integer, BluetoothGattCharacteristic>();
        }
    }

    public static HashMap<Integer, BluetoothGattDescriptor> getDescriptorMap(JSONArray descriptorArray) {
        HashMap<Integer, BluetoothGattDescriptor> descriptorMap = new HashMap<>();
        if (descriptorArray == null || descriptorMap == null) {
            return descriptorMap;
        }
        try {
            for (int index = 0; index < descriptorArray.length(); ++index) {
                String descriptorUuidString = getValueFromJson(
                    descriptorArray.getJSONObject(index), "descriptorUuid", String.class);
                byte[] descriptorValue = {};
                if (descriptorArray.getJSONObject(index).has("descriptorValue")) {
                    descriptorValue = convertJsonArrayToByteArray(
                        descriptorArray.getJSONObject(index), "descriptorValue");
                }
                int descriptorHandle = getValueFromJson(
                    descriptorArray.getJSONObject(index), "desHandle", Integer.class);
                int descriptorPermissions = getValueFromJson(
                    descriptorArray.getJSONObject(index), "permissions", Integer.class);
                UUID descriptorUuid = UUID.fromString(descriptorUuidString);
                BluetoothGattDescriptor descriptor =
                    new BluetoothGattDescriptor(descriptorUuid, descriptorPermissions);
                if (descriptor == null) {
                    continue;
                }
                descriptor.setValue(descriptorValue);
                descriptorMap.put(descriptorHandle, descriptor);
            }
            return descriptorMap;
        } catch (JSONException | IllegalArgumentException e) {
            ALog.e(LOG_TAG, "getDescriptorMap failed; err is " + e);
            return new HashMap<Integer, BluetoothGattDescriptor>();
        }
    }

    public static List<String> getDescriptorUuidList(JSONArray descriptorArray) {
        List<String> descriptorUuidList = new ArrayList<>();
        if (descriptorArray == null || descriptorUuidList == null) {
            return descriptorUuidList;
        }
        try {
            for (int index = 0; index < descriptorArray.length(); ++index) {
                String descriptorUuidString = getValueFromJson(
                    descriptorArray.getJSONObject(index), "descriptorUuid", String.class);
                descriptorUuidList.add(descriptorUuidString);
            }
            return descriptorUuidList;
        } catch (JSONException e) {
            ALog.e(LOG_TAG, "getDescriptorUuidList failed; err is " + e);
            return new ArrayList<String>();
        }
    }

}