const parameterGroupService = require('./service.js')
const {
  isParameterGroupPollEnabled
} = require('../../domain/parameter-groups/model.js')

const POLL_TIMER_ID = '__parameterPoll'
const MEMORY_AREA_ORDER = {
  ADDR32: 0,
  DATA: 1,
  IDATA: 2,
  XDATA: 3,
  CODE: 4,
  BIT: 5
}

function getMemoryAreaOrder(group = {}) {
  const key = String(group.sourceMemoryArea || '').trim().toUpperCase()

  return Object.prototype.hasOwnProperty.call(MEMORY_AREA_ORDER, key) ? MEMORY_AREA_ORDER[key] : 99
}

function getGroupAddress(group = {}) {
  const sourceAddress = Number(group.sourceAddress)
  if (Number.isFinite(sourceAddress)) return Math.max(0, Math.floor(sourceAddress))

  const startAddress = Number(group.startAddress)
  return Number.isFinite(startAddress) ? Math.max(0, Math.floor(startAddress)) : 0
}

function shouldPoll(data) {
  return !!data
    && (data.activeParamView === 'parameterGroups' || data.activeParamView === 'parameterGroup')
    && !!data.connectedDevice
    && !!data.parameterAutoPollEnabled
}

function getParameterGroups(data = {}) {
  return data.parameterGroups || []
}

function getPollableGroups(data) {
  return getParameterGroups(data)
    .filter((group) => {
      if (!group || group.addressOverflow) return false
      if (!isParameterGroupPollEnabled(group)) return false
      if ((group.registers || []).some((register) => register && register.isDirty)) return false
      if (data.isStorageAccessProtocol) {
        return !!group.sourceMemoryArea
      }

      return !!group.functionCode
    })
    .sort((left, right) => (
      getMemoryAreaOrder(left) - getMemoryAreaOrder(right)
      || getGroupAddress(left) - getGroupAddress(right)
      || String(left.name || '').localeCompare(String(right.name || ''))
    ))
}

function createParameterGroupPoller(getData) {
  const timers = {}

  function clearTimer(timerId) {
    if (!timers[timerId]) return

    clearTimeout(timers[timerId])
    delete timers[timerId]
  }

  function clearAll() {
    Object.keys(timers).forEach(clearTimer)
  }

  function schedule(delay) {
    clearTimer(POLL_TIMER_ID)
    timers[POLL_TIMER_ID] = setTimeout(async () => {
      const data = getData()
      if (!shouldPoll(data)) {
        clearTimer(POLL_TIMER_ID)
        return
      }
      const protocolMode = data.protocolMode

      for (const group of getPollableGroups(data)) {
        const latestData = getData()
        if (!shouldPoll(latestData)) break
        if (latestData.protocolMode !== protocolMode) break

        await parameterGroupService.readGroup(group.id, {
          maxPacketLength: latestData.parameterMaxPacketLength,
          protocolMode,
          showModal: false
        })
      }

      const latestData = getData()
      if (shouldPoll(latestData)) {
        schedule(latestData.parameterPollInterval || 100)
      }
    }, delay)
  }

  function scheduleVisible() {
    const data = getData()
    if (!shouldPoll(data)) {
      clearAll()
      return
    }

    schedule(data.parameterPollInterval || 100)
  }

  return {
    clearAll,
    clearTimer,
    schedule,
    scheduleVisible
  }
}

module.exports = {
  createParameterGroupPoller
}
