local utils = require('lib_utils')
local ScriptedAudio = require("soundhandler")

local config = utils.loadConfig('alarms.cfg')
local alarms = config.alarms
local voices = config.voices
local active = true
local voice = 1
local warmup
local callbacks

engine.addHook('On Gameplay Start', function()
  warmup = 10
end)

engine.addHook('On Frame', function()
  if not hv.Player:isValid() then return end
  if warmup > 0 then warmup = warmup - 1 end
  local ready = (warmup == 0)
  for _, alarm in ipairs(alarms) do
    if active or alarm.alwaysActive then
      local lit = callbacks[alarm.name](alarm)
      if lit ~= alarm.lit then
        alarm.lit = lit
        local show = alarm.show
        if lit and ready then
          if show then
            local gauge = show.gauge
            utils.runSEXP('hud-set-custom-gauge-active', true, gauge)
            if show.text then
              local text = show.text
              text = ba.XSTR(text[1], text[2])
              text = ba.replaceTokens(text)
              text = ba.replaceVariables(text)
              text = string.sub(text, 1, 31) -- SEXP string limit
              utils.runSEXP('hud-set-text', gauge, text)
            end
            local ms = show.ms
            if ms then
              mn.waitAsync(ms / 1000):continueWith(function()
                utils.runSEXP('hud-set-custom-gauge-active', false, gauge)
              end)
            end
          end
          local play = alarm.play
          if play then
            ScriptedAudio:playSound(voice .. '_' .. play, false)
          end
        elseif show then
          utils.runSEXP('hud-set-custom-gauge-active', false, show.gauge)
        end
      end
    end
  end
end, {State = 'GS_STATE_GAME_PLAY'})

callbacks = (function()
  local callbacks = {}

  local function isSecondaryBankLow(bankIndex)
    return function(alarm)
      local bank = hv.Player.SecondaryBanks[bankIndex]
      local percent = bank.AmmoLeft * 100 / bank.AmmoMax -- NaN if invalid
      local threshold = alarm.threshold or 25
      return (percent > 0) and (percent < threshold)
    end
  end

  local function isSecondaryBankEmpty(bankIndex)
    return function()
      local bank = hv.Player.SecondaryBanks[bankIndex]
      return bank:isValid() and bank.AmmoLeft == 0
    end
  end

  local function isSubsystemDestroyed(category)
    return function()
      local sexp = '( hits-left-subsystem-generic !' .. tostring(hv.Player) .. '! !' .. category .. '! )'
      return mn.evaluateNumericSEXP(sexp) == 0
    end
  end

  callbacks['Secondary 1 Low'] = isSecondaryBankLow(1)
  callbacks['Secondary 2 Low'] = isSecondaryBankLow(2)
  callbacks['Secondary 3 Low'] = isSecondaryBankLow(3)
  callbacks['Secondary 1 Empty'] = isSecondaryBankEmpty(1)
  callbacks['Secondary 2 Empty'] = isSecondaryBankEmpty(2)
  callbacks['Secondary 3 Empty'] = isSecondaryBankEmpty(3)

  callbacks['Communications Destroyed'] = isSubsystemDestroyed('Communications')
  callbacks['Engines Destroyed'] = isSubsystemDestroyed('Engines')
  callbacks['Navigation Destroyed'] = isSubsystemDestroyed('Navigation')
  callbacks['Sensors Destroyed'] = isSubsystemDestroyed('Sensors')
  callbacks['Weapons Destroyed'] = isSubsystemDestroyed('Weapons')

  callbacks['Hull Critical'] = function(alarm)
    local percent = hv.Player.HitpointsLeft * 100 / hv.Player.Class.HitpointsMax
    local threshold = alarm.threshold or 30
    return percent <= threshold
  end

  callbacks['Shields Breached'] = function(alarm)
    if hv.Player:getFlag('no-shields') then return false end
    local shield = hv.Player.Shields
    local threshold = alarm.threshold or 15
    local count = #shield
    for i = 1, count do
      if shield[i] < threshold then
        return true
      end
    end
    return false
  end

  callbacks['Weapon Energy Low'] = function(alarm)
    local threshold = alarm.threshold or 15
    local percent = hv.Player.WeaponEnergyLeft * 100 / hv.Player.WeaponEnergyMax
    return percent <= threshold
  end

  callbacks['Countermeasures Low'] = function(alarm)
    local threshold = alarm.threshold or 7
    local count = hv.Player.CountermeasuresLeft
    return (count > 0) and (count <= threshold)
  end

  callbacks['Countermeasures Empty'] = function()
    return hv.Player.CountermeasuresLeft == 0
  end

  callbacks['Departure Authorized'] = function()
    return not mn.evaluateSEXP('( are-ship-flags-set !' .. tostring(hv.Player) .. '! !no-subspace-drive! )')
  end

  callbacks['Cheater Cheater'] = function()
    return mn.evaluateSEXP('( player-is-cheating )')
  end

  return callbacks
end)()

return {
  setActive = function(a)
    active = a
  end,
  setVoice = function(v)
    if v < 0 or v >= #voices then
      ba.warning('Cannot set alarm voice to index ' .. v .. '!')
    else
      voice = v
    end
  end,
  getVoiceIndex = function(name)
    for i, v in ipairs(voices) do
      if string.lower(name) == string.lower(v) then
        return i - 1
      end
    end
    ba.warning('Invalid alarm voice "' .. name .. '"! Using default!')
    return 1
  end
}

