function BLEDevice(arg) {
  this.deviceId = "";
  this.serviceId = "";
  this.debug = arg ? (arg['debug'] || false) : false;
  this.connectTimeout = arg ? (arg['connectTimeout'] || 10000) : 10000;
  this.onBLECharacteristicValueChange = {}
};

/**
 * 日志输出
 * 
 * @param {msg} String
 *   输出的信息
 * @param {Array} args
 *   辅助输出信息
 */
BLEDevice.prototype.log = function (msg, ...args) {
  if (this.debug) {
    console.log(msg, ...args)
  }
};

BLEDevice.prototype.setStateCallBack = function (cb) {
  wx.onBluetoothAdapterStateChange((res) => {
    cb(res);
  });
}

/**
 * 初始化蓝牙模块
 * 
 * @return
 *   返回蓝牙模块Promise
 */
BLEDevice.prototype.openBluetoothAdapter = function() {
  return new Promise((resolve, reject) => {
    wx.openBluetoothAdapter({
      success: (res) => {
        this.log("openBluetoothAdapter", res);
        resolve(res);
      },
      fail: (res) => {
        this.log("openBluetoothAdapter failed", res)
        reject(res);
      }
    })
  })
};

/**
 * 关闭蓝牙模块
 */
BLEDevice.prototype.closeBluetoothAdapter = function() {
  wx.closeBluetoothAdapter();
};

/**
 * 获取本机蓝牙适配器状态
 */
BLEDevice.prototype.getBluetoothAdapterState = function () {
  wx.getBluetoothAdapterState({
    success: (res) => {
      if (res.discovering) {
        this.onBluetoothDeviceFound()
      } else if (res.available) {
		setTimeout(()=>{
			this.startBluetoothDevicesDiscovery(state.servicesId, state.key);
		},500)
      } 
    }
  })
};

/**
 * 开始搜寻附近的蓝牙外围设备
 * 
 * @param {String} servicesId
 *   要搜索的蓝牙设备主服务的uuid列表
 * @param {Function} cb
 *   发现设备后的回调
 *   若发现成功，参数success为true，foundDevices：发现的设备列表
 *   若发现失败，参数success为false
 * @return
 *   返回搜索蓝牙设备Promise
 */
BLEDevice.prototype.startBluetoothDevicesDiscovery = function (servicesId, cb) {
  let foundDevices = [];
  wx.onBluetoothDeviceFound((res) => {
    res.devices.forEach(device => {
      if (!device.name && !device.localName) {
        return;
      }

      const idx = inArray(foundDevices, 'deviceId', device.deviceId)

      if (idx === -1) {
        foundDevices.push(device)
      } else {
        foundDevices[idx] = device
      }
      this.log("onBluetoothDeviceFound", foundDevices);

      cb({ success: true, foundDevices: foundDevices});
    })
  })

  wx.startBluetoothDevicesDiscovery({
    services: servicesId,
    allowDuplicatesKey: true,
    success: (res) => {
      this.log("startBluetoothDevicesDiscovery", res);
    },
    fail: (res) => {
      cb({ success: false });
    }
  })
};

/**
 * 停止搜寻附近的蓝牙外围设备
 */
BLEDevice.prototype.stopBluetoothDevicesDiscovery = function () { 
  wx.stopBluetoothDevicesDiscovery();
};

/**
 * 连接低功耗蓝牙设备
 * 
 * @param {String} deviceId
 *   连接设备的ID
 * 
 * @return
 *   返回设备连接的Promise
 */
BLEDevice.prototype.createBLEConnection = function (deviceId) {
  this.deviceId = deviceId;
  return new Promise((resolve, reject) => {
    wx.createBLEConnection({
      deviceId: this.deviceId,
      success: (res) => {
        this.log("createBLEConnection", res)
        this.getBLEDeviceServices()
          .then(() => {
            resolve();
          }).catch((res) => {
            reject(res);
            this.closeBLEConnection();
          })
      },
      fail: (res) => {
        this.log("createBLEConnection failed", res)
        reject(res)
      }
    })
    this.stopBluetoothDevicesDiscovery();

    setTimeout(() => {
      reject('Connect timeout');
      this.closeBLEConnection();
    }, this.connectTimeout);
  })
};

/**
 * 断开与低功耗蓝牙设备的连接
 */
BLEDevice.prototype.closeBLEConnection = function () {
  wx.closeBLEConnection({
    deviceId: this.deviceId,
  })
};

/**
 * 获取蓝牙设备的所有服务
 * 
 * @param {String} findServicesUUID
 *   要查找的主服务uuid
 * 
 * @param {Number} findServicesIndex
 *   开始查找的位置
 * 
 * @return
 *   返回获取服务的Promise
 */
BLEDevice.prototype.getBLEDeviceServices = function () {
  return new Promise((resolve, reject) => {
    wx.getBLEDeviceServices({
      deviceId: this.deviceId,
      success: (res) => {
        let total = res.services.length
        for (let i = 0; i < res.services.length; i++) {
          this.log("getBLEDeviceServices",  res.services[i]);
          this.getBLEDeviceCharacteristics(res.services[i].uuid)
            .then(() => {
              total --;
              if (total === 0) {
                resolve();
              }
            })
            .catch(res => {
              reject(res);
            })
        }
      },
      fail: (res) => {
        reject(res);
      }
    })
  })
};

