local LD = require("design.LevelDesignLibrary")
local color = require("core.color")
local TUT = require("game.GlobalTutorials")
local thisLevel, thisObj, keyType, frontInteractZone, chestScript, chestSealsBrokenEvent, interactFunction
local keysUsed = 0
local challengeComplete = false
local runeTable = {}
local onBrokenCallbacks = {}
local isRevealedByLight = false
local isLit = false
local hideRunes = false
local HintBanterEnabled = false
local isDim = false
local debug_runesOn = false
local state
local interactAvailable = true
local states = {
  ENABLED = 1,
  DISABLED = 2,
  LOCKED = 3,
  OPENED = 4
}
function OnScriptLoaded(level, go)
  thisLevel = level
  thisObj = go
  frontInteractZone = LD.CreateInteractZone_Standard_180(go, "promptJointFront")
  frontInteractZone:SetHintXZRange(3)
  frontInteractZone:SetPromptJoint("promptJointLoc")
  LD.OverrideInteractZoneTags(frontInteractZone, "NotWhileSonInteracting", true)
  game.Interact.DisableTags("RunicChest_CustomConditions")
  keyType = go:GetLuaTableAttribute("KeyType")
  isRevealedByLight = go:GetLuaTableAttribute("RevealedByLight")
  hideRunes = go:GetLuaTableAttribute("HideChestSymbols")
  HintBanterEnabled = go:GetLuaTableAttribute("HintBanterEnabled")
  SoundInit()
  chestSealsBrokenEvent = thisObj:FindLuaTableAttribute("chestSealsBrokenEvent")
  if chestSealsBrokenEvent ~= nil then
    chestSealsBrokenEvent = LD.ExtractCallbacksForEvent(thisLevel, thisObj, chestSealsBrokenEvent)
  end
  function onBrokenCallbacks.Key01Broken()
    OnKeyBroken(1)
  end
  function onBrokenCallbacks.Key02Broken()
    OnKeyBroken(2)
  end
  function onBrokenCallbacks.Key03Broken()
    OnKeyBroken(3)
  end
  runeTable[1] = {
    successRune = 17,
    runeVisual = nil,
    baseSuccess = {
      false,
      false,
      false
    },
    lastState = nil,
    runeKey = nil,
    onBreakableDestroyed = nil
  }
  runeTable[2] = {
    successRune = 21,
    runeVisual = nil,
    baseSuccess = {
      false,
      false,
      false
    },
    lastState = nil,
    runeKey = nil,
    onBreakableDestroyed = nil
  }
  runeTable[3] = {
    successRune = 13,
    runeVisual = nil,
    baseSuccess = {
      false,
      false,
      false
    },
    lastState = nil,
    runeKey = nil,
    onBreakableDestroyed = nil
  }
  for i = 1, 3 do
    if keyType == "MemoryChest" then
      runeTable[i].successRune = thisObj:GetLuaTableAttribute("SuccessRune_0" .. tostring(i))
    end
    runeTable[i].runeKey = level:FindSingleGameObject(thisObj:FindLuaTableAttribute("sealBreakable0" .. tostring(i)))
    runeTable[i].runeVisual = thisObj:FindSingleGOByName("keyRune0" .. tostring(i))
    runeTable[i].onBreakableDestroyed = thisObj:FindLuaTableAttribute("Event_Breakable0" .. tostring(i) .. "Destroyed")
    if runeTable[i].onBreakableDestroyed ~= nil then
      runeTable[i].onBreakableDestroyed = LD.ExtractCallbacksForEvent(thisLevel, thisObj, runeTable[i].onBreakableDestroyed)
    end
  end
  chestScript = thisObj:FindSingleGOByName("ChestScript_RN").Child.LuaObjectScript
  game.SubObject.Sleep(go)
end
function OnPreStart(level, obj)
  for i = 1, 3 do
    if runeTable[1].runeKey and runeTable[2].runeKey and runeTable[3].runeKey then
      if keyType == "Breakable" then
        if runeTable[i].runeKey.LuaObjectScript.OnBroken ~= nil then
          runeTable[i].runeKey.LuaObjectScript.OnBroken(onBrokenCallbacks["Key0" .. tostring(i) .. "Broken"])
        end
      elseif keyType == "Bell" then
        runeTable[i].runeKey.LuaObjectScript.RegisterStartRingingCallback(function()
          OnBellStartRinging(runeTable[i].runeVisual, i)
        end)
        runeTable[i].runeKey.LuaObjectScript.RegisterStopRingingCallback(function()
          OnBellCooldownElapsed(runeTable[i].runeVisual, i)
        end)
      elseif keyType == "MemoryChest" then
        runeTable[i].runeKey.LuaObjectScript.RegisterOnRotateCallback(OnRotateCallback)
      end
    end
    runeTable[i].runeVisual.LuaObjectScript.SelectRune(runeTable[i].successRune)
    runeTable[i].lastState = runeTable[i].runeVisual.LuaObjectScript.IsEnabled()
  end
