const {
  formatFixedValue
} = require('../../utils/base-utils.js')
const {
  getDataType,
  isHexRegister
} = require('./value-types.js')

function padWordHex(value) {
  return Number(value || 0).toString(16).toUpperCase().padStart(4, '0')
}

function formatIntegerValue(value, dataType) {
  const type = getDataType(dataType).key
  const numberValue = Number(value)

  if (!Number.isFinite(numberValue)) return '--'
  if (type === 'int8_t') return String(((Math.round(numberValue) << 24) >> 24))
  if (type === 'uint8_t') return String(Math.round(numberValue) & 0xFF)
  if (type === 'int16_t') return String(((Math.round(numberValue) << 16) >> 16))
  if (type === 'uint16_t') return String(Math.round(numberValue) & 0xFFFF)
  if (type === 'int32_t') return String((Math.round(numberValue) | 0))
  if (type === 'uint32_t') return String(Math.round(numberValue) >>> 0)

  return String(Math.round(numberValue))
}

function formatHexValue(value) {
  const numberValue = Number(value)
  if (!Number.isFinite(numberValue)) return '--'

  return `0x${padWordHex(Math.round(numberValue) & 0xFFFF)}`
}

function formatFloatValue(value) {
  return formatFixedValue(value, 6).replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '')
}

function isLittleEndian(byteOrder) {
  const text = String(byteOrder || '').trim().toLowerCase()

  return text === 'little' || text === 'le' || text === '1'
}

function applyByteOrder(bytes, byteOrder) {
  const source = Array.isArray(bytes) ? bytes.slice() : []

  return isLittleEndian(byteOrder) ? source.reverse() : source
}

function parseIntegerText(value) {
  const text = String(value === undefined || value === null ? '' : value).trim()
  if (!text) return null

  const isHex = /^[-+]?0x[0-9a-f]+$/i.test(text) || /^0x[0-9a-f]+$/i.test(text)
  const parsed = isHex ? parseInt(text, 16) : Number(text)

  return Number.isFinite(parsed) ? parsed : null
}

function parseHexText(value) {
  const text = String(value === undefined || value === null ? '' : value).trim()
  if (!text) return null

  const hexText = text.toUpperCase().startsWith('0X') ? text.slice(2) : text
  if (/^[0-9A-F]{1,4}$/i.test(hexText)) {
    const parsedHex = parseInt(hexText, 16)
    return Number.isFinite(parsedHex) ? parsedHex : null
  }

  return null
}

function getNumericRange(dataType) {
  const type = getDataType(dataType).key

  if (type === 'int8_t') return { max: 127, min: -128 }
  if (type === 'uint8_t') return { max: 0xFF, min: 0 }
  if (type === 'int16_t') return { max: 32767, min: -32768 }
  if (type === 'uint16_t') return { max: 0xFFFF, min: 0 }
  if (type === 'int32_t') return { max: 2147483647, min: -2147483648 }
  if (type === 'uint32_t') return { max: 0xFFFFFFFF, min: 0 }
  if (type === 'hex') return { max: 0xFFFF, min: 0 }

  return { max: Number.POSITIVE_INFINITY, min: Number.NEGATIVE_INFINITY }
}

function parseNumberText(value, dataType) {
  const text = String(value === undefined || value === null ? '' : value).trim()
  if (!text || text === '--') return null

  if (getDataType(dataType).key === 'float') {
    const parsed = Number(text)
    return Number.isFinite(parsed) ? parsed : null
  }
  if (isHexRegister(dataType)) return parseHexText(text)

  return parseIntegerText(text)
}

function parseRangeBoundary(value, dataType, label) {
  const text = String(value === undefined || value === null ? '' : value).trim()
  if (!text) return null

  const parsed = parseNumberText(text, dataType)
  if (parsed === null) {
    throw new Error(`${label}无效`)
  }

  return parsed
}

function validateNumericValue(register, value) {
  const dataType = getDataType(register.dataType).key
  const range = getNumericRange(dataType)
  const numberValue = Number(value)
  if (!Number.isFinite(numberValue)) return false

  if (dataType !== 'float' && Math.round(numberValue) !== numberValue) {
    throw new Error(`${register.name || '寄存器'} 需要整数`)
  }
  if (numberValue < range.min || numberValue > range.max) {
    throw new Error(`${register.name || '寄存器'} 超出 ${dataType} 范围`)
  }

  const minValue = parseRangeBoundary(register.minValue, dataType, `${register.name || '寄存器'} 最小值`)
  const maxValue = parseRangeBoundary(register.maxValue, dataType, `${register.name || '寄存器'} 最大值`)
  if (minValue !== null && numberValue < minValue) {
    throw new Error(`${register.name || '寄存器'} 小于限制最小值`)
  }
  if (maxValue !== null && numberValue > maxValue) {
    throw new Error(`${register.name || '寄存器'} 大于限制最大值`)
  }

  return true
}

function floatToBytes(value, byteOrder = 'big') {
  const buffer = new ArrayBuffer(4)
  const view = new DataView(buffer)

  view.setFloat32(0, Number(value), false)

  return applyByteOrder([
    view.getUint8(0),
    view.getUint8(1),
    view.getUint8(2),
    view.getUint8(3)
  ], byteOrder)
}

function bytesToFloatValue(bytes, byteOrder = 'big') {
  if (!Array.isArray(bytes) || bytes.length < 4) return null

  const buffer = new ArrayBuffer(4)
  const view = new DataView(buffer)
  const source = applyByteOrder(bytes.slice(0, 4), byteOrder)

  for (let index = 0; index < 4; index += 1) {
    view.setUint8(index, Number(source[index]) & 0xFF)
  }

  return view.getFloat32(0, false)
}

function unsignedIntegerToBytes(value, byteLength, byteOrder = 'big') {
  let numberValue = Math.round(Number(value) || 0)
  const bytes = []

  if (numberValue < 0) {
    numberValue += Math.pow(2, byteLength * 8)
  }

  for (let index = byteLength - 1; index >= 0; index -= 1) {
    bytes[index] = numberValue & 0xFF
    numberValue = Math.floor(numberValue / 0x100)
  }

  return applyByteOrder(bytes, byteOrder)
}

function bytesToUnsignedInteger(bytes, byteOrder = 'big') {
  return applyByteOrder(bytes, byteOrder).reduce((value, byte) => ((value * 0x100) + (Number(byte) & 0xFF)), 0)
}

function bytesToSignedInteger(bytes, byteOrder = 'big') {
  const unsignedValue = bytesToUnsignedInteger(bytes, byteOrder)
  const signLimit = Math.pow(2, bytes.length * 8 - 1)
  const fullRange = Math.pow(2, bytes.length * 8)

  return unsignedValue >= signLimit ? unsignedValue - fullRange : unsignedValue
}

module.exports = {
  bytesToFloatValue,
  bytesToSignedInteger,
  bytesToUnsignedInteger,
  floatToBytes,
  formatFloatValue,
  formatHexValue,
  formatIntegerValue,
  getNumericRange,
  parseHexText,
  parseIntegerText,
  parseNumberText,
  parseRangeBoundary,
  unsignedIntegerToBytes,
  validateNumericValue
}
