import './Shower.scss';
import Taro, { Component, Config } from '@tarojs/taro';
import { View, Image, Text } from '@tarojs/components';
import ShowerStart from '../../images/shower/ic_sel@2x.png';
import ShowerCantStart from '../../images/shower/ic_nor@2x.png';
import LineImg from '../../images/shower/img_nor@2x.png';
import ScanIcon from '../../images/shower/ic_saoma@2x.png';

import { shareHandle } from '@/common/shareMethod';
import {
  fetchShowerInfo,
  DeviceInfoResponse,
  startShowerEquipment,
  fetchUsingShowerInfo,
} from '@/api/shower';
import { connect } from '@tarojs/redux';
import { Customer } from '@/types/Customer/Customer';
import { ComponentClass } from 'react';
import { ResponseDataEntity } from '@/api';
import { connectSocket } from '@/api/socket';
import { SOCKET_URL } from '@/constants';
import { BluetoothDevice } from '@/types/Shower/Shower';
import { updateBluetoothDevice } from './actions';
import { ab2str, str2ab } from '@/utils/arrayBuffer';

type DeviceInfo = {
  deviceId: string;
  uuid: string;
  isPrimary: boolean;
  serviceId: string;
  notifyId: string;
  writeId: string;
  readId: string;
};
type PageStateProps = {
  userinfo: Customer;
  bluetoothDevice: BluetoothDevice;
};
type PageDispatchProps = {
  updateBluetoothDevice: (entity: BluetoothDevice) => void;
};

type IProps = PageStateProps & PageDispatchProps;

type PageState = {
  bluetoothState: boolean;
  task: Taro.SocketTask | null;
  timer: number | undefined;
  reConnectting: boolean;
  deviceInfo: DeviceInfo;
  sockedDone: boolean;
  deviceDone: boolean;
  showerState: boolean;
};
const StopCode = 1000;

interface Shower {
  props: IProps;
  state: PageState;
}

@connect(
  ({ userinfo, Shower }) => ({
    userinfo,
    bluetoothDevice: Shower,
  }),
  dispatch => ({
    updateBluetoothDevice(data: BluetoothDevice) {
      dispatch(updateBluetoothDevice(data));
    },
  }),
)
class Shower extends Component {
  config: Config = {
    navigationBarTitleText: '洗 浴',
  };

  constructor(props) {
    super(props);
    this.state = {
      bluetoothState: false,
      task: null,
      timer: undefined,
      reConnectting: false,
      sockedDone: false,
      deviceDone: false,
      showerState: false,
      deviceInfo: {
        deviceId: '',
        uuid: '',
        isPrimary: false,
        serviceId: '',
        notifyId: '',
        writeId: '',
        readId: '',
      },
    };
  }

  onShareAppMessage = shareHandle;

  componentWillMount() {
    this.openBluetooth();
    this.connectDeviceSocket();
    this.checkUsingDevice();
  }

  componentWillUnmount() {
    this.closeBluetooth();
    this.closeDeviceSocket();
  }

  connectDeviceSocket() {
    connectSocket(SOCKET_URL).then(task => {
      task.onOpen(() => {
        console.log('onOpen');
        const { reConnectting } = this.state;
        if (reConnectting) {
          this.reConnectDeviceSocket(true);
        }
        this.setState({
          task: task,
          sockedDone: true,
        });
        task.send({ data: str2ab('{}') });
        this.sendDeviceCode();
      });
      task.onMessage(res => {
        const msg: string = ab2str(res.data);
        console.log('socket onMessage: ', msg);
        if (msg === '[0]') {
          console.log('结束蓝牙以及socket: ', msg);
          this.closeDeviceSocket();
          this.closeBluetoothConnection();
        } else if (msg === '[]') {
        } else {
          if (msg.length > 100) {
            this.sendMessageToDevice('[]');
          } else if (msg.length > 20) {
            for (let index = 0; index <= Math.floor(msg.length / 20); index++) {
              let str = msg.substring(index * 20, (index + 1) * 20);
              this.sendMessageToDevice(str);
            }
          } else {
            if (msg) {
              this.sendMessageToDevice(msg);
            }
          }
        }
      });
      task.onClose(e => {
        console.log('socked关闭', e);
        this.setState({
          task: null,
          sockedDone: false,
        });
        if (e.code === StopCode) {
          console.log('正确结束socket连接');
        } else {
          this.reConnectDeviceSocket(false);
        }
      });
    });
  }

