// -----------------------
// TypeScript [2.6.2]
// BluetoothLePlugin: https://github.com/randdusing/cordova-plugin-bluetoothle
// -----------------------

const encodeStringToBytes = (rawString: string) => {
  let data = atob(rawString)
  let bytes = new Uint8Array(data.length)
  for (let i = 0; i < bytes.length; i++) {
    bytes[i] = data.charCodeAt(i)
  }
  return bytes
}

const getAdvertisingData = (response: BluetoothlePlugin.ScanStatus) => {
  let advertisingData: Array<number>
  if (typeof response.advertisement !== 'string') {
    let advertisement = encodeStringToBytes(response.advertisement.manufacturerData)
    advertisingData = [].slice.call(advertisement, 2)
  } else {
    let advertisement = encodeStringToBytes(response.advertisement)
    if (advertisement.length > 17) {
      advertisingData = [].slice.call(advertisement, 11)
    } else {
      advertisingData = Array.from(advertisement)
    }
  }
  return advertisingData
}

const buildValueConvert = (value: number) => {
  return parseInt(value.toString(16), 10)
}

const twoByteConcat = (lower: number, higher: number) => {
  return (higher << 8) | lower
}

export default function (response: BluetoothlePlugin.ScanStatus): Broadcast {
  let data = getAdvertisingData(response)
  let broadcast = {
    takenAt: (new Date()).getTime(),
    ordinalId: 0,
    buildMajor: 0,
    buildMinor: 0,
    interval: 0,
    realTime: false,
    cadence: 0,
    heartRate: 0,
    power: 0,
    caloricBurn: 0,
    duration: 0,
    gear: null,
    distance: 0
  } as Broadcast

  let index = 0

  broadcast.buildMajor = buildValueConvert(data[index++])
  if (broadcast.buildMajor !== 6) {
    throw new Error('Invalid build major')
  }
  broadcast.buildMinor = buildValueConvert(data[index++])

  if (broadcast.buildMajor === 6 && data.length > (index + 13)) {
    let dataType = data[index]
    if (dataType === 0 || dataType === 255) {
      broadcast.interval = 0
    } else if (dataType > 128 && dataType < 255) {
      broadcast.interval = dataType - 128
    }
    broadcast.realTime = dataType === 0 || (dataType > 128 && dataType < 255)

    broadcast.ordinalId = data[index + 1]
    if (broadcast.ordinalId <= 0 || broadcast.ordinalId > 200) {
      throw new Error('Invalid machine id')
    }

    broadcast.cadence = Math.round(twoByteConcat(data[index + 2], data[index + 3]) / 10)
    broadcast.heartRate = Math.round(twoByteConcat(data[index + 4], data[index + 5]) / 10) || null
    broadcast.power = twoByteConcat(data[index + 6], data[index + 7])
    broadcast.caloricBurn = twoByteConcat(data[index + 8], data[index + 9])
    broadcast.duration = data[index + 10] * 60 + data[index + 11]

    broadcast.distance = twoByteConcat(data[index + 12], data[index + 13])
    if ((broadcast.distance & 32768) !== 0) {
      // Metric
      broadcast.distance = broadcast.distance / 10
    } else {
      // Imperial (to Metric)
      broadcast.distance = ((broadcast.distance & 32767) / 10) * 1.60934
    }

    if (broadcast.buildMinor >= 21 && data.length > (index + 14)) {
      broadcast.gear = data[index + 14]
    }
  }

  return broadcast
}