const {
  atoBandwidthInputRegisters,
  calculatedParameterRegisters,
  dqGainInputRegisters,
  oilParameterInputRegisters,
  parameterInputRegisters,
  prepositionParameterInputRegisters,
  protectionRegisters,
  protectionSwitchRegisters,
  speedLoopExtraRegisters,
  speedSlopeRegister,
  tailwindSwitchRegisters,
  getByteRegisterValue
} = require('./registers.js')
const {
  parseHexInteger
} = require('../../utils/base-utils.js')
const {
  getSharedInputValues,
  mergeInputValues,
  toFiniteNumber
} = require('./calculation-context.js')
const {
  calculateAtoGainWriteValues,
  calculateDqGainWriteValue,
  calculateParameterInputWriteValue,
  calculateParameterReadValue,
  calculateProtectionWriteValue,
  calculateSpeedSlope,
  formatFixedValue
} = require('./conversions.js')
const {
  toAddressKey,
  wordsToFloat
} = require('../../utils/register-value-utils.js')
const {
  appendInputUnit
} = require('./input-value-utils.js')

const VSP_CURVE_ORDER = [
  '开机电压',
  '关机电压',
  '调速最低电压',
  '调速最高电压'
]

const SPEED_LOOP_INPUT_ORDER = [
  '速度最小值',
  '速度最大值',
  'SOUT_MAX'
]

const TAILWIND_CALCULATED_NAMES = [
  'SPEED_KLPF_TAILWIND',
  'OBS_EA_KS_TAILWIND'
]

const ESTIMATOR_CALCULATED_PREFIXES = [
  'OBS_E',
  'OBS_FBASE',
  'OBS_EA_KS',
  'SPEED_KLPF',
  'FOC_KFG'
]

const PROTECTION_SECTION_DEFINITIONS = [
  {
    key: 'base',
    title: '基础',
    rows: [
      [
        { kind: 'switch', name: '保护使能' },
        { kind: 'switch', name: '恢复使能' },
        { kind: 'input', name: '故障恢复时间' }
      ]
    ]
  },
  {
    key: 'hardwareCurrent',
    title: '硬件过流',
    rows: [
      [
        { kind: 'input', name: '硬件过流值', label: '硬件过流' }
      ]
    ]
  },
  {
    key: 'current',
    title: '电流',
    rows: [
      [
        { kind: 'switch', name: '电流保护使能' },
        { kind: 'input', name: '软件过流值' }
      ]
    ]
  },
  {
    key: 'phase',
    title: '缺相',
    rows: [
      [
        { kind: 'switch', name: '缺相保护使能' }
      ]
    ]
  },
  {
    key: 'voltage',
    title: '电压',
    rows: [
      [
        { kind: 'switch', name: '电压保护使能' }
      ],
      [
        { kind: 'input', name: '过压保护值', label: '过压值' },
        { kind: 'input', name: '欠压保护值', label: '欠压值' }
      ],
      [
        { kind: 'input', name: '过压恢复值' },
        { kind: 'input', name: '欠压恢复值' }
      ]
    ]
  },
  {
    key: 'stall',
    title: '堵转',
    rows: [
      [
        { kind: 'switch', name: '堵转保护使能' }
      ],
      [
        { kind: 'input', name: '速度限制最大值' },
        { kind: 'input', name: '速度限制最小值' }
      ],
      [
        { kind: 'input', name: '反电动势低阈值' },
        { kind: 'input', name: '反电动势高阈值' }
      ],
      [
        { kind: 'input', name: '速度中间值' }
      ]
    ]
  },
  {
    key: 'power',
    title: '功率',
    rows: [
      [
        { kind: 'switch', name: '功率保护使能' },
        { kind: 'input', name: '功率保护值' },
        { kind: 'input', name: '功率保护时间' }
      ]
    ]
  },
  {
    key: 'temperature',
    title: '温度',
    rows: [
      [
        { kind: 'switch', name: '温度保护使能' }
      ],
      [
        { kind: 'input', name: '温度保护值' },
        { kind: 'input', name: '温度恢复值' },
        { kind: 'input', name: '温度保护时间' }
      ]
    ]
  },
  {
    key: 'serial',
    title: '串口',
    rows: [
      [
        { kind: 'switch', name: '串口保护使能' },
        { kind: 'input', name: '串口丢失检测时间' }
      ]
    ]
  },
  {
    key: 'pwm',
    title: 'PWM',
    rows: [
      [
        { kind: 'switch', name: 'PWM丢失保护使能' }
      ]
    ]
  }
]

