// -----------------------
// TypeScript [2.6.2]
// Serial Library for Node.JS: https://github.com/node-serialport/node-serialport
// -----------------------
import SerialPort from 'serialport'
import MSeriesReceiverParser from './mSeriesReceiverParser'

const SCAN_PERIOD = 10000
const RECEIVER_VENDOR_ID = 0x0483
const RECEIVER_PRODUCT_ID = 0x5740
const DEVICE_NAME = 'USB Multi-Bike Receiver'

export default class Receiver {
  callback: ((broadcast: Broadcast) => void)
  port: SerialPort

  constructor (callback: ((broadcast: Broadcast) => void)) {
    this.callback = callback
    this.scanPorts()
  }

  close () {
    if (this.port) {
      this.port.close()
    }
  }

  private async scanPorts () {
    let ports: [SerialPortExt.PortConfig] = await SerialPort.list()

    let targetPort = ports.find(port => {
      return parseInt(port.productId, 16) === RECEIVER_PRODUCT_ID && parseInt(port.vendorId, 16) === RECEIVER_VENDOR_ID
    })

    if (targetPort) {
      console.log('PORT FOUND')
      this.initPort(targetPort.comName)
    } else {
      setTimeout(() => { this.scanPorts() }, SCAN_PERIOD)
    }
  }

  private initPort (comName: string) {
    this.port = new SerialPort(comName)
    this.port.on('error', error => this.onError(error))
    this.port.on('open', () => this.opened())
    this.port.on('close', () => this.closed())
    this.port.on('data', data => this.onData(data))

    if (!this.port.isOpen) {
      setTimeout(() => {
        if (!this.port.isOpen) {
          this.port.open()
        }
      }, ms('5s'))
    }
  }

  private opened () {
    console.log(DEVICE_NAME + ': Port Opened')
  }

  private closed () {
    console.log(DEVICE_NAME + ': Port Closed')
    this.scanPorts()
  }

  private onError (error: Error) {
    console.error(error)
  }

  private onData (data: Buffer) {
    this.parse(data)
  }

  private parse (data: Buffer) {
    let rawStream = new Uint8Array(data)
    let segments = String.fromCharCode.apply(null, rawStream).split(' ')
    let firstSegmentValue = parseInt(segments[0], 10)
    if (firstSegmentValue <= 200) {
      let swValue = parseFloat(segments[6])
      if (swValue >= 6.0 && swValue < 7.0) {
        try {
          let broadcast = MSeriesReceiverParser(segments)
          this.callback(broadcast)
        } catch (error) {
          console.error('M Series parse error\n', error)
        }
      } else if (swValue >= 8.0 && swValue < 9.0) {
        console.error('Incorrect device connected')
      }
    }
  }
}