local EXPLOSION_EFFECT = "( explosion-effect %s %s %s 0 0 0 0 100 0 !0! 0 %s %s )"
local FADE_IN = "( fade-in 250 255 255 255 )"

local DISTORT_EFFECT = "( set-post-effect !distort noise! %s )"
local FILM_GRAIN_EFFECT = "( set-post-effect !film grain! %s )"

local SHOW_ALERT_FOR_SECONDS = 5
local ALERT_UPDATE_TIME_SECONDS = 0.1

local SHOW_HUD_GAUGE = "( hud-set-custom-gauge-active ( true ) !Warning_Display! )"
local HIDE_HUD_GAUGE = "( hud-set-custom-gauge-active ( false ) !Warning_Display! )"
local SET_HUD_FRAME_0 = "( hud-set-frame !Warning_Display! 0 )"
local SET_HUD_FRAME_1 = "( hud-set-frame !Warning_Display! 1 )"

local Sunglare = {}

function Sunglare:activate(delayLow, delayHigh, intensityLow, intensityHigh, durationLow, durationHigh, shieldDamage, windX, windY, windZ)
  Sunglare._delayLow = delayLow
  Sunglare._delayHigh = delayHigh
  Sunglare._intensityLow = intensityLow
  Sunglare._intensityHigh = intensityHigh
  Sunglare._durationLow = durationLow
  Sunglare._durationHigh = durationHigh
  Sunglare._shieldDamage = shieldDamage
  Sunglare._windX = windX
  Sunglare._windY = windY
  Sunglare._windZ = windZ
  Sunglare:reactivate()
end

function Sunglare:deactivate()
  Sunglare._immanent = false
  Sunglare._suspended = true
  mn.runSEXP(HIDE_HUD_GAUGE)
end

function Sunglare:reactivate()
  Sunglare:deactivate()
  Sunglare._token = (Sunglare._token or 0) + 1
  Sunglare._suspended = false
  Sunglare._schedule()
end

function Sunglare:now(intensityLow, intensityHigh, durationLow, durationHigh, shieldDamage, windX, windY, windZ)
  Sunglare._intensityLow = intensityLow
  Sunglare._intensityHigh = intensityHigh
  Sunglare._durationLow = durationLow
  Sunglare._durationHigh = durationHigh
  Sunglare._shieldDamage = shieldDamage
  Sunglare._windX = windX
  Sunglare._windY = windY
  Sunglare._windZ = windZ
  mn.runSEXP(FADE_IN)
  Sunglare._doRand()
  Sunglare._applyShieldDamage(hv.Player)
  Sunglare._applyEMPEffect(hv.Player)
  if windX ~= nil and windY ~= nil and windZ ~= nil then
    Sunglare._enableSolarWind()
  end
  Sunglare._beginHudEffects()
end

function Sunglare:isImmanent()
  return Sunglare._immanent
end

function Sunglare._schedule()
  local token = Sunglare._token
  local delay = ba.rand32(Sunglare._delayLow, Sunglare._delayHigh)
  mn.waitAsync(delay):continueWith(function()
    if (not Sunglare._suspended) and (token == Sunglare._token) then
      mn.waitAsync(SHOW_ALERT_FOR_SECONDS):continueWith(Sunglare._trigger)
      if not JumpManager:inSubspace() then
        mn.runSEXP(SHOW_HUD_GAUGE)
        Sunglare._immanent = true
        Sunglare._frame = 0
        Sunglare._updateGauge()
      end
    end
  end)
end

function Sunglare._trigger()
  if not Sunglare._suspended then
    Sunglare._immanent = false
    Sunglare._schedule()
    mn.runSEXP(HIDE_HUD_GAUGE)
    if not JumpManager:inSubspace() then
      mn.runSEXP(FADE_IN)
	  Sunglare._doRand()
      Sunglare._applyShieldDamage(hv.Player)
      Sunglare._applyEMPEffect(hv.Player)
	  if Sunglare._windX ~= nil and Sunglare._windY ~= nil and Sunglare._windZ ~= nil then
        Sunglare._enableSolarWind()
      end
	  Sunglare._beginHudEffects()
    end
  end
end

function Sunglare._doRand()
  Sunglare._intensity = ba.rand32(Sunglare._intensityLow, Sunglare._intensityHigh)
  Sunglare._duration = ba.rand32(Sunglare._durationLow, Sunglare._durationHigh)
end

function Sunglare._updateGauge()
  if Sunglare._immanent then
    if Sunglare._frame == 0 then
      mn.runSEXP(SET_HUD_FRAME_0)
      Sunglare._frame = 1
    else
      mn.runSEXP(SET_HUD_FRAME_1)
      Sunglare._frame = 0
    end
    mn.waitAsync(ALERT_UPDATE_TIME_SECONDS):continueWith(Sunglare._updateGauge)
  end
end

function Sunglare._applyShieldDamage(ship)
  local shields = ship.Shields
  if shields:isValid() then
    local hp = shields[NONE] - Sunglare._shieldDamage
    if hp > 0 then
      shields[NONE] = hp
    else
      shields[NONE] = 0
    end
  end
end

function Sunglare._applyEMPEffect(ship)
  if ship:isValid() then
    local x = math.floor(ship.Position.x)
    local y = math.floor(ship.Position.y)
    local z = math.floor(ship.Position.z)
    mn.runSEXP(EXPLOSION_EFFECT:format(x, y, z, Sunglare._intensity, Sunglare._duration))
  end
end

function Sunglare._enableSolarWind()
  -- save the current gravity so we can put it back later
  Sunglare._savedGravity = mn.Gravity
  
  if Sunglare._windX == nil then
    Sunglare._windX = 0
  end
  if Sunglare._windY == nil then
    Sunglare._windY = 0
  end
  if Sunglare._windZ == nil then
    Sunglare._windZ = 0
  end
  local vec = ba.createVector(Sunglare._windX/100, Sunglare._windY/100, Sunglare._windZ/100)
  mn.Gravity = vec
  mn.waitAsync(Sunglare._duration):continueWith(Sunglare._disableSolarWind)
end

function Sunglare._disableSolarWind()
  mn.Gravity = Sunglare._savedGravity
  Sunglare._savedGravity = nil
end

function Sunglare._beginHudEffects()
  Sunglare._distortVal = Sunglare._duration
  Sunglare._filmGrainVal = math.floor(Sunglare._duration * 2.5)
  Sunglare._doHudEffects()
end

function Sunglare._doHudEffects()
  Sunglare._distortVal = Sunglare._distortVal - 1
  Sunglare._filmGrainVal = math.floor(Sunglare._filmGrainVal - 2.5)
  local sched = true
  
  if Sunglare._distortVal < 0 then
    Sunglare._distortVal = 0
  end
  if Sunglare._filmGrainVal < 0 then
    Sunglare._filmGrainVal = 0
  end
  if (Sunglare._filmGrainVal + Sunglare._distortVal) == 0 then
    sched = false
  end

  mn.runSEXP(DISTORT_EFFECT:format(Sunglare._distortVal))
  mn.runSEXP(FILM_GRAIN_EFFECT:format(Sunglare._filmGrainVal))
  
  if sched then
    mn.waitAsync(1):continueWith(Sunglare._doHudEffects)
  end
end

return Sunglare