end
function OnFirstStart(level, obj)
  for i = 1, 3 do
    if hideRunes or isRevealedByLight then
      runeTable[i].runeVisual.LuaObjectScript.HideRune()
    end
    if challengeComplete == false then
      runeTable[i].runeVisual.LuaObjectScript.Enable()
    end
  end
end
function OnStart(level, go)
  if state == states.ENABLED then
    Enable()
  elseif state == states.DISABLED then
    Disable()
  elseif state == states.LOCKED then
    Lock()
  elseif state == states.OPENED then
    Disable()
    return
  else
    chestScript.Disable()
    frontInteractZone:Enable()
  end
  if interactAvailable == false then
    DisableInteractBehaviors()
  else
    EnableInteractBehaviors()
  end
  if keyType == "MemoryChest" then
    OnRotateCallback()
  end
end
function OnSaveCheckpoint(level, go)
  return {
    state = state,
    keysUsed = keysUsed,
    challengeComplete = challengeComplete,
    interactAvailable = interactAvailable
  }
end
function OnRestoreCheckpoint(level, go, savedInfo)
  state = savedInfo.state
  keysUsed = savedInfo.keysUsed
  challengeComplete = savedInfo.challengeComplete
  interactAvailable = savedInfo.interactAvailable
end
function OnUseWorld(level, go)
  if frontInteractZone:PlayerCanInteract() then
    PerformKratosInteraction_Locked()
  end
end
function OnInteractStart(level, obj, creature)
  interactFunction()
end
function OnInteractAbort(level, obj, creature)
end
function OnInteractFinish(level, obj, creature)
  state = states.OPENED
end
function OnInteractDone()
end
function PerformKratosInteraction_Locked()
  Lock()
  LD.CallFunctionAfterDelay(function()
    TUT.RunicLock_Tutorial()
  end, 1)
  TripleChestLockedBanter()
  PlayLockMusic()
  PlayLockSound()
end
function Enable()
  if state ~= states.OPENED then
    state = states.ENABLED
    frontInteractZone:Disable()
    chestScript.Enable()
    chestScript.UnlockChest()
  end
end
function Reset()
  keysUsed = 0
  challengeComplete = false
  chestScript.Reset()
  state = nil
  chestScript.Disable()
  frontInteractZone:Enable()
  for i = 1, 3 do
    runeTable[i].runeVisual.LuaObjectScript.Enable()
  end
end
function Disable()
  state = states.DISABLED
  frontInteractZone:Disable()
  chestScript.Disable()
end
function Lock()
  state = states.LOCKED
  frontInteractZone:Disable()
  chestScript.Enable()
  chestScript.LockChest()
end
function GetInteractZone()
  return chestScript.GetInteractZone()
end
function GetFakeInteractZone()
  return frontInteractZone
end
function RunicSoftSave()
  game.SubObject.SoftSave(thisObj)
  if keyType == "MemoryChest" then
    for i = 1, 3 do
      if runeTable[i].runeKey then
        runeTable[i].runeKey.LuaObjectScript.SoftSave()
      end
    end
  end
end
function ResolveRunicSoftSave()
  for i = 1, 3 do
    if runeTable[i].runeKey then
      if keyType == "Breakable" then
        runeTable[i].runeKey:Destroy()
      elseif keyType == "Bell" then
        runeTable[i].runeKey.LuaObjectScript.SoftSaveCleanup()
      elseif keyType == "MemoryChest" then
        runeTable[i].runeKey.LuaObjectScript.ResolveSoftSave()
      end
    end
  end
end
function ForceStateDisabled()
  state = states.DISABLED
end
function HideChest()
  chestScript.HideChest()
  for i = 1, 3 do
    runeTable[i].runeVisual.LuaObjectScript.HideRune()
  end
