local LD = require("design.LevelDesignLibrary")
local timer = require("level.timer")
local thisObj, thisLevel, player, onCrystalFullyLitEvents, onCrystalFullyUnlitEvents, fullyLitCallbacks, fullyUnlitCallbacks, embedCallbacks, embedToActiveCallbacks, embedPoweredCallbacks, drainObjects, drainResult, aimAssistActive, deactivateFX, drainIntialFX, drainOnFX, drainOff_FX
local crystalActive = false
local startActive = false
local drainFX_LoopTimer, loopTimer, throwOverrideValue
local reticleFeedbackOn = false
local drainedThisCrystal, token
local pickupCooldownTime = 11
local pickupCooldownTimer, remainingTime
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  player = game.Player.FindPlayer()
  if obj:FindLuaTableAttribute("CrystalFullyLit") ~= nil then
    onCrystalFullyLitEvents = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("CrystalFullyLit"))
  end
  if obj:FindLuaTableAttribute("CrystalFullyUnlit") ~= nil then
    onCrystalFullyUnlitEvents = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("CrystalFullyUnlit"))
  end
  if obj:FindLuaTableAttribute("DrainObjects") ~= nil and obj:FindLuaTableAttribute("DrainObjects") ~= "" then
    drainObjects = level:FindGameObjects(tostring(obj:FindLuaTableAttribute("DrainObjects") .. "*"))
  end
  if obj:FindLuaTableAttribute("throwOverrideValue") ~= nil then
    throwOverrideValue = obj:FindLuaTableAttribute("throwOverrideValue")
  else
    throwOverrideValue = 0.7
  end
  print("throwOverrideValue", throwOverrideValue)
  drainResult = obj:GetLuaTableAttribute("DrainResult")
  startActive = obj:GetLuaTableAttribute("startActive")
  drainOnFX = obj:FindSingleGOByName("DrainFX")
  drainOff_FX = obj:FindSingleGOByName("DrainOFFState")
  drainFX_LoopTimer = StartLevelTimer(10, StartLoopFX)
  drainFX_LoopTimer:Stop()
  SoundInit()
  pickupCooldownTimer = StartLevelTimer(pickupCooldownTime, CheckForPickupTimeout)
  pickupCooldownTimer:Stop()
  game.SubObject.SetUpdateDisableDistance(thisObj, 15)
  thisObj:OnAnimationDone(thisObj, "PlayIdleAnim", {Animation = "Open", Force = true})
end
function OverrideStartActive(newValue)
  startActive = newValue
end
function OnPreStart()
  if startActive == true then
    SetActive(true)
  else
    SetActive(false)
  end
end
function OnUpdate(level, obj)
  if crystalActive then
    ReticleEnabled(true)
  elseif player:PickupIsActive("DrainEnergy") then
    ReticleEnabled(true)
  else
    ReticleEnabled(false)
  end
end
function ReticleEnabled(data)
  if data == true and not reticleFeedbackOn then
    thisObj:AddThrowableTarget({
      JointName = "aimAssistJ",
      AttachName = "BLADE_RIGHT",
      OutwardRadius = throwOverrideValue,
      ReturnRadius = 0.2,
      ClampRadius = 0,
      OverrideMaxDistance = 13.5
    })
    if not aimAssistActive then
      AddAimAssist()
    end
    reticleFeedbackOn = true
    print("Adding reticle target to drain")
  elseif data == false and reticleFeedbackOn then
    thisObj:RemoveThrowableTarget({JointName = "aimAssistJ"})
    reticleFeedbackOn = false
    print("removing reticle target to drain")
  end
end
function OnWeaponIncoming(level, go, attacker, weapon)
  print("GOT SOME INCOMING..........", attacker, weapon)
