const {
  toFiniteNumber
} = require('./base-utils.js')

function toAddressKey(address) {
  if (typeof address === 'number' && Number.isFinite(address)) {
    return Math.round(address).toString(16).toUpperCase()
  }

  const text = String(address || '').trim().toUpperCase()
  if (!text) return ''

  const hexText = text.startsWith('0X') ? text.slice(2) : text
  if (/^[0-9A-F]+$/.test(hexText)) {
    return parseInt(hexText, 16).toString(16).toUpperCase()
  }

  const numberValue = Number(text)
  if (Number.isFinite(numberValue)) {
    return Math.round(numberValue).toString(16).toUpperCase()
  }

  return text
}

function wordsToFloat(highWord, lowWord) {
  const highValue = Number(highWord)
  const lowValue = Number(lowWord)
  if (!Number.isInteger(highValue) || !Number.isInteger(lowValue)) return null

  const buffer = new ArrayBuffer(4)
  const view = new DataView(buffer)

  view.setUint16(0, highValue & 0xFFFF, false)
  view.setUint16(2, lowValue & 0xFFFF, false)

  return view.getFloat32(0, false)
}

function floatToWords(value) {
  const numberValue = toFiniteNumber(value, NaN)
  if (!Number.isFinite(numberValue)) return null

  const buffer = new ArrayBuffer(4)
  const view = new DataView(buffer)

  view.setFloat32(0, numberValue, false)

  return [view.getUint16(0, false), view.getUint16(2, false)]
}

function toRegisterWord(value) {
  const numberValue = toFiniteNumber(value, NaN)
  if (!Number.isFinite(numberValue)) return null

  const word = Math.round(numberValue)

  return word >= 0 && word <= 0xFFFF ? word : null
}

function addCoilReadValues(readValues, startAddress, quantity, response) {
  if (!readValues || !readValues.coils || !response || !Array.isArray(response.dataBytes)) return

  for (let offset = 0; offset < quantity; offset += 1) {
    const byte = response.dataBytes[Math.floor(offset / 8)] || 0
    const bit = (byte >> (offset % 8)) & 0x01
    readValues.coils[toAddressKey(startAddress + offset)] = bit
  }
}

function addWordReadValues(readValues, startAddress, response) {
  if (!readValues || !readValues.words || !response || !Array.isArray(response.words)) return

  response.words.forEach((word, index) => {
    readValues.words[toAddressKey(startAddress + index)] = Number(word) & 0xFFFF
  })
}

function getRegisterWordCache(startAddress, words) {
  if (!Array.isArray(words)) return {}

  return words.reduce((result, word, index) => {
    result[startAddress + index] = Number(word) & 0xFFFF
    return result
  }, {})
}

module.exports = {
  addCoilReadValues,
  addWordReadValues,
  floatToWords,
  getRegisterWordCache,
  toAddressKey,
  toRegisterWord,
  wordsToFloat
}
