const storageAccessProtocol = require('../../protocols/storage-access/index.js')
const {
  bytesToHex
} = require('../../utils/binary-utils.js')
const {
  normalizeHexDwordText,
  normalizeHexText,
  normalizeHexWordText,
  parseHexBytes,
  parseHexDword,
  parseHexNumber,
  validateHexDwordText,
  validateHexText,
  validateHexWordText
} = require('../../utils/validation.js')
const protocolIo = require('./protocol-io.js')

const MEMORY_COMMANDS = [
  { key: 'read', label: '读取', description: '按字节读取内存' },
  { key: 'write', label: '写入', description: '按字节写入内存' }
]

const MEMORY_AREAS = [
  { key: storageAccessProtocol.AREA.ADDR32, label: 'addr32', name: 'ADDR32', addressWidth: 32 },
  { key: storageAccessProtocol.AREA.DATA, label: 'data', name: 'DATA' },
  { key: storageAccessProtocol.AREA.IDATA, label: 'idata', name: 'IDATA' },
  { key: storageAccessProtocol.AREA.XDATA, label: 'xdata', name: 'XDATA' },
  { key: storageAccessProtocol.AREA.CODE, label: 'code', name: 'CODE', readOnly: true }
]

const CONTROL_COMMANDS = [
  { key: 'reset', label: '复位', op: storageAccessProtocol.CONTROL_OP.RESET }
]

function formatDwordHex(value) {
  const numberValue = Math.max(0, Math.floor(Number(value) || 0))

  return numberValue.toString(16).toUpperCase().padStart(8, '0')
}

function getMemoryAreaOptions() {
  return MEMORY_AREAS.map((area) => ({ ...area }))
}

function resolveMemoryCommand(index) {
  const commandIndex = Number(index) === 1 ? 1 : 0

  return {
    command: MEMORY_COMMANDS[commandIndex],
    index: commandIndex
  }
}

function resolveMemoryArea(index, commandKey = 'read') {
  const memoryAreas = getMemoryAreaOptions()
  let areaIndex = Number(index) || 0

  if (commandKey === 'write' && memoryAreas[areaIndex] && memoryAreas[areaIndex].readOnly) {
    areaIndex = 0
  }
  if (!memoryAreas[areaIndex]) areaIndex = 0

  return {
    area: memoryAreas[areaIndex] || memoryAreas[0],
    index: areaIndex,
    options: memoryAreas
  }
}

function normalizeDisplayHexText(value, addressWidth = 16) {
  const text = addressWidth === 32
    ? normalizeHexDwordText(value)
    : normalizeHexWordText(value)

  return text.toUpperCase()
}

function buildMemoryPreview(commandKey, area, address, length, dataBytes) {
  try {
    const frameOptions = {
      maxFrameBytes: 0
    }

    if (commandKey === 'write') {
      return bytesToHex(storageAccessProtocol.buildWriteFrame(area, address, dataBytes, frameOptions), ' ')
    }

    return bytesToHex(storageAccessProtocol.buildReadFrame(area, address, length, frameOptions), ' ')
  } catch (error) {
    return ''
  }
}