end
function AddAimAssist()
  aimAssistActive = true
  thisObj:AddAimAssist({
    Joint = "aimAssistJ",
    Angle = 12,
    MaxRadius = 1.4,
    ZoomSnapScreenAngle = 12,
    ZoomSnapRadius = 1.4,
    ZoomSnapTargetRadius = 0,
    AimSphereScreenAngle = 20,
    AimSphereRadius = 1,
    MaxDistance = 20,
    Group = "BLADES"
  })
end
function RemoveAimAssist()
  aimAssistActive = false
  thisObj:RemoveAimAssist({
    Joint = "aimAssistJ",
    Angle = 12,
    MaxRadius = 1.4,
    Group = "BLADES"
  })
end
function OnFullyLit(functionToCall)
  if fullyLitCallbacks == nil then
    fullyLitCallbacks = {}
  end
  table.insert(fullyLitCallbacks, functionToCall)
end
function FireFullyLitCallbacks()
  if fullyLitCallbacks ~= nil then
    for i = 1, #fullyLitCallbacks do
      fullyLitCallbacks[i]()
    end
  end
end
function OnFullyUnlit(functionToCall)
  if fullyUnlitCallbacks == nil then
    fullyUnlitCallbacks = {}
  end
  table.insert(fullyUnlitCallbacks, functionToCall)
end
function FireFullyUnlitCallbacks()
  if fullyUnlitCallbacks ~= nil then
    for i = 1, #fullyUnlitCallbacks do
      fullyUnlitCallbacks[i]()
    end
  end
end
function OnBladeEmbedCallbacks(functionToCall)
  if embedCallbacks == nil then
    embedCallbacks = {}
  end
  table.insert(embedCallbacks, functionToCall)
end
function FireEmbedCallback()
  if embedCallbacks ~= nil then
    for i = 1, #embedCallbacks do
      embedCallbacks[i]()
    end
  end
end
function OnBladeEmbedPoweredCallbacks(functionToCall)
  if embedPoweredCallbacks == nil then
    embedPoweredCallbacks = {}
  end
  table.insert(embedPoweredCallbacks, functionToCall)
end
function FireEmbedPoweredCallback()
  if embedPoweredCallbacks ~= nil then
    for i = 1, #embedPoweredCallbacks do
      embedPoweredCallbacks[i]()
    end
  end
end
function OnBladeEmbedActivateCallbacks(functionToCall)
  if embedToActiveCallbacks == nil then
    embedToActiveCallbacks = {}
  end
  table.insert(embedToActiveCallbacks, functionToCall)
end
function FireEmbedActivateCallback()
  if embedToActiveCallbacks ~= nil then
    for i = 1, #embedToActiveCallbacks do
      embedToActiveCallbacks[i]()
    end
  end
end
function DebugSetActive()
  SetActive(true)
end
function CheckForPickupTimeout()
  if drainedThisCrystal then
    SetActive(true)
    PlaySoundOrbActivated_TimeOut()
    pickupCooldownTimer:Reset()
    RestoreTokenState(thisObj)
    player:CallScript("LuaHook_DrainBladesOff")
  end
end
function DrainCrystal()
  if game.GetNewGamePlus() and game.GetCineNumber() < 387 then
    return
  end
  if not player:PickupIsActive("DrainEnergy") then
    player:PickupAcquire("DrainEnergy")
    player:PickupAcquire("DrainEnergyBuff")
  end
  pickupCooldownTimer:Restart()
  token = game.Token.Find({
    Holder = thisObj,
    Tag = "BifrostDrain"
  })
  token:SetHolder(player)
  HideActiveColl()
  if drainedThisCrystal == true then
    SetVisualState(2)
  end
  FireFullyUnlitCallbacks()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onCrystalFullyUnlitEvents, "CrystalFullyUnlitEvents")
  thisObj:RemoveMarker("hasDrainEnergy")
  if drainObjects ~= nil and drainObjects ~= "" then
    drainObjectsDisable()
  end
end
function ForceDrainCrystal()
  SetVisualState(2)
  HideActiveColl()
  FireFullyUnlitCallbacks()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onCrystalFullyUnlitEvents, "CrystalFullyUnlitEvents")
  thisObj:RemoveMarker("hasDrainEnergy")
  if drainObjects ~= nil and drainObjects ~= "" then
    drainObjectsDisable()
  end