/**
 * 获取蓝牙设备某个服务中所有特征值
 * 
 * @param {String} serviceId
 *   要查找特征的service uuid
 * 
 * @return
 *   返回服务特征值的Promise
 */
BLEDevice.prototype.getBLEDeviceCharacteristics = function (serviceId) { 

  return new Promise((resolve, reject) => {
    wx.getBLEDeviceCharacteristics({
      deviceId: this.deviceId,
      serviceId: serviceId,
      success: (res) => {
        resolve(res);

        for (let i = 0; i < res.characteristics.length; i++) {
          this.log("getBLEDeviceCharacteristics",  i, res.characteristics[i]);
          let item = res.characteristics[i];
          if (item.properties.read) {
            this.log("可读：", res.characteristics[i]);
          }
          if (item.properties.write) {
            this.log("可写：", res.characteristics[i]);
          }
          if (item.properties.notify || item.properties.indicate) {
            this.log("可订阅", res.characteristics[i]);
          }
        }
      },
      fail: (res) => {
        reject(res);
      },
      complete: (res) => {
      }
    })
  })
};

/**
 * 读取低功耗蓝牙设备的特征值的二进制数据值
 * 
 * @param {String} serviceId
 *   蓝牙可订阅服务的uuid
 *
 * @param {String} readCharacteristicId
 *   蓝牙可读特征值的uuid
 *
 * @param {Function} callback
 *   特征变化的回调函数
 */
BLEDevice.prototype.readBLECharacteristicValue = function (serviceId, readCharacteristicId, callback) { 
  wx.onBLECharacteristicValueChange((characteristic) => {
    this.log("onBLECharacteristicValueChange", characteristic.value)
    let cb = this.onBLECharacteristicValueChange[readCharacteristicId]
    if (cb) {
      cb(characteristic)
    }
  })

  return new Promise((resolve, reject) => {
    wx.readBLECharacteristicValue({
      deviceId: this.deviceId,
      serviceId: serviceId,
      characteristicId: readCharacteristicId,
      success: (res) => {
        resolve(res)
        this.log('readBLECharacteristicValue', res);
        this.onBLECharacteristicValueChange[readCharacteristicId] = callback;
      },
      fail: (res) => {
        reject(res)
      }
    })
  })
};

/**
 * 向低功耗蓝牙设备特征值中写入二进制数据
 * 
 * @param {String} writeCharacteristicId
 *   蓝牙可写特征值的uuid
 * 
 *  @param {ArrayBuffer} buffer
 *   特征值对应的二进制值
 */
BLEDevice.prototype.writeBLECharacteristicValue = function (serviceId, writeCharacteristicId, buffer) { 
  // // 向蓝牙设备发送一个0x00的16进制数据
  this.log("writeBLECharacteristicValue buffer", buffer);
  wx.writeBLECharacteristicValue({
    deviceId: this.deviceId,
    serviceId: serviceId,
    characteristicId: writeCharacteristicId,
    value: buffer,
    success: (res) => {
      this.log('writeBLECharacteristicValue', res);
    }
  })
};

/**
 * 启用低功耗蓝牙设备特征值变化时的notify功能，订阅特征值
 * 
 * @param {String} serviceId
 *   蓝牙可订阅服务的uuid
 * 
 * @param {String} notifyCharacteristicId
 *   蓝牙可订阅特征值的uuid
 * 
 * @param {Function} callback
 *   特征变化的回调函数
 */
BLEDevice.prototype.notifyBLECharacteristicValueChange = function (serviceId, notifyCharacteristicId, callback) {
  
  wx.onBLECharacteristicValueChange((characteristic) => {
    this.log("onBLECharacteristicValueChange", characteristic.value)
    let cb = this.onBLECharacteristicValueChange[notifyCharacteristicId]
    if (cb) {
      cb(characteristic)
    }
  })
  return new Promise((reslove, reject) => {
    wx.notifyBLECharacteristicValueChange({
      deviceId: this.deviceId,
      serviceId: serviceId,
      characteristicId: notifyCharacteristicId,
      state: true,
      success: (res) => {
        reslove(res)
        this.log('notifyBLECharacteristicValueChange', res);
        this.onBLECharacteristicValueChange[notifyCharacteristicId] = callback
      },
      fail: (res) => {
        reject(res)
      }
    })
  })
};

/**
 * 关闭低功耗蓝牙设备特征值变化时的notify功能，订阅特征值
 * 
 * @param {String} serviceId
 *   蓝牙可订阅服务的uuid
 * 
 * @param {String} notifyCharacteristicId
 *   蓝牙可订阅特征值的uuid
 */
BLEDevice.prototype.unNotifyBLECharacteristicValueChange = function (serviceId, notifyCharacteristicId) {
  return new Promise((resolve, reject) => {
    delete this.onBLECharacteristicValueChange[notifyCharacteristicId]

    wx.notifyBLECharacteristicValueChange({
      deviceId: this.deviceId,
      serviceId: serviceId,
      characteristicId: notifyCharacteristicId,
      state: false,
      success: function (res) { 
        resolve(res)
      },
      fail: function (res) {
        reject(res)
      }
    })
  })
}


const inArray = (arr, key, val) => {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i][key] === val) {
      return i;
    }
  }
  return -1;
};

/**
 * ArrayBuffer转16进度字符串示例
 */
const ab2hex = (buffer) => {
  var hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function (bit) {
      return ('00' + bit.toString(16)).slice(-2);
    }
  )
  return hexArr.join('');
};

module.exports = BLEDevice