const logEnable = false
let isAndroid = false
let ecBluetoothAdapterStateChangeCallback = () => {}
let ecBluetoothDeviceFoundCallback = () => {}
let ecBLEConnectionStateChangeCallback = () => {}
let ecBLECharacteristicValueChangeCallback = () => {}
let ecDeviceId = ''
let ecGattServerUUID = ''
const ecGattServerUUIDOption1 = '0000FFF0-0000-1000-8000-00805F9B34FB'
const ecGattServerUUIDOption2 = 'FFF0'
let ecGattCharacteristicWriteUUID = ''
const ecGattCharacteristicWriteUUIDOption1 = '0000FFF2-0000-1000-8000-00805F9B34FB'
const ecGattCharacteristicWriteUUIDOption2 = 'FFF2'

const log = data => {
	if (logEnable) {
		console.log('[eciot]:' + JSON.stringify(data))
	}
}

const onBluetoothAdapterStateChange = cb => {
	ecBluetoothAdapterStateChangeCallback = cb
}

const _openBluetoothAdapter = () => {
	return new Promise(function(resolve, reject) {
		uni.openBluetoothAdapter({
			success(res) {
				log(res)
				resolve({
					ok: true,
					errCode: 0,
					errMsg: ''
				})
			},
			fail(res) {
				log(res)
				// {"errMsg":"openBluetoothAdapter:fail not available","code":10001}
				resolve({
					ok: false,
					errCode: res.code,
					errMsg: res.errMsg,
				})
			},
		})
	})
}

uni.onBluetoothAdapterStateChange(res => {
	log(res)
	// {"discovering":true,"available":true}
	if (!res.available) {
		ecBluetoothAdapterStateChangeCallback({
			ok: false,
			errCode: 30005,
			errMsg: '蓝牙适配器不可用',
		})
	}
})

const openBluetoothAdapter = async () => {
	await _openBluetoothAdapter()
	const systemInfo = uni.getSystemInfoSync()
	log(systemInfo)
	if (systemInfo.platform.toLowerCase() === 'android') {
		isAndroid = true
	}
	const systemSetting = uni.getSystemSetting()
	log(systemSetting)
	const appAuthorizeSetting = uni.getAppAuthorizeSetting()
	log(appAuthorizeSetting) 
	if (!systemSetting.bluetoothEnabled) {
	    ecBluetoothAdapterStateChangeCallback({
	        ok: false,
	        errCode: 30001,
	        errMsg: '请打开系统蓝牙开关',
	    })
	    return
	}
	if (isAndroid && !systemSetting.locationEnabled) {
	    ecBluetoothAdapterStateChangeCallback({
	        ok: false,
	        errCode: 30002,
	        errMsg: '请打开系统定位开关',
	    })
	    return
	}	
	if (isAndroid && (appAuthorizeSetting.locationAuthorized!=='authorized')) {
	    ecBluetoothAdapterStateChangeCallback({
	        ok: false,
	        errCode: 30003,
	        errMsg: '请打开应用定位权限，允许应用使用您的位置信息', 
	    })
	    return
	}
	const openRes = await _openBluetoothAdapter()
	ecBluetoothAdapterStateChangeCallback(openRes)
}

uni.onBluetoothDeviceFound(res => {
	// log(res)
	const device = res.devices[0]
	const name = device.name ? device.name : device.localName
	if (!name) {
		return
	} 
	let id = device.deviceId
	let rssi = device.RSSI
	ecBluetoothDeviceFoundCallback({
		id,
		name,
		rssi
	})
})

const onBluetoothDeviceFound = cb => {
	ecBluetoothDeviceFoundCallback = cb
}

const startBluetoothDevicesDiscovery = () => {
	uni.startBluetoothDevicesDiscovery({
		//services: [ecServerId],
		allowDuplicatesKey: true,
		// powerLevel: 'high',
		complete(res) {
			log(res)
		},
	})
}

const stopBluetoothDevicesDiscovery = () => {
	uni.stopBluetoothDevicesDiscovery({
		complete(res) {
			log(res)
		},
	})
}

const onBLEConnectionStateChange = cb => {
	ecBLEConnectionStateChangeCallback = cb
}

const _createBLEConnection = () => {
	return new Promise(function(resolve, reject) {
		uni.createBLEConnection({
			deviceId: ecDeviceId,
			success(res) {
				log(res)
				resolve({
					ok: true,
					errCode: 0,
					errMsg: ''
				})
			},
			fail(res) {
				log(res)
				resolve({
					ok: false,
					errCode: res.code,
					errMsg: res.errMsg,
				})
			},
		})
	})
}

const getBLEDeviceServices = () => {
	return new Promise(function(resolve, reject) {
		setTimeout(()=>{
			uni.getBLEDeviceServices({
				deviceId: ecDeviceId,
				success(res) {
					log(res)
					resolve({
						ok: true,
						errCode: 0,
						errMsg: '',
						services: res.services,
					})
				},
				fail(res) {
					log(res)
					resolve({
						ok: false,
						errCode: res.code,
						errMsg: res.errMsg
					})
				},
			})
		},800)
		
	})
}

