local LD = require("design.LevelDesignLibrary")
local startEnabled, enabled
local hasInteracted = false
local frontInteractZone, thisLevel, thisObj, playerRef, lockCount, FinalEvent
locked = nil
local locksUnlocked = 0
local forceLock = false
local promptPlacementMove = "BRA_PutDownMidEnter"
local EventTable = {}
local LockResource, resourceRecipe, synchContainer, finalOrb, KeyRunes03, KeyRunes05, pedestalArt, orbOriginalPosition
local BuildEventData = function(index)
  local EventHeaderStr = "Event" .. tostring(index) .. "_"
  local animStr = EventHeaderStr .. "AnimObjects"
  local preString = EventHeaderStr .. "PreAnimCallbacks"
  local postString = EventHeaderStr .. "PostAnimCallbacks"
  local AnimDoneStr = "AnimObjAnimationCallback"
  local AnimObjectsList
  if thisObj:FindLuaTableAttribute(animStr) ~= nil then
    AnimObjectsList = LD.ConvertStringListToTable(thisObj:FindLuaTableAttribute(animStr))
  end
  local animObjects = {}
  for _, x in ipairs(AnimObjectsList) do
    if thisLevel.GameObjects ~= nil and thisLevel.GameObjects[x] ~= nil then
      thisLevel.GameObjects[x]:PauseAnimation()
      thisLevel.GameObjects[x]:OnAnimationDone(thisObj, AnimDoneStr)
      table.insert(animObjects, thisLevel.GameObjects[x])
    elseif thisLevel:FindSingleGameObject(x) ~= nil then
      local newObj = thisLevel:FindSingleGameObject(x)
      newObj:PauseAnimation()
      newObj:OnAnimationDone(thisObj, AnimDoneStr)
      table.insert(animObjects, newObj)
    end
  end
  local preCallbacks
  if thisObj:FindLuaTableAttribute(preString) ~= nil then
    preCallbacks = LD.ExtractCallbacksForEvent(thisLevel, thisObj, thisObj:FindLuaTableAttribute(preString))
  end
  local postCallbacks
  if thisObj:FindLuaTableAttribute(postString) ~= nil then
    postCallbacks = LD.ExtractCallbacksForEvent(thisLevel, thisObj, thisObj:FindLuaTableAttribute(postString))
  end
  return {
    AnimObjects = animObjects,
    AnimCount = #animObjects + 1,
    PreAnimCallbackStr = preString,
    PreAnimCallbacks = preCallbacks,
    PostAnimCallbackStr = preString,
    PostAnimCallbacks = postCallbacks,
    ObjectsDoneAnimating = 0,
    FirePreAnimCallbacks = function(self)
      if thisObj:FindLuaTableAttribute(self.PreAnimCallbackStr) then
        LD.ExecuteCallbacksForEvent(thisLevel, thisObj, self.PreAnimCallbacks, self.PreAnimCallbackStr)
      end
    end,
    FirePostAnimCallbacks = function(self)
      if thisObj:FindLuaTableAttribute(self.PostAnimCallbackStr) then
        LD.ExecuteCallbacksForEvent(thisLevel, thisObj, self.PostAnimCallbacks, self.PostAnimCallbackStr)
      end
    end,
    AnimationCallback = function(self)
      self.ObjectsDoneAnimating = self.ObjectsDoneAnimating + 1
      if self.ObjectsDoneAnimating >= self.AnimCount then
        self:FirePostAnimCallbacks()
      end
    end
  }