function formatInputValue(item, value) {
  if (value === '' || value === undefined || value === null) return '--'

  const numberValue = toFiniteNumber(value, NaN)
  if (!Number.isFinite(numberValue)) return value

  if (item.type === 'uint8_t' && (numberValue < 0 || numberValue > 0xFF)) return '--'

  if (item.type === 'float') return formatFixedValue(numberValue, 2)

  return String(Math.round(numberValue))
}

function getInputValues(registers) {
  return mergeInputValues(registers)
}

function getSharedParameterValues(registers, extraRegisters = []) {
  return {
    ...getSharedInputValues(),
    ...getInputValues(registers.concat(extraRegisters))
  }
}

function updateAtoBandwidthValues(registers, inputValues) {
  return registers.map((item) => ({
    ...item,
    ...calculateAtoGainWriteValues(item.inputValue, inputValues)
  }))
}

function updateInputWriteValues(registers) {
  const inputValues = getInputValues(registers)

  return registers.map((item) => ({
    ...item,
    writeValue: calculateParameterInputWriteValue(item, item.inputValue, inputValues)
  }))
}

function updateSpeedLoopExtraValues(registers, inputValues) {
  return registers.map((item) => {
    const writeValue = calculateParameterInputWriteValue(item, item.inputValue, inputValues)

    return {
      ...item,
      actualText: '',
      writeValue
    }
  })
}

function updateOilParameterValues(registers, inputValues) {
  return registers.map((item) => ({
    ...item,
    writeValue: calculateParameterInputWriteValue(item, item.inputValue, inputValues)
  }))
}

function updateSpeedSlope(register, inputValues) {
  const speedSlope = calculateSpeedSlope(inputValues)

  return {
    ...register,
    writeValue: speedSlope === null ? '--' : formatFixedValue(speedSlope, 2)
  }
}

function addSourceIndex(registers) {
  return registers.map((item, index) => ({
    ...item,
    sourceIndex: index
  }))
}

function isNameIn(names, item) {
  return names.includes(item.name)
}

function isTailwindAtoRegister(item) {
  return item.suffix === 'TAILWIND'
}

function isTailwindCalculatedRegister(item) {
  return isNameIn(TAILWIND_CALCULATED_NAMES, item)
}

function isEstimatorCalculatedRegister(item) {
  if (isTailwindCalculatedRegister(item)) return false

  return ESTIMATOR_CALCULATED_PREFIXES.some((prefix) => item.name.startsWith(prefix))
}

function sortByNameOrder(registers, nameOrder) {
  return registers
    .filter((item) => nameOrder.includes(item.name))
    .slice()
    .sort((left, right) => nameOrder.indexOf(left.name) - nameOrder.indexOf(right.name))
}

function mapByName(registers) {
  return registers.reduce((result, item) => {
    result[item.name] = item
    return result
  }, {})
}

function buildProtectionField(definition, registerMap, switchMap) {
  const source = definition.kind === 'switch'
    ? switchMap[definition.name]
    : registerMap[definition.name]

  if (!source) return null

  return {
    ...source,
    kind: definition.kind,
    label: definition.label || source.name,
    metaValue: source.writeValue === 0 ? '0' : (source.writeValue || '--')
  }
}

function buildProtectionGroups(registers, switches = []) {
  const protectionDisplayRegisters = addSourceIndex(registers)
  const protectionSwitchDisplayRegisters = addSourceIndex(switches)
  const registerMap = mapByName(protectionDisplayRegisters)
  const switchMap = mapByName(protectionSwitchDisplayRegisters)

  return {
    protectionDisplayRegisters,
    protectionSections: PROTECTION_SECTION_DEFINITIONS.map((section) => ({
      ...section,
      rows: section.rows
        .map((row, rowIndex) => {
          const fields = row
            .map((definition) => buildProtectionField(definition, registerMap, switchMap))
            .filter(Boolean)

          return fields.length
            ? {
              fields,
              key: `${section.key}-${rowIndex}`
            }
            : null
        })
        .filter(Boolean)
    })).filter((section) => section.rows.length)
  }
}

function getWord(readValues, address) {
  return readValues.words[toAddressKey(address)]
}