const getBLEDeviceCharacteristics = serviceId => {
	return new Promise(function(resolve, reject) {
		uni.getBLEDeviceCharacteristics({
			deviceId: ecDeviceId,
			serviceId,
			success(res) {
				log(res)
				resolve({
					ok: true,
					errCode: 0,
					errMsg: '',
					characteristics: res.characteristics,
				})
			},
			fail(res) {
				log(res)
				resolve({
					ok: false,
					errCode: res.code,
					errMsg: res.errMsg
				})
			},
		})
	})
}

const notifyBLECharacteristicValueChange = (serviceId, characteristicId) => {
	return new Promise(function(resolve, reject) {
		uni.notifyBLECharacteristicValueChange({
			state: true,
			deviceId: ecDeviceId,
			serviceId,
			characteristicId,
			success(res) {
				log(res)
				resolve({
					ok: true,
					errCode: 0,
					errMsg: ''
				})
			},
			fail(res) {
				log(res)
				resolve({
					ok: false,
					errCode: res.code,
					errMsg: res.errMsg
				})
			},
		})
	})
}

const setBLEMTU = mtu => {
	return new Promise(function(resolve, reject) {
		setTimeout(()=>{
			uni.setBLEMTU({
				deviceId: ecDeviceId,
				mtu,
				success(res) {
					log(res)
					resolve({
						ok: true,
						errCode: 0,
						errMsg: ''
					})
				},
				fail(res) {
					log(res)
					resolve({
						ok: false,
						errCode: res.code,
						errMsg: res.errMsg
					})
				},
			})
		},500)
	})
}

uni.onBLEConnectionStateChange(async res => {
	log(res)
	// {"deviceId":"EC:22:05:13:78:49","connected":true}
	if (res.connected) {
		const servicesResult = await getBLEDeviceServices()
		if (!servicesResult.ok) {
			ecBLEConnectionStateChangeCallback(servicesResult)
			closeBLEConnection()
			return
		}
		for (const service of servicesResult.services) {
			if ((service.uuid.toUpperCase() === ecGattServerUUIDOption1) ||
				(service.uuid.toUpperCase() === ecGattServerUUIDOption2)) {
				ecGattServerUUID = service.uuid
			}
			const characteristicsResult = await getBLEDeviceCharacteristics(
				service.uuid
			)
			if (!characteristicsResult.ok) {
				ecBLEConnectionStateChangeCallback(characteristicsResult)
				closeBLEConnection()
				return
			}
			for (const characteristic of characteristicsResult.characteristics) {
				if (
					characteristic.properties &&
					characteristic.properties.notify
				) {
					const notifyResult =
						await notifyBLECharacteristicValueChange(
							service.uuid,
							characteristic.uuid
						)
					if (!notifyResult.ok) {
						ecBLEConnectionStateChangeCallback({
							ok: false,
							errCode: 30000,
							errMsg: 'notify error',
						})
						closeBLEConnection()
						return
					}
				}

				if ((characteristic.uuid.toUpperCase() ===
						ecGattCharacteristicWriteUUIDOption1) ||
					(characteristic.uuid.toUpperCase() ===
						ecGattCharacteristicWriteUUIDOption2)) {
					ecGattCharacteristicWriteUUID = characteristic.uuid
				}
			}
		}
		if (isAndroid) {
			await setBLEMTU(247)
		}
		ecBLEConnectionStateChangeCallback({
			ok: true,
			errCode: 0,
			errMsg: '',
		})
	} else {
		ecBLEConnectionStateChangeCallback({
			ok: false,
			errCode: 0,
			errMsg: 'disconnect',
		})
	}
})
	
const createBLEConnection = async id => {
	ecDeviceId = id
	const res = await _createBLEConnection()
	if (!res.ok) {
		ecBLEConnectionStateChangeCallback(res)
	}
}

const closeBLEConnection = () => {
	uni.closeBLEConnection({
		deviceId: ecDeviceId,
		complete(res) {
			log(res)
		},
	})
}

uni.onBLECharacteristicValueChange(res => {
	log(res)
	let x = new Uint8Array(res.value)
	log(x)
	let str = utf8BytesToStr(x)
	let strHex = ''
	for (let i = 0; i < x.length; i++) {
		strHex = strHex + x[i].toString(16).padStart(2, '0').toUpperCase()
	}
	log(str)
	log(strHex)
	ecBLECharacteristicValueChangeCallback(str, strHex)
})
	
const onBLECharacteristicValueChange = cb => {
	ecBLECharacteristicValueChangeCallback = cb
}