function normalizeMemoryCommandState(current = {}, changed = {}) {
  const next = {
    storageAccessAddress: current.storageAccessAddress || '',
    storageAccessAreaIndex: current.storageAccessAreaIndex || 0,
    storageAccessCommandIndex: current.storageAccessCommandIndex || 0,
    storageAccessDataText: current.storageAccessDataText || '',
    storageAccessLength: current.storageAccessLength || '',
    ...changed
  }
  const resolvedCommand = resolveMemoryCommand(next.storageAccessCommandIndex)
  const command = resolvedCommand.command
  const resolvedArea = resolveMemoryArea(next.storageAccessAreaIndex, command.key)
  const area = resolvedArea.area
  const addressWidth = Number(area.addressWidth) === 32 ? 32 : 16
  const addressText = normalizeDisplayHexText(next.storageAccessAddress, addressWidth)
  const lengthText = normalizeDisplayHexText(next.storageAccessLength, 16)
  const dataText = normalizeHexText(next.storageAccessDataText)
  const dataBytes = dataText ? parseHexBytes(dataText) : []
  const address = parseInt(addressText || '0', 16)
  const byteLength = parseInt(lengthText || '0', 16)
  const hasAddressText = !!addressText.trim()
  const hasLengthText = !!lengthText.trim()
  const addressError = hasAddressText
    ? (addressWidth === 32
      ? validateHexDwordText(addressText, '地址')
      : validateHexWordText(addressText, '地址'))
    : ''
  const lengthError = hasLengthText ? validateHexWordText(lengthText, '长度') : ''
  let errorText = addressError || lengthError

  if (!errorText && hasLengthText && byteLength <= 0) {
    errorText = '长度必须大于 0'
  } else if (!errorText && command.key === 'write') {
    const hexError = validateHexText(next.storageAccessDataText)
    if (hexError) {
      errorText = hexError
    } else if (area.readOnly) {
      errorText = `${area.label} 区不可写`
    } else if (dataBytes.length !== byteLength) {
      errorText = `写入长度为 ${byteLength} 字节，当前数据为 ${dataBytes.length} 字节`
    }
  }

  const canPreview = !errorText && hasAddressText && hasLengthText && byteLength > 0

  return {
    storageAccessAddress: addressText,
    storageAccessAddressMaxLength: addressWidth === 32 ? 8 : 4,
    storageAccessAddressWidthText: `${addressWidth}bit`,
    storageAccessAreaIndex: resolvedArea.index,
    storageAccessAreaLabel: area.label,
    storageAccessAreaLocked: false,
    storageAccessAreaOptions: resolvedArea.options,
    storageAccessCommandIndex: resolvedCommand.index,
    storageAccessCommandLabel: command.label,
    storageAccessDataText: dataText,
    storageAccessErrorText: errorText,
    storageAccessLength: lengthText,
    storageAccessPreviewHexText: canPreview
      ? buildMemoryPreview(command.key, area.key, address, byteLength, dataBytes)
      : '',
    storageAccessPreviewText: canPreview
      ? `${area.label} 0x${addressWidth === 32 ? formatDwordHex(address) : address.toString(16).toUpperCase().padStart(4, '0')} / ${byteLength} bytes`
      : '',
    storageAccessShowWriteData: command.key === 'write',
    storageAccessTitleText: `${command.label}命令`
  }
}

function parseMemoryCommandInput(data = {}) {
  const state = normalizeMemoryCommandState(data)
  const command = resolveMemoryCommand(state.storageAccessCommandIndex).command
  const area = resolveMemoryArea(state.storageAccessAreaIndex, command.key).area

  if (state.storageAccessErrorText) {
    throw new Error(state.storageAccessErrorText)
  }
  if (!String(state.storageAccessAddress || '').trim()) {
    throw new Error('地址请输入十六进制')
  }
  if (!String(state.storageAccessLength || '').trim()) {
    throw new Error('长度请输入十六进制')
  }

  return {
    area,
    areaValue: area.key,
    byteLength: parseHexNumber(state.storageAccessLength, '长度', 0xFFFF),
    command,
    commandKey: command.key,
    dataBytes: command.key === 'write' ? parseHexBytes(state.storageAccessDataText || '') : [],
    startAddress: Number(area.addressWidth) === 32
      ? parseHexDword(state.storageAccessAddress, '地址')
      : parseHexNumber(state.storageAccessAddress, '地址', 0xFFFF),
    state
  }
}

async function executeMemoryCommand(data = {}, options = {}) {
  const command = parseMemoryCommandInput(data)

  if (command.commandKey === 'read') {
    const bytes = await protocolIo.readMemory(
      command.areaValue,
      command.startAddress,
      command.byteLength,
      options.label || '存储访问协议读取',
      options.kind || 'communication-storage-read',
      options
    )

    return {
      bytes,
      command,
      ok: !!bytes,
      previewHex: bytes ? bytesToHex(bytes, ' ') : '',
      state: command.state
    }
  }

  const ok = await protocolIo.writeMemory(
    command.areaValue,
    command.startAddress,
    command.dataBytes,
    options.label || '存储访问协议写入',
    options.kind || 'communication-storage-write',
    options
  )

  return {
    command,
    ok,
    state: command.state
  }
}

function getControlCommand(commandKey) {
  return CONTROL_COMMANDS.find((command) => command.key === commandKey) || null
}

async function executeControlCommand(commandKey, data = {}, options = {}) {
  const command = getControlCommand(commandKey)
  if (!command) {
    return {
      errorText: '特殊指令无效',
      ok: false
    }
  }

  const response = await protocolIo.executeControl(
    command.op,
    [],
    command.label || '特殊指令',
    `storage-control-${command.key}`,
    {
      maxPacketLength: options.maxPacketLength,
      showModal: options.showModal !== false
    }
  )

  if (!response) {
    return {
      errorText: '指令执行失败或超时',
      ok: false
    }
  }

  return {
    command,
    ok: true,
    response
  }
}

module.exports = {
  executeControlCommand,
  executeMemoryCommand,
  getControlCommand,
  getMemoryAreaOptions,
  normalizeMemoryCommandState
}
