const childProcess = require('child_process')
const Readline = require('@serialport/parser-readline')

// get only serial port names
function checkPathOfDevice(path) {
  return /(tty(S|WCH|ACM|USB|AMA|MFD|O|XRUSB)|rfcomm)/.test(path) && path
}

function propName(name) {
  return {
    DEVNAME: 'path',
    ID_VENDOR_ENC: 'manufacturer',
    ID_SERIAL_SHORT: 'serialNumber',
    ID_VENDOR_ID: 'vendorId',
    ID_MODEL_ID: 'productId',
    DEVLINKS: 'pnpId',
  }[name.toUpperCase()]
}

function decodeHexEscape(str) {
  return str.replace(/\\x([a-fA-F0-9]{2})/g, (a, b) => {
    return String.fromCharCode(parseInt(b, 16))
  })
}

function propVal(name, val) {
  if (name === 'pnpId') {
    const match = val.match(/\/by-id\/([^\s]+)/)
    return (match && match[1]) || undefined
  }
  if (name === 'manufacturer') {
    return decodeHexEscape(val)
  }
  if (/^0x/.test(val)) {
    return val.substr(2)
  }
  return val
}

function listLinux() {
  return new Promise((resolve, reject) => {
    const ports = []
    const ude = childProcess.spawn('udevadm', ['info', '-e'])
    const lines = ude.stdout.pipe(new Readline())
    ude.on('close', code => code && reject(new Error(`Error listing ports udevadm exited with error code: ${code}`)))
    ude.on('error', reject)
    lines.on('error', reject)

    let port = {}
    let skipPort = false
    lines.on('data', line => {
      const lineType = line.slice(0, 1)
      const data = line.slice(3)
      // new port entry
      if (lineType === 'P') {
        port = {
          manufacturer: undefined,
          serialNumber: undefined,
          pnpId: undefined,
          locationId: undefined,
          vendorId: undefined,
          productId: undefined,
        }
        skipPort = false
        return
      }

      if (skipPort) {
        return
      }

      // Check dev name and save port if it matches flag to skip the rest of the data if not
      if (lineType === 'N') {
        if (checkPathOfDevice(data)) {
          ports.push(port)
        } else {
          skipPort = true
        }
        return
      }

      // parse data about each port
      if (lineType === 'E') {
        const keyValue = data.match(/^(.+)=(.*)/)
        if (!keyValue) {
          return
        }
        const key = propName(keyValue[1])
        if (!key) {
          return
        }
        port[key] = propVal(key, keyValue[2])
      }
    })

    lines.on('finish', () => resolve(ports))
  })
}

module.exports = listLinux