function getRegisterReadValue(item, readValues) {
  if (item.area && item.area.key === 'coil') {
    const coilValue = readValues.coils[toAddressKey(item.address)]
    return coilValue === undefined ? null : coilValue
  }

  const firstWord = getWord(readValues, item.address)
  if (!Number.isInteger(firstWord)) return null

  if (item.type === 'uint8_t' && item.bytePosition) {
    return getByteRegisterValue(item, firstWord)
  }

  if (item.type === 'float') {
    const nextAddress = (parseHexInteger(item.address) + 1).toString(16).toUpperCase()
    return wordsToFloat(firstWord, getWord(readValues, nextAddress))
  }

  return firstWord
}

function formatReadValue(item, value) {
  if (value === null || value === undefined || !Number.isFinite(Number(value))) return null

  const numberValue = Number(value)

  if (item.type === 'float') return formatFixedValue(numberValue, 2)

  return String(Math.round(numberValue))
}

function getReadInputValue(item, readValue, readText, options = {}) {
  let inputText = readText

  if (options.useCalculatedInputValue) {
    const calculatedValue = calculateParameterReadValue(item, readValue, options.inputValues || {})

    if (calculatedValue !== null) {
      inputText = formatFixedValue(calculatedValue, 2)
    }
  }

  return appendInputUnit(item, inputText)
}

function getDqReadInputValue(item, rawText) {
  const rawValue = Number(rawText)
  if (!Number.isFinite(rawValue)) return ''

  if (item.gainType === 'kp') return formatFixedValue(rawValue / 4095, 2)
  if (item.gainType === 'ki') return formatFixedValue(rawValue / 32767, 2)

  return rawText
}

function applyReadValuesToRegisters(registers, readValues, options = {}) {
  return registers.map((item) => {
    const readValue = getRegisterReadValue(item, readValues)
    const readText = formatReadValue(item, readValue)

    if (readText === null) return item

    const nextItem = {
      ...item,
      isDirty: false,
      writeValue: readText
    }

    if (Object.prototype.hasOwnProperty.call(item, 'value')) {
      nextItem.value = Number(readValue) !== 0
    }

    if (options.updateInputValue) {
      nextItem.inputValue = getReadInputValue(item, readValue, readText, options)
    }

    if (options.updateDqInputValue) {
      nextItem.inputValue = getDqReadInputValue(item, readText)
    }

    return nextItem
  })
}

function applyReadValuesToAtoRegisters(registers, readValues) {
  return registers.map((item) => {
    const kpWord = getWord(readValues, item.kpAddress)
    const kiWord = getWord(readValues, item.kiAddress)

    if (!Number.isInteger(kpWord) && !Number.isInteger(kiWord)) return item

    return {
      ...item,
      isDirty: false,
      kpWriteValue: Number.isInteger(kpWord) ? String(kpWord) : item.kpWriteValue,
      kiWriteValue: Number.isInteger(kiWord) ? String(kiWord) : item.kiWriteValue
    }
  })
}

function clearDirty(registers = [], matcher = () => true) {
  return registers.map((item) => (
    matcher(item)
      ? {
        ...item,
        isDirty: false
      }
      : item
  ))
}