end
function ShowChest()
  chestScript.ShowChest(state ~= states.ENABLED)
  if state == states.ENABLED then
    chestScript.Enable()
  elseif state == states.LOCKED then
    Lock()
  end
  for i = 1, 3 do
    runeTable[i].runeVisual.LuaObjectScript.ShowRune()
  end
end
function OverrideRuneNumber(i, runeNumber)
  runeTable[i].successRune = runeNumber
  runeTable[i].runeVisual.LuaObjectScript.SelectRune(runeNumber)
  runeTable[i].runeVisual.LuaObjectScript.ShowRune()
  runeTable[i].baseSuccess = {
    false,
    false,
    false
  }
  runeTable[i].lastState = runeTable[i].runeVisual.LuaObjectScript.IsEnabled()
end
function DisableInteractBehaviors()
  interactAvailable = false
  LD.OverrideInteractZoneTags(frontInteractZone, "RunicChest_CustomConditions", true)
  LD.OverrideInteractZoneTags(chestScript.GetInteractZone(), "RunicChest_CustomConditions", true)
end
function EnableInteractBehaviors()
  interactAvailable = true
  LD.OverrideInteractZoneTags(frontInteractZone, "RunicChest_CustomConditions", false)
  LD.OverrideInteractZoneTags(chestScript.GetInteractZone(), "RunicChest_CustomConditions", false)
end
function PlayRuneOnFX(runeID)
  local jointName = "key_FX_Location_0" .. tostring(runeID)
  game.FX.Spawn("FX_Rune_on", nil, {
    AutoDelete = true,
    GameObject = thisObj,
    Joint = jointName
  })
  if keyType ~= "MemoryChest" then
    chestScript.PlaySoundRuneAppear()
  end
end
function PlayRuneOffFX(runeID)
  local jointName = "key_FX_Location_0" .. tostring(runeID)
  game.FX.Spawn("FX_Rune_off", nil, {
    AutoDelete = true,
    GameObject = thisObj,
    Joint = jointName
  })
  if keyType ~= "MemoryChest" then
    chestScript.PlaySoundRuneDisappear()
  end
end
function OnKeyBroken(runeIndex)
  local i = runeIndex
  keysUsed = keysUsed + 1
  PlayRuneOffFX(runeIndex)
  runeTable[i].runeVisual.LuaObjectScript.Disable()
  if keyType == "Breakable" then
    TUT.RunicProgress_Tutorial(keysUsed)
    BreakableBrokenBanter(keysUsed)
  end
  CheckKeys()
  PlayProgressSound()
  PlaySealBrokenMusic()
  if runeTable[i].onBreakableDestroyed ~= nil then
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, runeTable[i].onBreakableDestroyed, "Brk 0" .. tostring(i) .. " Destroyed")
  end
  if game.Loot.AlertTripleChestBreakableBroken ~= nil then
    game.Loot.AlertTripleChestBreakableBroken(runeIndex)
  end
end
function CheckKeys()
  if keysUsed == 3 then
    challengeComplete = true
    Enable()
    if chestSealsBrokenEvent ~= nil then
      LD.ExecuteCallbacksForEvent(thisLevel, thisObj, chestSealsBrokenEvent, "chestSealsBrokenEvent")
    end
    if keyType == "Breakable" then
      game.Audio.PlaySound("SND_LOOT_Chest_Runic_Puzzle_Complete")
    end
    PlayUnlockMusic()
    PlayUnlockSound()
  end
end
function OnBellStartRinging(runeVisual, runeID)
  runeVisual.LuaObjectScript.Disable()
  keysUsed = keysUsed + 1
  PlayRuneOffFX(runeID)
  if keysUsed == 3 then
    challengeComplete = true
    Enable()
    BellsCompleteBanter()
    if chestSealsBrokenEvent ~= nil then
      LD.ExecuteCallbacksForEvent(thisLevel, thisObj, chestSealsBrokenEvent, "chestSealsBrokenEvent")
    end
    game.Audio.PlaySound("SND_LOOT_Chest_Runic_Puzzle_Complete")
    PlayUnlockSound()
    runeTable[1].runeKey.LuaObjectScript.ChallengeComplete()
    runeTable[2].runeKey.LuaObjectScript.ChallengeComplete()
    runeTable[3].runeKey.LuaObjectScript.ChallengeComplete()
  end
end
function OnBellCooldownElapsed(runeVisual, runeID)
  PlayRuneOnFX(runeID)
  if challengeComplete == false then
    keysUsed = keysUsed - 1
    runeVisual.LuaObjectScript.Enable({delay = 0.75})
  end