const _writeBLECharacteristicValue = buffer => {
	return new Promise(function(resolve, reject) {
		uni.writeBLECharacteristicValue({
			deviceId: ecDeviceId,
			serviceId: ecGattServerUUID,
			characteristicId: ecGattCharacteristicWriteUUID,
			value: buffer,
			writeType: 'writeNoResponse',
			success(res) {
				log(res)
				resolve({
					ok: true,
					errCode: 0,
					errMsg: ''
				})
			},
			fail(res) {
				log(res)
				resolve({
					ok: false,
					errCode: res.code,
					errMsg: res.errMsg
				})
			},
		})
	})
}
const writeBLECharacteristicValue = async (str, isHex) => {
	if (str.length === 0)
		return {
			ok: false,
			errCode: 30000,
			errMsg: 'data is null'
		}
	let buffer
	if (isHex) {
		buffer = new ArrayBuffer(str.length / 2)
		let x = new Uint8Array(buffer)
		for (let i = 0; i < x.length; i++) {
			x[i] = parseInt(str.substr(2 * i, 2), 16)
		}
	} else {
		buffer = new Uint8Array(strToUtf8Bytes(str)).buffer
	}

	return await _writeBLECharacteristicValue(buffer)
}

const utf8BytesToStr = utf8Bytes => {
	let unicodeStr = ''
	for (let pos = 0; pos < utf8Bytes.length;) {
		let flag = utf8Bytes[pos]
		let unicode = 0
		if (flag >>> 7 === 0) {
			unicodeStr += String.fromCharCode(utf8Bytes[pos])
			pos += 1
		}
		// else if ((flag & 0xFC) === 0xFC) {
		//     unicode = (utf8Bytes[pos] & 0x3) << 30
		//     unicode |= (utf8Bytes[pos + 1] & 0x3F) << 24
		//     unicode |= (utf8Bytes[pos + 2] & 0x3F) << 18
		//     unicode |= (utf8Bytes[pos + 3] & 0x3F) << 12
		//     unicode |= (utf8Bytes[pos + 4] & 0x3F) << 6
		//     unicode |= (utf8Bytes[pos + 5] & 0x3F)
		//     unicodeStr += String.fromCharCode(unicode)
		//     pos += 6
		// }
		// else if ((flag & 0xF8) === 0xF8) {
		//     unicode = (utf8Bytes[pos] & 0x7) << 24
		//     unicode |= (utf8Bytes[pos + 1] & 0x3F) << 18
		//     unicode |= (utf8Bytes[pos + 2] & 0x3F) << 12
		//     unicode |= (utf8Bytes[pos + 3] & 0x3F) << 6
		//     unicode |= (utf8Bytes[pos + 4] & 0x3F)
		//     unicodeStr += String.fromCharCode(unicode)
		//     pos += 5
		// }
		else if ((flag & 0xf0) === 0xf0) {
			unicode = (utf8Bytes[pos] & 0xf) << 18
			unicode |= (utf8Bytes[pos + 1] & 0x3f) << 12
			unicode |= (utf8Bytes[pos + 2] & 0x3f) << 6
			unicode |= utf8Bytes[pos + 3] & 0x3f
			unicodeStr += String.fromCharCode(unicode)
			pos += 4
		} else if ((flag & 0xe0) === 0xe0) {
			unicode = (utf8Bytes[pos] & 0x1f) << 12
			unicode |= (utf8Bytes[pos + 1] & 0x3f) << 6
			unicode |= utf8Bytes[pos + 2] & 0x3f
			unicodeStr += String.fromCharCode(unicode)
			pos += 3
		} else if ((flag & 0xc0) === 0xc0) {
			//110
			unicode = (utf8Bytes[pos] & 0x3f) << 6
			unicode |= utf8Bytes[pos + 1] & 0x3f
			unicodeStr += String.fromCharCode(unicode)
			pos += 2
		} else {
			unicodeStr += String.fromCharCode(utf8Bytes[pos])
			pos += 1
		}
	}
	return unicodeStr
}
const strToUtf8Bytes = str => {
	let bytes = []
	for (let i = 0; i < str.length; ++i) {
		let code = str.charCodeAt(i)
		if (code >= 0x10000 && code <= 0x10ffff) {
			bytes.push((code >> 18) | 0xf0) // 第一个字节
			bytes.push(((code >> 12) & 0x3f) | 0x80)
			bytes.push(((code >> 6) & 0x3f) | 0x80)
			bytes.push((code & 0x3f) | 0x80)
		} else if (code >= 0x800 && code <= 0xffff) {
			bytes.push((code >> 12) | 0xe0)
			bytes.push(((code >> 6) & 0x3f) | 0x80)
			bytes.push((code & 0x3f) | 0x80)
		} else if (code >= 0x80 && code <= 0x7ff) {
			bytes.push((code >> 6) | 0xc0)
			bytes.push((code & 0x3f) | 0x80)
		} else {
			bytes.push(code)
		}
	}
	return bytes
}

export default {
	onBluetoothAdapterStateChange,
	openBluetoothAdapter,

	onBluetoothDeviceFound,
	startBluetoothDevicesDiscovery,
	stopBluetoothDevicesDiscovery,

	onBLEConnectionStateChange,
	createBLEConnection,
	closeBLEConnection,

	onBLECharacteristicValueChange,
	writeBLECharacteristicValue,
}