end
function OnScriptLoaded(level, go)
  thisLevel = level
  thisObj = go
  playerRef = game.Player.FindPlayer()
  frontInteractZone = LD.CreateInteractZone_Standard_360(go, "promptJointFront")
  lockCount = go:GetLuaTableAttribute("LockCount")
  pedestalArt = go:FindSingleGOByName("key_device")
  assert(pedestalArt, "Cannot find art module for the stone pedestal.")
  assert(pedestalArt.Child)
  synchContainer = thisObj:FindSingleGOByName("synchJointContainer")
  pedestalArt = pedestalArt.Child
  for i = 1, lockCount do
    table.insert(EventTable, BuildEventData(i))
  end
  if go:FindLuaTableAttribute("FinalEvent") ~= nil then
    FinalEvent = LD.ExtractCallbacksForEvent(thisLevel, thisObj, go:FindLuaTableAttribute("FinalEvent"))
  end
  if go:FindLuaTableAttribute("LockResource") ~= nil then
    LockResource = go:FindLuaTableAttribute("LockResource")
  end
  resourceRecipe = LockResource .. "_Unlock"
  KeyRunes03 = pedestalArt:FindSingleGOByName("KeyRunes03")
  KeyRunes05 = pedestalArt:FindSingleGOByName("KeyRunes05")
  finalOrb = thisObj:FindSingleGOByName("KeyOrbAnim")
  startEnabled = go:GetLuaTableAttribute("StartEnabled")
  if startEnabled then
    Enable()
  elseif startEnabled == false then
    Disable()
  end
  SoundInit()
  game.SubObject.SetUpdateDisableDistance(thisObj, 11)
end
function OnFirstStart(level, go)
  pedestalArt:JumpAnimationToFrame(0, {
    Animation = "key_device_glow_on"
  })
  pedestalArt:PauseAnimation({
    Animation = "key_device_glow_on"
  })
  if lockCount == 1 then
    pedestalArt:JumpAnimationToFrame(80, {
      Animation = "key_device_rotate_discs"
    })
  else
    pedestalArt:JumpAnimationToFrame(0, {
      Animation = "key_device_rotate_discs"
    })
    pedestalArt:PauseAnimation({
      Animation = "key_device_rotate_discs"
    })
  end
  finalOrb:JumpAnimationToFrame(0)
  finalOrb:PauseAnimation()
  finalOrb:Hide()
  for _, rune in pairs(KeyRunes03.Children) do
    rune:JumpAnimationToFrame(0)
    rune:PauseAnimation()
  end
  for _, rune in pairs(KeyRunes05.Children) do
    rune:JumpAnimationToFrame(0)
    rune:PauseAnimation()
  end
  if lockCount == 5 then
    KeyRunes03:Hide()
  elseif lockCount == 3 then
    KeyRunes05:Hide()
  else
    KeyRunes03:Hide()
    KeyRunes05:Hide()
  end
end
function OnStart(level, go)
  if locksUnlocked >= lockCount then
    finalOrb:Show()
    Disable()
    PlayPedestalLoopSound()
  else
    pedestalArt:JumpAnimationToFrame(0, {
      Animation = "key_device_glow_on"
    })
    pedestalArt:PauseAnimation({
      Animation = "key_device_glow_on"
    })
    orbOriginalPosition = finalOrb:GetWorldPosition()
  end
end
function OnUpdate(level, go)
  if not enabled or locksUnlocked >= lockCount then
    game.SubObject.Sleep(thisObj)
    return
  end
  if game.Wallets.GetResourceValue("HERO_SAVEONLY", LockResource) > 0 and frontInteractZone:IsLocked() and not forceLock then
    Unlock()
  elseif game.Wallets.GetResourceValue("HERO_SAVEONLY", LockResource) <= 0 and frontInteractZone:IsUnlocked() then
    Lock()
  end
end
function OnUseWorld(level, go)
  if enabled and frontInteractZone:PlayerCanInteract() and game.Wallets.CanPurchase("HERO_SAVEONLY", resourceRecipe) then
    local zeroToPlayer = (playerRef:GetWorldPosition() - thisObj:GetWorldJointPosition(thisObj:GetJointIndex("zeroJoint"))):Normalized()
    local approachRadius = 1.6
    local synchForward = -zeroToPlayer
    local rotationOffset = 7
    synchForward = synchForward:RotateXZ(rotationOffset)
    synchContainer:SetWorldFacing(synchForward)
    synchContainer:SetWorldPosition(thisObj:GetWorldJointPosition(thisObj:GetJointIndex("zeroJoint")) + zeroToPlayer * approachRadius)
    PlayInteractSound()
    LockCheck()
  end
