const controlService = require('./control-service.js')
const {
  controlState,
  paramsState: paramsPageState
} = require('../../domain/motor-control/data.js')
const transport = require('../../transport/ble-core.js')
const modbusClient = require('../../protocols/modbus-rtu/client.js')
const {
  notifyPageToast
} = require('../../utils/page-toast.js')
const {
  addCoilReadValues,
  addWordReadValues
} = require('../../utils/register-value-utils.js')

const readValues = {
  coils: {},
  words: {}
}

let paramsSnapshot = paramsPageState.createInitialState()
let paramsSnapshotVersion = 0

const READ_STEPS = [
  {
    address: 0x00,
    functionCode: 0x01,
    label: '同步线圈 00-10',
    quantity: 17,
    onResponse(response, chunk) {
      addCoilReadValues(readValues, chunk.address, chunk.quantity, response)
      controlService.applyControlReadValues(readValues.coils)
    }
  },
  {
    address: 0x30,
    functionCode: 0x03,
    label: '同步估算器参数 30-4A',
    quantity: 27,
    onResponse(response, chunk) {
      addWordReadValues(readValues, chunk.address, response)
    }
  },
  {
    address: 0x60,
    functionCode: 0x03,
    label: '同步参数配置 60-8D',
    quantity: 46,
    onResponse(response, chunk) {
      addWordReadValues(readValues, chunk.address, response)
      controlService.applyMotorReadWords(response.words || [], chunk.address)
    }
  },
  {
    address: controlState.DRIVER_PARAM_START_ADDRESS,
    functionCode: 0x04,
    label: '同步驱动器参数 A0-B3',
    quantity: controlState.DRIVER_PARAM_WORD_COUNT,
    onResponse(response, chunk) {
      addWordReadValues(readValues, chunk.address, response)
      controlService.applyDriverReadWords(response.words || [])
    }
  },
  {
    address: controlState.STATUS_START_ADDRESS,
    functionCode: 0x04,
    label: '同步状态',
    quantity: () => controlState.getStatusWordCount(controlService.getState().userStatusCount),
    onResponse(response, chunk) {
      addWordReadValues(readValues, chunk.address, response)
      controlService.applyStatusReadWords(response.words || [], chunk.address)
    }
  }
]

let syncing = false
const subscribers = []

function getState() {
  return {
    isSyncing: syncing,
    syncVersion: paramsSnapshotVersion
  }
}

function notify() {
  const state = getState()

  subscribers.slice().forEach((subscriber) => {
    subscriber(state)
  })
}

function setSyncing(value) {
  syncing = !!value
  notify()
}

transport.subscribe((transportState) => {
  if (!transportState.connectedDevice && syncing) {
    setSyncing(false)
  }
})

function subscribe(subscriber) {
  if (typeof subscriber !== 'function') return () => {}

  subscribers.push(subscriber)
  subscriber(getState())

  return () => {
    const index = subscribers.indexOf(subscriber)
    if (index >= 0) subscribers.splice(index, 1)
  }
}

function resetReadValues() {
  readValues.coils = {}
  readValues.words = {}
}

function getParamsSnapshot() {
  return {
    ...paramsSnapshot,
    syncVersion: paramsSnapshotVersion
  }
}

async function syncAllRegisters() {
  if (syncing) return false

  const transportState = transport.getState()
  if (!transportState.connectedDevice) return false

  const slaveAddress = modbusClient.getSharedSlaveAddress()
  if (slaveAddress === null) return false

  setSyncing(true)
  resetReadValues()

  try {
    for (const step of READ_STEPS) {
      const quantity = typeof step.quantity === 'function' ? step.quantity() : step.quantity
      const values = await modbusClient.readSpans(
        slaveAddress,
        step.functionCode,
        [{
          address: step.address,
          quantity
        }],
        step.label,
        'sync-read',
        {
          onChunk(response, chunk) {
            if (typeof step.onResponse === 'function') {
              step.onResponse(response, chunk)
            }
          }
        }
      )

      if (!values) return false
      if (values.coils) Object.assign(readValues.coils, values.coils)
      if (values.words) Object.assign(readValues.words, values.words)
    }

    paramsSnapshot = paramsPageState.applyReadValues(paramsSnapshot, readValues)
    paramsSnapshotVersion += 1
    notify()
    notifyPageToast('同步完成')
    return true
  } finally {
    setSyncing(false)
  }
}

module.exports = {
  getParamsSnapshot,
  getState,
  subscribe,
  syncAllRegisters
}