  reConnectDeviceSocket(cancel: boolean) {
    const { timer, reConnectting } = this.state;
    if (cancel) {
      clearTimeout(timer);
      this.setState({
        timer: null,
        reConnectting: false,
      });
      return;
    }
    console.log(reConnectting, timer);

    if (reConnectting) {
      this.connectDeviceSocket();
    } else if (timer) {
      clearTimeout(timer);
      this.setState({
        timer: null,
        reConnectting: false,
      });
      console.log('请保证网络正常');
      Taro.showModal({
        title: '警告',
        content: '请保持网络畅通正常',
      });
    } else {
      let newTimer = setTimeout(() => {
        this.setState({
          reConnectting: false,
        });
      }, 10000);
      console.log(reConnectting, timer);
      this.setState({
        timer: newTimer,
        reConnectting: true,
      });
      this.connectDeviceSocket();
    }
  }

  sendDeviceCode() {
    const { task, sockedDone, deviceDone } = this.state;
    const { bluetoothDevice } = this.props;
    console.log('socket状态：', sockedDone, '蓝牙状态：', deviceDone);

    if (sockedDone && task && deviceDone) {
      let deviceData = '{<' + bluetoothDevice.code + '>}';
      console.log('发送设备编号:', deviceData);
      task.send({
        data: str2ab(deviceData),
      });
    }
  }

  closeDeviceSocket() {
    const { task } = this.state;
    console.log('in close', task);
    if (task) {
      task.close({
        code: StopCode,
        complete: () => {
          this.setState({
            task: null,
          });
        },
      });
    }
  }

  checkUsingDevice() {
    const { userinfo, updateBluetoothDevice } = this.props;
    fetchUsingShowerInfo({
      customerId: userinfo.customerId,
    })
      .then(res => {
        const data: DeviceInfoResponse | null = res.data;
        if (data) {
          updateBluetoothDevice(data);
          this.setState({
            showerState: true,
          });
          if (this.checkBluetoothState()) {
            this.startUseShower(true);
          } else if (data.isOnlyBluetooth) {
            this.warnBluetoothOpen();
          } else {
            this.startUseShower(true);
          }
        }
      })
      .catch(err => {
        console.error(err);
      });
  }

  openBluetooth() {
    Taro.openBluetoothAdapter()
      .then(res => {
        console.log(res);
        this.setState({
          bluetoothState: true,
        });
      })
      .catch(err => {
        console.error(err);
        Taro.onBluetoothAdapterStateChange(res => {
          console.log('adapterState changed, now is', res);
          if (res.available) {
            this.setState({
              bluetoothState: true,
            });
          }
        });
      });
  }

  checkBluetoothState() {
    const { bluetoothState } = this.state;
    return bluetoothState;
  }

  closeBluetoothConnection() {
    const { deviceInfo } = this.state;
    this.setState({
      showerState: false,
    });
    Taro.closeBLEConnection({
      deviceId: deviceInfo.deviceId,
    })
      .then(() => {
        this.setState({
          deviceDone: false,
        });
      })
      .catch(err => {
        console.log('断开蓝牙连接失败！');
        console.error(err);
      });
  }

  closeBluetooth() {
    Taro.closeBluetoothAdapter();
  }

  warnBluetoothOpen() {
    Taro.showModal({
      title: '警告',
      content: '为保障正常使用，请打开蓝牙功能',
    });
  }

  scanEquipment() {
    return Taro.scanCode({
      onlyFromCamera: true,
      scanType: ['qrCode'],
    })
      .then(res => {
        console.log(res);
        const { result } = res;
        const { userinfo, updateBluetoothDevice } = this.props;

        return fetchShowerInfo({
          deviceCode: result,
          customerId: userinfo.customerId.toString(),
          campusId: userinfo.areaId,
        })
          .then((res: ResponseDataEntity<DeviceInfoResponse>) => {
            const data = res.data;
            updateBluetoothDevice({
              code: data.code,
              isOnlyBluetooth: data.isOnlyBluetooth,
              position: data.position,
            });
          })
          .catch(err => {
            console.error(err);
            Taro.showToast({
              title: err.msg || '请扫描正确的设备码',
              icon: 'none',
            });
          });
      })
      .catch(err => {
        console.error(err);
        // Taro.showToast({
        //   title: err.msg || '呼起扫码失败！',
        //   icon: 'none',
        // });
      });
  }