function clearGroupDirty(data, groupKey) {
  const nextState = {
    ...data
  }

  if (groupKey === 'estimator') {
    nextState.atoBandwidthInputRegisters = clearDirty(data.atoBandwidthInputRegisters)
    nextState.calculatedParameterRegisters = clearDirty(data.calculatedParameterRegisters, isEstimatorCalculatedRegister)
  }
  if (groupKey === 'dq') nextState.dqGainInputRegisters = clearDirty(data.dqGainInputRegisters)
  if (groupKey === 'tailwind') {
    nextState.tailwindSwitchRegisters = clearDirty(data.tailwindSwitchRegisters, (item) => item.name !== '预定位启用')
    nextState.atoBandwidthInputRegisters = clearDirty(data.atoBandwidthInputRegisters, isTailwindAtoRegister)
    nextState.calculatedParameterRegisters = clearDirty(data.calculatedParameterRegisters, isTailwindCalculatedRegister)
  }
  if (groupKey === 'preposition') {
    nextState.tailwindSwitchRegisters = clearDirty(data.tailwindSwitchRegisters, (item) => item.name === '预定位启用')
    nextState.prepositionParameterInputRegisters = clearDirty(data.prepositionParameterInputRegisters)
  }
  if (groupKey === 'speedLoop') {
    nextState.parameterInputRegisters = clearDirty(data.parameterInputRegisters, (item) => (
      SPEED_LOOP_INPUT_ORDER.includes(item.name)
    ))
    nextState.speedLoopExtraRegisters = clearDirty(data.speedLoopExtraRegisters)
  }
  if (groupKey === 'vsp') {
    nextState.parameterInputRegisters = clearDirty(data.parameterInputRegisters, (item) => VSP_CURVE_ORDER.includes(item.name))
    nextState.speedSlopeRegister = {
      ...data.speedSlopeRegister,
      isDirty: false
    }
  }
  if (groupKey === 'oil') nextState.oilParameterInputRegisters = clearDirty(data.oilParameterInputRegisters)
  if (groupKey === 'protection') {
    nextState.protectionRegisters = clearDirty(data.protectionRegisters)
    nextState.protectionSwitchRegisters = clearDirty(data.protectionSwitchRegisters)
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function clearRegisterDirty(registers = [], index) {
  return registers.map((item, currentIndex) => (
    currentIndex === index
      ? {
        ...item,
        isDirty: false
      }
      : item
  ))
}

function clearTailwindSwitchDirty(data, index) {
  const nextState = {
    ...data,
    tailwindSwitchRegisters: clearRegisterDirty(data.tailwindSwitchRegisters, index)
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function clearProtectionSwitchDirty(data, index) {
  const nextState = {
    ...data,
    protectionSwitchRegisters: clearRegisterDirty(data.protectionSwitchRegisters, index)
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function buildViewState(state) {
  const inputRegisters = addSourceIndex(state.parameterInputRegisters)
  const atoRegisters = addSourceIndex(state.atoBandwidthInputRegisters)
  const dqRegisters = addSourceIndex(state.dqGainInputRegisters)
  const calculatedRegisters = addSourceIndex(state.calculatedParameterRegisters)
  const speedLoopExtras = addSourceIndex(state.speedLoopExtraRegisters)
  const tailwindSwitches = addSourceIndex(state.tailwindSwitchRegisters)

  return {
    vspCurveRegisters: sortByNameOrder(inputRegisters, VSP_CURVE_ORDER),
    speedLoopInputDisplayRegisters: sortByNameOrder(inputRegisters, SPEED_LOOP_INPUT_ORDER),
    speedLoopExtraDisplayRegisters: speedLoopExtras,
    atoBandwidthDisplayRegisters: atoRegisters.filter((item) => !isTailwindAtoRegister(item)),
    tailwindAtoBandwidthDisplayRegisters: atoRegisters.filter(isTailwindAtoRegister),
    tailwindCalculatedDisplayRegisters: calculatedRegisters.filter(isTailwindCalculatedRegister),
    tailwindControlRegisters: tailwindSwitches.filter((item) => item.name !== '预定位启用'),
    prepositionSwitchRegisters: tailwindSwitches.filter((item) => item.name === '预定位启用'),
    prepositionParameterDisplayRegisters: addSourceIndex(state.prepositionParameterInputRegisters),
    dqGainDisplayRegisters: dqRegisters,
    estimatorCalculatedDisplayRegisters: calculatedRegisters.filter(isEstimatorCalculatedRegister),
    ...buildProtectionGroups(state.protectionRegisters, state.protectionSwitchRegisters)
  }
}

function createInitialState() {
  const state = {
    atoBandwidthInputRegisters,
    atoBandwidthDisplayRegisters: [],
    calculatedParameterRegisters,
    dqGainInputRegisters,
    dqGainDisplayRegisters: [],
    estimatorCalculatedDisplayRegisters: [],
    oilParameterInputRegisters,
    parameterInputRegisters,
    prepositionParameterDisplayRegisters: [],
    prepositionParameterInputRegisters,
    prepositionSwitchRegisters: [],
    protectionDisplayRegisters: [],
    protectionSections: [],
    protectionRegisters,
    protectionSwitchRegisters,
    speedLoopExtraRegisters,
    speedLoopExtraDisplayRegisters: [],
    speedLoopInputDisplayRegisters: [],
    speedSlopeRegister,
    tailwindAtoBandwidthDisplayRegisters: [],
    tailwindCalculatedDisplayRegisters: [],
    tailwindControlRegisters: [],
    tailwindSwitchRegisters,
    vspCurveRegisters: []
  }

  return {
    ...state,
    ...buildViewState(state)
  }
}

function refreshState(data) {
  const inputValues = getSharedParameterValues(data.parameterInputRegisters, data.speedLoopExtraRegisters)
  const nextSpeedLoopExtraRegisters = updateSpeedLoopExtraValues(data.speedLoopExtraRegisters, inputValues)
  const nextState = {
    ...data,
    atoBandwidthInputRegisters: updateAtoBandwidthValues(data.atoBandwidthInputRegisters, inputValues),
    oilParameterInputRegisters: updateOilParameterValues(data.oilParameterInputRegisters, inputValues),
    speedLoopExtraRegisters: nextSpeedLoopExtraRegisters,
    speedSlopeRegister: updateSpeedSlope(data.speedSlopeRegister, inputValues)
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applyReadValues(data, readValues) {
  const inputValues = getSharedParameterValues(data.parameterInputRegisters, data.speedLoopExtraRegisters)
  const nextState = {
    ...data,
    atoBandwidthInputRegisters: applyReadValuesToAtoRegisters(data.atoBandwidthInputRegisters, readValues),
    calculatedParameterRegisters: applyReadValuesToRegisters(data.calculatedParameterRegisters, readValues),
    dqGainInputRegisters: applyReadValuesToRegisters(data.dqGainInputRegisters, readValues, {
      updateDqInputValue: true
    }),
    oilParameterInputRegisters: applyReadValuesToRegisters(data.oilParameterInputRegisters, readValues, {
      inputValues,
      updateInputValue: true,
      useCalculatedInputValue: true
    }),
    parameterInputRegisters: applyReadValuesToRegisters(data.parameterInputRegisters, readValues, {
      inputValues,
      updateInputValue: true,
      useCalculatedInputValue: true
    }),
    prepositionParameterInputRegisters: applyReadValuesToRegisters(data.prepositionParameterInputRegisters, readValues, {
      updateInputValue: true
    }),
    protectionRegisters: applyReadValuesToRegisters(data.protectionRegisters, readValues, {
      inputValues,
      updateInputValue: true,
      useCalculatedInputValue: true
    }),
    protectionSwitchRegisters: applyReadValuesToRegisters(data.protectionSwitchRegisters, readValues),
    speedLoopExtraRegisters: applyReadValuesToRegisters(data.speedLoopExtraRegisters, readValues, {
      updateInputValue: true
    }),
    speedSlopeRegister: applyReadValuesToRegisters([data.speedSlopeRegister], readValues)[0],
    tailwindSwitchRegisters: applyReadValuesToRegisters(data.tailwindSwitchRegisters, readValues)
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applyParameterInput(data, index, value) {
  const changedRegisters = data.parameterInputRegisters.map((item, currentIndex) => {
    if (currentIndex !== index) return item

    return {
      ...item,
      isDirty: true,
      inputValue: value,
      writeValue: formatInputValue(item, value)
    }
  })
  const nextRegisters = updateInputWriteValues(changedRegisters)
  const inputValues = getSharedParameterValues(nextRegisters, data.speedLoopExtraRegisters)
  const nextState = {
    ...data,
    parameterInputRegisters: nextRegisters,
    atoBandwidthInputRegisters: updateAtoBandwidthValues(data.atoBandwidthInputRegisters, inputValues),
    speedLoopExtraRegisters: updateSpeedLoopExtraValues(data.speedLoopExtraRegisters, inputValues),
    speedSlopeRegister: updateSpeedSlope(data.speedSlopeRegister, inputValues)
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applyAtoBandwidthInput(data, index, value) {
  const inputValues = getSharedParameterValues(data.parameterInputRegisters)
  const nextRegisters = data.atoBandwidthInputRegisters.map((item, currentIndex) => {
    if (currentIndex !== index) return item

    return {
      ...item,
      isDirty: true,
      inputValue: value,
      ...calculateAtoGainWriteValues(value, inputValues)
    }
  })
  const nextState = {
    ...data,
    atoBandwidthInputRegisters: nextRegisters
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applyDqGainInput(data, index, value) {
  const nextRegisters = data.dqGainInputRegisters.map((item, currentIndex) => {
    if (currentIndex !== index) return item

    return {
      ...item,
      isDirty: true,
      inputValue: value,
      writeValue: calculateDqGainWriteValue(item, value)
    }
  })
  const nextState = {
    ...data,
    dqGainInputRegisters: nextRegisters
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applySpeedLoopExtraInput(data, index, value) {
  const changedRegisters = data.speedLoopExtraRegisters.map((item, currentIndex) => {
    if (currentIndex !== index) return item

    return {
      ...item,
      isDirty: true,
      inputValue: value
    }
  })
  const inputValues = getSharedParameterValues(data.parameterInputRegisters, changedRegisters)
  const nextState = {
    ...data,
    speedLoopExtraRegisters: updateSpeedLoopExtraValues(changedRegisters, inputValues)
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applyOilParameterInput(data, index, value) {
  const changedRegisters = data.oilParameterInputRegisters.map((item, currentIndex) => {
    if (currentIndex !== index) return item

    return {
      ...item,
      isDirty: true,
      inputValue: value
    }
  })
  const inputValues = getSharedParameterValues(data.parameterInputRegisters, data.speedLoopExtraRegisters)
  const nextState = {
    ...data,
    oilParameterInputRegisters: updateOilParameterValues(changedRegisters, inputValues)
  }

  return nextState
}

function applyPrepositionParameterInput(data, index, value) {
  const nextState = {
    ...data,
    prepositionParameterInputRegisters: data.prepositionParameterInputRegisters.map((item, currentIndex) => {
      if (currentIndex !== index) return item

      return {
        ...item,
        isDirty: true,
        inputValue: value,
        writeValue: formatInputValue(item, value)
      }
    })
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applyTailwindSwitchChange(data, index, checked) {
  const nextState = {
    ...data,
    tailwindSwitchRegisters: data.tailwindSwitchRegisters.map((item, currentIndex) => {
      if (currentIndex !== index) return item

      return {
        ...item,
        isDirty: true,
        value: checked,
        writeValue: checked ? 1 : 0
      }
    })
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applyProtectionSwitchChange(data, index, checked) {
  const nextState = {
    ...data,
    protectionSwitchRegisters: data.protectionSwitchRegisters.map((item, currentIndex) => {
      if (currentIndex !== index) return item

      return {
        ...item,
        isDirty: true,
        value: checked,
        writeValue: checked ? 1 : 0
      }
    })
  }

  return {
    ...nextState,
    ...buildViewState(nextState)
  }
}

function applyProtectionInput(data, index, value) {
  const nextRegisters = data.protectionRegisters.map((item, currentIndex) => {
    if (currentIndex !== index) return item

    return {
      ...item,
      isDirty: true,
      inputValue: value,
      writeValue: value === '' ? '--' : calculateProtectionWriteValue(item, toFiniteNumber(value, NaN))
    }
  })

  return {
    ...data,
    protectionRegisters: nextRegisters,
    ...buildProtectionGroups(nextRegisters, data.protectionSwitchRegisters)
  }
}

function getInputRegister(data, group, index) {
  if (group === 'parameter') return data.parameterInputRegisters[index]
  if (group === 'ato') return data.atoBandwidthInputRegisters[index]
  if (group === 'dq') return data.dqGainInputRegisters[index]
  if (group === 'speedLoopExtra') return data.speedLoopExtraRegisters[index]
  if (group === 'oil') return data.oilParameterInputRegisters[index]
  if (group === 'preposition') return data.prepositionParameterInputRegisters[index]
  if (group === 'protection') return data.protectionRegisters[index]

  return null
}

function applyInputBlur(data, group, index, value) {
  const item = getInputRegister(data, group, index)
  if (!item) return data

  const inputValue = appendInputUnit(item, value === undefined ? item.inputValue : value)

  if (group === 'parameter') return applyParameterInput(data, index, inputValue)
  if (group === 'ato') return applyAtoBandwidthInput(data, index, inputValue)
  if (group === 'dq') return applyDqGainInput(data, index, inputValue)
  if (group === 'speedLoopExtra') return applySpeedLoopExtraInput(data, index, inputValue)
  if (group === 'oil') return applyOilParameterInput(data, index, inputValue)
  if (group === 'preposition') return applyPrepositionParameterInput(data, index, inputValue)
  if (group === 'protection') return applyProtectionInput(data, index, inputValue)

  return data
}

module.exports = {
  applyAtoBandwidthInput,
  clearGroupDirty,
  clearProtectionSwitchDirty,
  clearTailwindSwitchDirty,
  applyDqGainInput,
  applyInputBlur,
  applyOilParameterInput,
  applyParameterInput,
  applyPrepositionParameterInput,
  applyProtectionInput,
  applyProtectionSwitchChange,
  applyReadValues,
  applySpeedLoopExtraInput,
  applyTailwindSwitchChange,
  createInitialState,
  refreshState
}