end
function CrystalFullyLit()
  if drainObjects ~= nil and drainObjects ~= "" then
    drainObjectsEnable()
  end
  FireFullyLitCallbacks()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onCrystalFullyLitEvents, "CrystalFullyLitEvents")
end
function CrystalFullyUnlit()
  if drainObjects ~= nil and drainObjects ~= "" then
    print("tbd")
  end
  FireFullyUnlitCallbacks()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onCrystalFullyUnlitEvents, "CrystalFullyUnlitEvents")
end
function drainObjectsEnable()
  if drainObjects ~= nil and drainObjects ~= "" then
    for i = 1, #drainObjects do
      if drainResult == "PlayAnimToEnd" then
        drainObjects[i]:JumpAnimToFrame(0)
        drainObjects[i]:PlayAnimToEnd()
      elseif drainResult == "PlayAnimCycle" then
        drainObjects[i]:PlayAnimCycle()
      elseif drainResult == "None" then
        print("not doing anythin bub")
      end
    end
  end
end
function drainObjectsDisable()
  if drainObjects ~= nil and drainObjects ~= "" then
    for i = 1, #drainObjects do
      if drainResult ~= "PlayAnimToEnd" then
      end
      if drainObjects[i].AnimFrame ~= nil then
        drainObjects[i]:PauseAnim()
      else
        print("not doing anythin bub")
      end
    end
  end
end
function SetActive(active)
  crystalActive = active
  if active == true then
    pickupCooldownTimer:Reset()
    drainedThisCrystal = false
    SetVisualState(1)
    CrystalFullyLit()
    ShowActiveColl()
    CreateToken(thisObj)
  else
    SetVisualState(0)
    HideActiveColl()
  end
end
function ShowActiveColl()
  thisObj:ShowJoint(thisObj:GetJointIndex("DrainActiveColl"))
  thisObj:HideJoint(thisObj:GetJointIndex("DrainNotActiveColl"))
end
function HideActiveColl()
  thisObj:HideJoint(thisObj:GetJointIndex("DrainActiveColl"))
  thisObj:ShowJoint(thisObj:GetJointIndex("DrainNotActiveColl"))
end
function SetVisualState(data)
  if data == 0 then
    print("DRAIN: VISUAL STATE 0")
    thisObj:StartAnim("Close")
    thisObj:StartMaterialAnim("drainPowerOff")
    thisObj:PlayAnimToEnd()
    drainOnFX:Hide()
    RemoveInitialFX()
    drainOff_FX:Show()
    PlaySoundOrbDeactivated()
    StopSoundOrbActiveIdleLoop()
    PlaySoundOrbInactiveIdleLoop()
  elseif data == 1 then
    print("DRAIN: VISUAL STATE 1")
    thisObj:StartAnim("Open")
    thisObj:SetAnimRate(6)
    thisObj:StartMaterialAnim("drainPowerOn")
    thisObj:PlayAnimToEnd()
    if deactivateFX ~= nil then
      deactivateFX:HideParticleEmitter()
      deactivateFX:Hide()
    end
    SpawnIntialHitFX()
    drainFX_LoopTimer:Restart()
    drainOff_FX:Hide()
    StopSoundOrbInactiveIdleLoop()
    PlaySoundOrbActiveIdleLoop()
  elseif data == 2 then
    print("DRAIN: VISUAL STATE 2")
    SpawnDeActivateFX()
    drainOff_FX:Show()
    drainFX_LoopTimer:Stop()
    thisObj:StartAnim("Close")
    thisObj:StartMaterialAnim("drainPowerOff")
    thisObj:PlayAnimToEnd()
    crystalActive = false
    PlaySoundOrbDeactivated()
    StopSoundOrbActiveIdleLoop()
    PlaySoundOrbInactiveIdleLoop()
  end