  startDevicesDiscovery() {
    if (this.checkBluetoothState()) {
      return Taro.startBluetoothDevicesDiscovery()
        .then(() => {
          const { code, isOnlyBluetooth } = this.props.bluetoothDevice;
          let timer: number | null = null;
          return new Promise(resolve => {
            if (isOnlyBluetooth) {
              timer = setTimeout(() => {
                Taro.stopBluetoothDevicesDiscovery();
              }, 10000);
            } else {
              timer = setTimeout(() => {
                Taro.stopBluetoothDevicesDiscovery();
              }, 5000);
            }
            Taro.onBluetoothDeviceFound(res => {
              const devices = res.devices;
              if (devices[0].name === code) {
                let deviceId = devices[0].deviceId;
                Taro.stopBluetoothDevicesDiscovery();
                this.setState({
                  deviceId,
                });
                if (timer) {
                  clearInterval(timer);
                }
                resolve(deviceId);
              }
            });
          });
        })
        .catch(err => {
          // Taro.stopBluetoothDevicesDiscovery();
          Taro.hideLoading();
          console.error('startBluetoothDevicesDiscovery: ', err);
          this.warnBluetoothOpen();
        });
    } else {
      Taro.hideLoading();
      this.warnBluetoothOpen();
      throw '未开启蓝牙';
    }
  }

  createConnection(deviceId: string) {
    console.log(deviceId);
    return new Promise((resolve, reject) => {
      Taro.createBLEConnection({
        deviceId,
      });

      let abStr = '';
      Taro.onBLECharacteristicValueChange(res => {
        console.log(
          `characteristic ${res.characteristicId} has changed, now is ${
            res.value
          }`,
        );
        console.log(res.value);
        let datastr = ab2str(res.value);
        console.log('获取socket消息： ', datastr);
        if (datastr === '<>') {
          console.log('结束蓝牙以及socket: ', datastr);
          this.closeDeviceSocket();
          this.closeBluetoothConnection();
        } else {
          const { task } = this.state;
          if (
            datastr.substring(0, 1) === '{' &&
            datastr.substring(datastr.length - 1, datastr.length) === '}'
          ) {
            console.log('发送完整数据： ', datastr);
            if (task) {
              task.send({
                data: str2ab(datastr),
              });
            }
          } else if (datastr.substring(0, 1) === '{') {
            console.log('接受头部数据： ', datastr);
            abStr = datastr;
          } else if (
            abStr.length &&
            datastr.substring(datastr.length - 1, datastr.length) === '}'
          ) {
            abStr += datastr;
            console.log('接受尾部数据后： ', abStr);
            console.log('发送完整数据： ', abStr);
            if (task) {
              task.send({
                data: str2ab(abStr),
              });
            }
            abStr = '';
          } else {
            console.log('弃掉数据：', datastr);
          }
        }
      });

      Taro.onBLEConnectionStateChange(res => {
        const { showerState } = this.state;
        console.log(
          `device ${res.deviceId} state has changed, connected: ${
            res.connected
          }`,
        );
        if (res.connected) {
          this.setState({
            deviceDone: true,
            showerState: true,
          });
          resolve(res.deviceId);
        } else {
          console.log(res);
          this.setState({
            deviceDone: false,
          });
          if (showerState) {
            this.reConnectDeviceBluetooth();
          }
          reject('蓝牙未成功连接');
        }
      });
    });
  }

  sendMessageToDevice(msg: string) {
    const { deviceInfo } = this.state;
    console.log(msg, str2ab(msg).byteLength);
    if (deviceInfo.writeId) {
      Taro.writeBLECharacteristicValue({
        deviceId: deviceInfo.deviceId,
        serviceId: deviceInfo.serviceId,
        characteristicId: deviceInfo.writeId,
        value: str2ab(msg),
      });
    }
  }

  reConnectDeviceBluetooth() {
    if (this.checkBluetoothState()) {
      const { deviceInfo } = this.state;
      this.createConnection(deviceInfo.deviceId);
    }
  }

  getDeviceServices(deviceId: string) {
    return Taro.getBLEDeviceServices({
      deviceId: deviceId,
    }).then(services => {
      return { deviceId: deviceId, services: services.services };
    });
  }

  getDeviceCharacter({
    deviceId,
    serviceId,
  }: {
    deviceId: string;
    serviceId: string;
  }) {
    return Taro.getBLEDeviceCharacteristics({
      deviceId,
      serviceId,
    }).then(res => {
      let notifyId = '',
        writeId = '',
        readId = '';
      for (let i = 0; i < res.characteristics.length; i++) {
        let charc = res.characteristics[i];
        if (charc.properties.notify) {
          notifyId = charc.uuid;
        }
        if (charc.properties.write) {
          writeId = charc.uuid;
        }
        if (charc.properties.write) {
          readId = charc.uuid;
        }
      }
      return {
        notifyId,
        writeId,
        readId,
      };
    });
  }