end
function AnimateRune(runeNum)
  if lockCount == 1 then
    return
  end
  local rune
  if lockCount == 5 then
    local runeList = {
      "rune01b",
      "rune02",
      "rune03b",
      "rune04"
    }
    rune = KeyRunes05:FindSingleGOByName(runeList[runeNum])
  elseif lockCount == 3 then
    local runeList = {"rune01", "rune03"}
    rune = KeyRunes03:FindSingleGOByName(runeList[runeNum])
  end
  rune:JumpAnimationToFrame(0)
  rune:PlayAnimationToEnd()
  rune:OnAnimationDone(thisObj, "UnlockPedestal")
end
function AnimObjAnimationCallback()
  EventTable[locksUnlocked]:AnimationCallback()
end
function DiscAnimationCallback()
  EventTable[locksUnlocked]:AnimationCallback()
  if locksUnlocked < lockCount then
    AnimateRune(locksUnlocked)
  else
    UnlockPedestal()
  end
end
function LuaHook_LockCheckCallback()
  EventTable[locksUnlocked]:FirePreAnimCallbacks()
  for _, anim in pairs(EventTable[locksUnlocked].AnimObjects) do
    anim:PlayAnimationToEnd()
  end
  finalOrb:SetWorldPosition(orbOriginalPosition)
  finalOrb:JumpAnimationToFrame(0)
  finalOrb:Show()
  PlayKeyPlaceSound()
  if locksUnlocked >= lockCount then
    Disable()
    finalOrb:JumpAnimationToFrame(10)
    finalOrb:PauseAnimation()
    finalOrb:ClearAllAnimationCallbacks()
    if lockCount == 1 then
      pedestalArt:PlayAnimationToFrame(0, {
        Animation = "key_device_rotate_discs",
        Rate = -1
      })
      game.FX.Spawn("stone_pedestal_fx", thisLevel, {GameObject = thisObj, AutoDelete = true})
      pedestalArt:OnAnimationDone(thisObj, "DiscAnimationCallback", {
        Animation = "key_device_rotate_discs"
      })
      PlayPedestalSound()
    else
      UnlockPedestal()
    end
  else
    forceLock = true
    Lock()
    finalOrb:JumpAnimationToFrame(8)
    finalOrb:PlayAnimationToEnd()
    local rate, frame
    local playForwards = locksUnlocked % 2 == 1
    if playForwards then
      frame = 80
      rate = 1
    else
      frame = 0
      rate = -1
    end
    pedestalArt:PlayAnimationToFrame(frame, {
      Animation = "key_device_rotate_discs",
      Rate = rate
    })
    game.FX.Spawn("stone_pedestal_fx", thisLevel, {GameObject = thisObj, AutoDelete = true})
    pedestalArt:OnAnimationDone(thisObj, "DiscAnimationCallback", {
      Animation = "key_device_rotate_discs"
    })
    PlayPedestalSound()
  end
end
function UnlockPedestal()
  if locksUnlocked >= lockCount then
    PlayPedestalLoopSound()
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, FinalEvent, "FinalEvent")
    hasInteracted = true
    pedestalArt:JumpAnimationToFrame(0, {
      Animation = "key_device_glow_on"
    })
    pedestalArt:PlayAnimationToEnd({
      Animation = "key_device_glow_on"
    })
    if FinalEvent == nil or FinalEvent == "" then
      pedestalArt:OnAnimationDone(thisObj, "FinishPedestalInteraction", {
        Animation = "key_device_glow_on"
      })
    end
  elseif EventTable[locksUnlocked].PostAnimCallbacks == nil or EventTable[locksUnlocked].PostAnimCallbacks == "" then
    FinishPedestalInteraction()
  end