end
function PlayIdleAnim()
  if crystalActive then
    print("Playing Drain idle anim")
    thisObj:StartAnim("Idle")
    thisObj:PlayAnimCycle()
    thisObj:SetAnimRate(-7)
  end
  thisObj:OnAnimationDone(thisObj, "PlayIdleAnim", {Animation = "Open", Force = true})
end
function GetActive()
  return crystalActive
end
function CreateToken(obj)
  token = game.Token.Find({
    Owner = obj,
    Tag = "BifrostDrain"
  })
  if token == nil then
    print("TOKEN: CREATING A NEW TOKEN FOR OBJ")
    token = game.Token.New({
      Owner = obj,
      Holder = obj,
      Tag = "BifrostDrain"
    })
  end
end
function GiveTokenToObject(obj)
  token = game.Token.Find({
    Holder = player,
    Tag = "BifrostDrain"
  })
  if token ~= nil then
    print("TOKEN: Setting OWNER and HOLDER", token, thisObj)
    token:SetOwner(thisObj)
    token:SetHolder(thisObj)
  end
end
function RestoreTokenState(obj)
  token = game.Token.Find({
    Holder = player,
    Tag = "BifrostDrain"
  })
  if token ~= nil and token:GetOwner() == obj and token:GetHolder() ~= obj then
    print("TOKEN: Pickup timed out, resetting OWNER and HOLDER", token, player)
    token:SetHolder(obj)
    return
  end
end
function OnTokenLoseOwnership(level, go, token)
  SetActive(false)
  token:Delete(thisObj)
  if deactivateFX ~= nil then
    deactivateFX:Hide()
  end
  drainedThisCrystal = false
  print("TOKEN: lost ownership of the token")
end
function OnTokenDropped(level, go, token)
  token:Delete(go)
end
function OnBladePull()
  print("DRAIN: I GOT DRAIN PULL MSG")
  local token = game.Token.Find({
    Holder = player,
    Tag = "BifrostDrain"
  })
  if token == nil and not player:PickupIsActive("DrainEnergy") and crystalActive then
    drainedThisCrystal = true
    DrainCrystal()
    print("I WAS ATTACHED!!!")
  end
end
function OnDrainReturn()
  print("DRAIN: I GOT DRAIN RETURN MSG")
  if not crystalActive then
    local token = game.Token.Find({
      Holder = player,
      Tag = "BifrostDrain"
    })
    if token ~= nil then
      GiveTokenToObject(thisObj)
      pickupCooldownTimer:Reset()
      SetActive(true)
      PlaySoundOrbActivated()
      if player:PickupIsActive("DrainEnergyBuff") then
        player:PickupRelinquish("DrainEnergyBuff")
      end
      if player:PickupIsActive("DrainEnergy") then
        player:PickupRelinquish("DrainEnergy")
        player:CallScript("LuaHook_DrainBladesOff")
      end
    end
  end
end
function OnHitByWeapon(level, obj, weapon, attacker)
  if weapon:GetName() == "axe00" then
    game.Audio.IncrementBanterFact("HitGatedObject", 1, 10)
    game.Audio.PlayBanterNonCritical("ca_gear_gated")
  end
end
function OnWeaponEmbed(level, go, attacker, weapon)
  if weapon:GetName() == "explosive00" then
    print("DRAIN: WEAPON EMBED IS....", weapon)
    if not crystalActive then
      if not player:PickupIsActive("DrainEnergy") then
        LD.ExtractAndExecuteCallbacksForEvent(level, thisObj, thisObj:GetLuaTableAttribute("onBladeEmbed"), "Blade Embedded")
        print("FIRING EMBED CALLBACK")
        FireEmbedCallback()
      else
        FireEmbedActivateCallback()
        PlaySoundOrbHit()
        StopSoundHelwindTimer()
      end
    elseif not (not crystalActive or player:PickupIsActive("DrainEnergy")) or not crystalActive and player:PickupIsActive("DrainEnergy") then
      LD.ExtractAndExecuteCallbacksForEvent(level, thisObj, thisObj:GetLuaTableAttribute("onBladeEmbedPowered"), "Blade EmbeddedPowered")
      print("FIRING EMBED Powered CALLBACK")
      FireEmbedPoweredCallback()
      PlaySoundOrbHit()
    end
  end