  openNotify({ notifyId, writeId, readId }) {
    this.setState(({ deviceInfo }: PageState) => ({
      deviceInfo: {
        ...deviceInfo,
        notifyId,
        writeId,
        readId,
      },
    }));
    const { deviceInfo } = this.state;
    return Taro.notifyBLECharacteristicValueChange({
      deviceId: deviceInfo.deviceId,
      serviceId: deviceInfo.serviceId,
      characteristicId: notifyId,
      state: true,
    }).then(res => {
      console.log(res);

      let abStr = '';
      Taro.onBLECharacteristicValueChange(res => {
        let msg = ab2str(res.value);
        console.log('获取蓝牙设备消息： ', msg);
        if (msg === '<>') {
          this.closeBluetoothConnection();
          this.closeDeviceSocket();
        } else {
          if (
            msg.substring(0, 1) === '{' &&
            msg.substring(msg.length - 1, msg.length) === '}'
          ) {
            const { task } = this.state;
            console.log('发送给socket完整数据： ', msg);
            task &&
              task.send({
                data: str2ab(msg),
              });
          } else if (msg.substring(0, 1) === '{') {
            console.log('接受头部数据： ', msg);
            abStr = msg;
          } else if (
            abStr.length &&
            msg.substring(msg.length - 1, msg.length) === '}'
          ) {
            abStr += msg;
            console.log('接受尾部数据后： ', abStr);
            console.log('发送给socket完整数据： ', abStr);

            const { task } = this.state;
            task &&
              task.send({
                data: str2ab(abStr),
              });
            abStr = '';
          } else {
            if (abStr.length) {
              abStr += msg;
            } else {
              console.log('弃掉数据：', msg);
            }
          }
        }
      });
    });
  }

  startUseShower(reConnect: boolean) {
    if (!reConnect) {
      Taro.showLoading({
        title: '开启中',
        mask: true,
      });
    }
    const { sockedDone } = this.state;
    if (!sockedDone) {
      this.connectDeviceSocket();
    }
    this.startDevicesDiscovery()
      .then((deviceId: string) => this.createConnection(deviceId))
      .then(this.getDeviceServices)
      .then(res => {
        const deviceId = res.deviceId;
        const services = res.services;
        let service = services[services.length - 1];
        const serviceId = service.uuid;
        this.setState(({ deviceInfo }: PageState) => ({
          deviceInfo: {
            ...deviceInfo,
            deviceId,
            serviceId,
          },
        }));
        return this.getDeviceCharacter({
          deviceId,
          serviceId,
        });
      })
      .then(res => this.openNotify(res))
      .then(() => {
        this.sendDeviceCode();
        if (!reConnect) {
          setTimeout(() => {
            this.sendStartShower();
          }, 2000);
        }
      })
      .catch(err => {
        console.error(err);
        Taro.hideLoading();
      });
  }

  sendStartShower() {
    const { code } = this.props.bluetoothDevice;
    const { userinfo } = this.props;
    startShowerEquipment({
      deviceCode: code,
      customerId: userinfo.customerId.toString(),
      customerName: userinfo.customerName,
      customerPhone: userinfo.customerPhone,
    })
      .then(res => {
        console.log(res);
        Taro.showToast({
          title: '开启成功！',
        });
        this.setState({
          showerState: true,
        });
      })
      .catch(err => {
        console.error(err);
        Taro.showToast({
          title: err.msg || '开启失败！',
          icon: 'none',
        });
        this.closeBluetoothConnection();
      });
  }

  render() {
    const {
      bluetoothDevice: { code, position },
    } = this.props;
    const { showerState } = this.state;
    return (
      <View className='Shower'>
        <View className='equipment-info-box'>
          <View className='equipment-info'>
            {code ? (
              <View className='equipment-title matched'>已匹配设备</View>
            ) : (
              <View className='equipment-title'>未匹配设备</View>
            )}

            <Image className='equipment-line' src={LineImg} />
            {code ? (
              <View className='equipment-list'>
                <View className='equipment-item'>
                  <Text className='equipment-label'>设备编号：</Text>
                  <Text className='equipment-value'>{code}</Text>
                </View>
                <View className='equipment-item'>
                  <Text className='equipment-label'>设备位置：</Text>
                  <Text className='equipment-value'>{position}</Text>
                </View>
              </View>
            ) : (
              <View v-loading='loading' className='equipment-noCode'>
                点击左下角扫一扫完成匹配
              </View>
            )}
          </View>
        </View>
        <View className='btn-box'>
          <Image
            className='shower-scan-btn'
            src={ScanIcon}
            onClick={this.scanEquipment}
          />
          {code && !showerState ? (
            <Image
              className='shower-start-btn'
              src={ShowerStart}
              onClick={() => this.startUseShower(false)}
            />
          ) : (
            <Image className='shower-start-btn' src={ShowerCantStart} />
          )}
        </View>
      </View>
    );
  }
}

export default Shower as ComponentClass<PageStateProps, PageState>;