end
function FinishPedestalInteraction()
  if game.Wallets.GetResourceValue("HERO_SAVEONLY", LockResource) > 0 then
    Unlock()
  end
  forceLock = false
  game.Wallets.Craft("HERO_SAVEONLY", resourceRecipe)
  local nifCheck = game.FindLevel("Nid100_Entrance")
  if nifCheck == nil then
    LD.CallFunctionAfterDelay(function()
      game.World.StoreCheckpoint()
    end, 0.1)
  end
end
function LockCheck(level, go)
  locksUnlocked = locksUnlocked + 1
  playerRef:RequestInteract(thisObj, {InteractZone = frontInteractZone, LerpObject = finalOrb})
  print("LOCK CHECK!")
end
function OnInteractStart(level, go)
  local args = {completion_percentage = 0.6}
  LD.PlaySingleSynchMove_KratosObject(synchContainer, "synchJoint", "Place key stone", promptPlacementMove, "", frontInteractZone, false, "Bare", args)
end
function Lock()
  locked = true
  frontInteractZone:Lock()
end
function Unlock()
  locked = false
  frontInteractZone:Unlock()
end
function Enable()
  Unlock()
  enabled = true
  frontInteractZone:Enable()
end
function Disable()
  enabled = false
  frontInteractZone:Disable()
end
function OnSaveCheckpoint(level)
  print("Chest - Pickle")
  return {enabled = enabled, locksUnlocked = locksUnlocked}
end
function OnRestoreCheckpoint(level, go, tab)
  print("Chest - Unpickle")
  enabled = tab.enabled
  locksUnlocked = tab.locksUnlocked
  if hasInteracted then
    frontInteractZone:Disable()
  end
end
function LuaHook_ShowOrb()
  if finalOrb ~= nil then
    finalOrb:JumpAnimationToFrame(0)
    finalOrb:PauseAnimation()
    finalOrb:SetWorldPosition(playerRef:GetWorldJointPosition(playerRef:GetJointIndex("linkJoint")))
    finalOrb:Show()
  end
end
local soundEvents = {
  soundEmitter = nil,
  soundInteract = "",
  soundPedestal = "SND_MECH_Keystone_Activate",
  soundPedestalLoop = "SND_MECH_Keystone_Activate_LP",
  soundKeyPlace = "SND_MECH_Keystone_Activate_Key_Insert"
}
function SoundInit()
  soundEvents.soundEmitter = thisObj:FindSingleSoundEmitterByName("SNDStonePedestal")
  soundEvents.soundInteract = thisObj:FindLuaTableAttribute("soundInteract")
end
function SoundSetup(sounds)
  if sounds ~= nil then
    if sounds.soundEmitter ~= nil then
      soundEvents.soundEmitter = sounds.soundEmitter
    end
    for key, value in pairs(soundEvents) do
      for newKey, newValue in pairs(sounds) do
        if newKey == key and newValue ~= nil and newValue ~= value then
          soundEvents[key] = newValue
        end
      end
      LD.SoundDebug(tostring(key) .. ": " .. tostring(soundEvents[key]))
    end
  end
end
function PlayInteractSound()
  LD.PlaySound(soundEvents.soundEmitter, soundEvents.soundInteract)
end
function PlayPedestalSound()
  LD.PlaySound(soundEvents.soundEmitter, soundEvents.soundPedestal)
end
function PlayPedestalLoopSound()
  print("PLAYING LOOP")
  LD.PlayRestartableSoundLoop(soundEvents.soundEmitter, soundEvents.soundPedestalLoop)
end
function PlayKeyPlaceSound()
  print("PLAYING KEY PLACE")
  LD.PlaySound(soundEvents.soundEmitter, soundEvents.soundKeyPlace)
end