end
function OnRotateCallback()
  keysUsed = 0
  if (isRevealedByLight == false or isLit) and challengeComplete == false then
    for i = 1, 3 do
      runeTable[i].baseSuccess[1] = runeTable[i].successRune == runeTable[1].runeKey.LuaObjectScript.GetCurrentRune()
      runeTable[i].baseSuccess[2] = runeTable[i].successRune == runeTable[2].runeKey.LuaObjectScript.GetCurrentRune()
      runeTable[i].baseSuccess[3] = runeTable[i].successRune == runeTable[3].runeKey.LuaObjectScript.GetCurrentRune()
      if runeTable[i].baseSuccess[1] or runeTable[i].baseSuccess[2] or runeTable[i].baseSuccess[3] then
        runeTable[i].runeVisual.LuaObjectScript.Disable()
        keysUsed = keysUsed + 1
        if runeTable[i].lastState ~= runeTable[i].runeVisual.LuaObjectScript.IsEnabled() then
          runeTable[i].lastState = runeTable[i].runeVisual.LuaObjectScript.IsEnabled()
          PlayRuneOffFX(i)
        end
      else
        if runeTable[i].runeVisual.LuaObjectScript.IsEnabled() == false then
          runeTable[i].runeVisual.LuaObjectScript.Enable({delay = 0.75})
        end
        if runeTable[i].lastState ~= runeTable[i].runeVisual.LuaObjectScript.IsEnabled() then
          runeTable[i].lastState = runeTable[i].runeVisual.LuaObjectScript.IsEnabled()
          PlayRuneOnFX(i)
        end
      end
    end
    CheckFlipperRune()
  end
end
function CheckFlipperRune()
  if keysUsed == 3 then
    challengeComplete = true
    Enable()
    if chestSealsBrokenEvent ~= nil then
      LD.ExecuteCallbacksForEvent(thisLevel, thisObj, chestSealsBrokenEvent, "chestSealsBrokenEvent")
    end
  elseif state == states.LOCKED then
    Lock()
  end
end
function LightReveal()
  isLit = true
  for i = 1, 3 do
    runeTable[i].runeVisual.LuaObjectScript.ShowRune()
  end
  if keyType == "MemoryChest" then
    OnRotateCallback()
  end
end
function LightHide()
  isLit = false
  for i = 1, 3 do
    runeTable[i].runeVisual.LuaObjectScript.HideRune()
  end
  if keyType == "MemoryChest" then
    OnRotateCallback()
  end
end
function DimRunes()
  for i = 1, 3 do
    runeTable[i].runeVisual.LuaObjectScript.Dim()
  end
  isDim = true
end
function UndimRunes()
  for i = 1, 3 do
    runeTable[i].runeVisual.LuaObjectScript.Undim()
  end
  isDim = false
end
function DimRunesImmediate()
  for i = 1, 3 do
    runeTable[i].runeVisual.LuaObjectScript.Disable()
    runeTable[i].runeVisual.LuaObjectScript.Dim()
  end
  isDim = true
end
function temp_UnlockKey()
  if keysUsed < 3 then
    keysUsed = keysUsed + 1
  end
  if keysUsed == 1 then
    runeTable[1].runeVisual.LuaObjectScript.Enable()
  elseif keysUsed == 2 then
    runeTable[2].runeVisual.LuaObjectScript.Enable()
  elseif keysUsed == 3 then
    runeTable[3].runeVisual.LuaObjectScript.Enable()
    challengeComplete = true
    Enable()
    if chestSealsBrokenEvent ~= nil then
      LD.ExecuteCallbacksForEvent(thisLevel, thisObj, chestSealsBrokenEvent, "chestSealsBrokenEvent")
    end
  end
end
function temp_LockKey()
  if challengeComplete == false then
    if 0 < keysUsed then
      keysUsed = keysUsed - 1
    end
    if keysUsed == 0 then
      runeTable[1].runeVisual.LuaObjectScript.Disable()
    elseif keysUsed == 1 then
      runeTable[2].runeVisual.LuaObjectScript.Disable()
    elseif keysUsed == 2 then
      runeTable[3].runeVisual.LuaObjectScript.Disable()
    end
  end
end
function TripleChestLockedBanter()
  if HintBanterEnabled then
    game.Audio.PlayBanterNonCritical("ca_triple_chest_notice")
  end