end
function SpawnIntialHitFX()
  if deactivateFX ~= nil then
    deactivateFX:HideParticleEmitter()
    deactivateFX:Hide()
  end
  print("DRAIN: Playing Inital FX......................................................")
  drainIntialFX = game.FX.Spawn("Drain_Object_Active", thisLevel, {
    GameObject = thisObj,
    Joint = "aimAssistJ",
    Follow = true,
    AutoDelete = true
  })
end
function StartLoopFX()
  print("DRAIN: Playing Loop FX......................................................")
  RemoveInitialFX()
  drainOnFX:Show()
end
function SpawnDeActivateFX()
  RemoveInitialFX()
  drainOnFX:Hide()
  deactivateFX = game.FX.Spawn("Drain_EnergyDEactivate", thisLevel, {
    GameObject = thisObj,
    Joint = "aimAssistJ",
    Follow = true,
    AutoDelete = true
  })
end
function RemoveInitialFX()
  if drainIntialFX ~= nil and drainIntialFX.Spawned then
    drainIntialFX:Remove()
  end
end
function HideObjectCollision()
  thisObj:HideCollision()
end
function ShowObjectCollision()
  thisObj:ShowCollision()
end
function OnSaveCheckpoint(level)
  return {temp_crystalActive = crystalActive, drainedThisCrystal = drainedThisCrystal}
end
function OnRestoreCheckpoint(level, go, tab)
  crystalActive = tab.temp_crystalActive
  drainedThisCrystal = tab.drainedThisCrystal
  if not drainedThisCrystal then
    if crystalActive then
      startActive = true
    else
      startActive = false
    end
  else
    startActive = true
  end
end
local SND_DrainCrystal, SND_Kratos
function SoundInit()
  SND_DrainCrystal = thisObj:FindSingleSoundEmitterByName("SNDBifrostDrain")
  SND_Kratos = player:FindSingleSoundEmitterByName("SNDKratos")
end
function PlaySoundOrbActiveIdleLoop()
  LD.PlayRestartableSoundLoop(SND_DrainCrystal, "SND_MAG_HelDrain_Orb_Active_LP")
end
function StopSoundOrbActiveIdleLoop()
  LD.StopRestartableSoundLoop(SND_DrainCrystal, "SND_MAG_HelDrain_Orb_Active_LP")
end
function PlaySoundOrbInactiveIdleLoop()
  LD.PlayRestartableSoundLoop(SND_DrainCrystal, "SND_MAG_HelDrain_Orb_Inactive_LP")
end
function StopSoundOrbInactiveIdleLoop()
  LD.StopRestartableSoundLoop(SND_DrainCrystal, "SND_MAG_HelDrain_Orb_Inactive_LP")
end
function PlaySoundOrbActivated()
  LD.PlaySound(SND_DrainCrystal, "SND_MAG_HelDrain_Orb_Start")
end
function PlaySoundOrbActivated_TimeOut()
  LD.PlaySound(SND_DrainCrystal, "SND_MAG_HelDrain_Orb_Start_FadeIn")
  LD.PlaySound(SND_Kratos, "SND_MAG_Hel_Drain_Blades_Expire_Whoosh")
end
function PlaySoundOrbDeactivated()
  LD.PlaySound(SND_DrainCrystal, "SND_MAG_HelDrain_Orb_Stop")
end
function PlaySoundOrbHit()
  LD.PlaySound(SND_DrainCrystal, "SND_MAG_HelDrain_Orb_Hit")
end
function StopSoundHelwindTimer()
  LD.StopSound(SND_Kratos, "SND_MAG_Blades_HelDrain_Timer", true)
end