end
function BreakableBrokenBanter(numBroken)
  if HintBanterEnabled then
    if numBroken == 1 then
      game.Audio.PlayBanterNonCritical("ca_triple_chest_1_down")
    elseif numBroken == 2 then
      game.Audio.PlayBanterNonCritical("ca_triple_chest_2_down")
    elseif numBroken == 3 then
      game.Audio.PlayBanterNonCritical("ca_triple_chest_3_down")
    end
  end
end
function BellsCompleteBanter()
  if HintBanterEnabled then
    game.Audio.PlayBanterNonCritical("ca_triple_chest_complete")
  end
end
local musicEvents = {
  OpenedMusic = "",
  LockMusic = "",
  UnlockMusic = "",
  SealBrokenMusic = ""
}
function MusicSetup(sounds)
  if sounds ~= nil then
    for k, v in pairs(musicEvents) do
      for i, j in pairs(sounds) do
        if i == k and j ~= nil and j ~= "" then
          musicEvents[k] = j
        end
      end
      LD.SoundDebug(tostring(k) .. ": " .. tostring(v))
    end
  end
end
function PlayMusic(music)
  if music ~= "" and music ~= nil then
    game.Audio.StartMusic(music)
  else
    print(tostring(music) .. " is either empty or nil")
  end
end
function PlayOpenedMusic()
  PlayMusic(musicEvents.OpenedMusic)
end
function PlayLockMusic()
  PlayMusic(musicEvents.LockMusic)
end
function PlayUnlockMusic()
  PlayMusic(musicEvents.UnlockMusic)
end
function PlaySealBrokenMusic()
  PlayMusic(musicEvents.SealBrokenMusic)
end
local soundEmitter, soundEmitterName, soundEventUnlock, soundEventProgress, soundEventLock
function SoundInit()
  soundEmitterName = thisObj:FindLuaTableAttribute("soundEmitterName")
  if soundEmitterName ~= nil and soundEmitterName ~= "" then
    soundEmitter = thisObj:FindSingleSoundEmitterByName(soundEmitterName)
  end
  musicEvents.LockMusic = thisObj:FindLuaTableAttribute("soundEventLockMusic")
  musicEvents.OpenedMusic = thisObj:FindLuaTableAttribute("soundEventOpenedMusic")
  musicEvents.SealBrokenMusic = thisObj:FindLuaTableAttribute("soundEventSealBrokenMusic")
  musicEvents.UnlockMusic = thisObj:FindLuaTableAttribute("soundEventUnlockMusic")
  soundEventUnlock = thisObj:FindLuaTableAttribute("soundEventUnlock")
  soundEventLock = thisObj:FindLuaTableAttribute("soundEventLock")
  soundEventProgress = thisObj:FindLuaTableAttribute("soundEventProgress")
end
function PlaySound(snd)
  if soundEmitter ~= nil then
    soundEmitter:Start(snd)
  else
    print(tostring(soundEmitter) .. " is nil")
  end
end
function StopSound(snd)
  if soundEmitter ~= nil then
    soundEmitter:Stop(snd)
  else
    print(tostring(soundEmitter) .. " is nil")
  end
end
function PlayUnlockSound()
  PlaySound(soundEventUnlock)
end
function PlayLockSound()
  PlaySound(soundEventLock)
end
function PlayProgressSound()
  PlaySound(soundEventProgress)
end
function Debug_ToggleRunes()
  debug_runesOn = not debug_runesOn
  if debug_runesOn then
    for i = 1, 3 do
      runeTable[i].runeVisual.LuaObjectScript.Enable()
    end
  else
    for i = 1, 3 do
      runeTable[i].runeVisual.LuaObjectScript.Disable()
    end
  end
end
function ShowDebugText()
  local debugText = "IsDim: " .. tostring(isDim)
  for i = 1, 3 do
    debugText = debugText .. [[

 SucessRune[]] .. tostring(i) .. "] = " .. tostring(runeTable[i].successRune) .. " Match: [ " .. tostring(runeTable[i].baseSuccess[1]) .. ", " .. tostring(runeTable[i].baseSuccess[2]) .. ", " .. tostring(runeTable[i].baseSuccess[3]) .. " ]"
  end
  debugText = debugText .. " KeysUsed " .. keysUsed
  engine.DrawTextInWorld(thisObj:GetWorldPosition(), debugText, color.white)
end
