local LD = require("design.LevelDesignLibrary")
local color = require("core.color")
local thisObj, thisLevel, enabled
local ringTable = {}
local runesSolved = {
  false,
  false,
  false
}
local RuneBowl
local ring_maxAnimLength = 0
local jumpAnimOffset = 3.2
local solvedAnimOffset = 8
local previouslySolved = {
  false,
  false,
  false
}
local debug_solve = false
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  ringTable[1] = {
    animRing = "Anim_OuterRing",
    axeTarget = "AxeTarget_outer",
    rune = "rune1",
    isEmbed = false,
    matchRingAnimIndex = nil
  }
  ringTable[2] = {
    animRing = "Anim_MidRing",
    axeTarget = "AxeTarget_mid",
    rune = "rune2",
    isEmbed = false,
    matchRingAnimIndex = nil
  }
  ringTable[3] = {
    animRing = "Anim_InnerRing",
    axeTarget = "AxeTarget_inner",
    rune = "rune3",
    isEmbed = false,
    matchRingAnimIndex = nil
  }
  for i = 1, 3 do
    ringTable[i].animRing = thisObj:FindSingleGOByName(ringTable[i].animRing)
    ringTable[i].axeTarget = thisObj:FindSingleGOByName(ringTable[i].axeTarget)
    ringTable[i].rune = thisObj:FindSingleGOByName(ringTable[i].rune)
  end
  ring_maxAnimLength = ringTable[1].animRing.AnimLengthFrames
  game.SubObject.Sleep(thisObj)
  SoundInit()
end
function OnFirstStart(level, obj)
  enabled = false
  ringTable[1].animRing:JumpAnimationToFrame(225)
  ringTable[2].animRing:JumpAnimationToFrame(135)
  ringTable[3].animRing:JumpAnimationToFrame(45)
  for i = 1, 3 do
    ringTable[i].animRing:PauseAnimation()
  end
end
function OnStart(level, obj)
  if not (runesSolved[1] and runesSolved[2]) or not runesSolved[3] then
    for i = 1, 3 do
      ringTable[i].axeTarget.LuaObjectScript.RegisterEmbedCallback(function()
        Embedded(i)
      end)
      ringTable[i].axeTarget.LuaObjectScript.RegisterUnembedStartCallback(function()
        Unembedded(i)
      end)
    end
  end
end
function OnUpdate(level, obj)
  local sleepUpdate = true
  for index = 1, 3 do
    if ringTable[index].matchRingAnimIndex then
      local indexToMatch = ringTable[index].matchRingAnimIndex
      if ApproxFrames(ringTable[index].animRing, ringTable[indexToMatch].animRing, jumpAnimOffset) then
        ringTable[index].animRing:JumpAnimToFrame(ringTable[indexToMatch].animRing.AnimFrame)
        ringTable[index].animRing:PlayAnimCycle(0.6)
        runesSolved[index] = true
        runesSolved[indexToMatch] = true
        previouslySolved[index] = true
        previouslySolved[indexToMatch] = true
        ringTable[index].rune:PlayAnimToFrame(90)
        ringTable[indexToMatch].rune:PlayAnimToFrame(90)
        ringTable[index].matchRingAnimIndex = nil
      else
        sleepUpdate = false
      end
    end
  end
  if sleepUpdate then
    game.SubObject.Sleep(obj)
    if runesSolved[1] and runesSolved[2] and runesSolved[3] then
      enabled = true
      for i = 1, 3 do
        ringTable[i].axeTarget.LuaObjectScript.Disable()
        if ringTable[i].axeTarget.IsRefNode then
          ringTable[i].axeTarget.Child:HideCollision()
        else
          ringTable[i].axeTarget:HideCollision()
        end
        ringTable[i].animRing:PlayAnimToEnd(1.2)
        ringTable[i].animRing:OnAnimationDone(thisObj, "PreTriggerEmbedBehavior_" .. tostring(i), {Force = true})
        PlaySoundSolve()
      end
    end
  end
end
function OnSaveCheckpoint(level, obj)
  return {enabled = enabled, runesSolved = runesSolved}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  enabled = savedInfo.enabled
  runesSolved = savedInfo.runesSolved
end
function PreTriggerEmbedBehavior_1()
  ringTable[1].axeTarget.Child.LuaObjectScript.TriggerEmbedBehavior()
  GameObjects.Riv350_Banter.LuaObjectScript.PuzzleWin()
  PlaySoundCompleted()
end
function PreTriggerEmbedBehavior_2()
  ringTable[2].axeTarget.Child.LuaObjectScript.TriggerEmbedBehavior()
end
function PreTriggerEmbedBehavior_3()
  ringTable[3].axeTarget.Child.LuaObjectScript.TriggerEmbedBehavior()
end
function SetRuneBowl(runeBowlObj)
  RuneBowl = runeBowlObj
  if RuneBowl.IsRefNode then
    RuneBowl = RuneBowl.Child
  end
end
function ApproxFrames(ring1, ring2, offset)
  local lowFrame = 0
  local highFrame = 0
  if ring1.AnimFrame < ring2.AnimFrame then
    highFrame = ring2.AnimFrame
    lowFrame = ring1.AnimFrame
  else
    highFrame = ring1.AnimFrame
    lowFrame = ring2.AnimFrame
  end
  if highFrame + offset > ring_maxAnimLength and offset >= lowFrame then
    return lowFrame <= highFrame + offset - ring_maxAnimLength
  end
  return offset >= highFrame - lowFrame
end
function Embedded(index)
  ringTable[index].isEmbed = true
  if enabled == false then
    return
  end
  ringTable[index].animRing:PauseAnimation()
  runesSolved[index] = false
  if index == 2 then
    runesSolved[1] = false
    runesSolved[3] = false
  elseif index == 1 then
    runesSolved[3] = ApproxFrames(ringTable[2].animRing, ringTable[3].animRing, solvedAnimOffset)
    runesSolved[2] = runesSolved[3]
  elseif index == 3 then
    runesSolved[2] = ApproxFrames(ringTable[2].animRing, ringTable[1].animRing, solvedAnimOffset)
    runesSolved[1] = runesSolved[2]
  end
  if previouslySolved[1] ~= runesSolved[1] or previouslySolved[2] ~= runesSolved[2] or previouslySolved[3] ~= runesSolved[3] then
    LD.CallFunctionAfterDelay(function()
      GameObjects.Riv350_Banter.LuaObjectScript.PuzzleBroken()
    end, 0.5)
  end
  for i = 1, 3 do
    if runesSolved[i] == false then
      ringTable[i].rune:PlayAnimationToFrame(0, {Rate = -2.1})
    end
    previouslySolved[i] = runesSolved[i]
  end
  if thisLevel == game.FindLevel("Riv350_CalderaVista") then
    print("Playing freeze gear sound")
    thisLevel:CallScript("PlayGearEmbedSound")
  end
  _G.GearEmbed()
end
function Unembedded(index)
  ringTable[index].isEmbed = false
  if enabled == false then
    return
  end
  if index + 1 <= 3 and ApproxFrames(ringTable[index].animRing, ringTable[index + 1].animRing, solvedAnimOffset) == true then
    ringTable[index].matchRingAnimIndex = index + 1
    PlaySoundMatch()
    GameObjects.Riv350_Banter.LuaObjectScript.PuzzleSuccess()
  end
  if 1 <= index - 1 and ApproxFrames(ringTable[index].animRing, ringTable[index - 1].animRing, solvedAnimOffset) == true then
    ringTable[index].matchRingAnimIndex = index - 1
    PlaySoundMatch()
    GameObjects.Riv350_Banter.LuaObjectScript.PuzzleSuccess()
  else
    GameObjects.Riv350_Banter.LuaObjectScript.puzzleNotClose()
  end
  if ringTable[index].matchRingAnimIndex then
    local matchedFrame = ringTable[ringTable[index].matchRingAnimIndex].animRing.AnimFrame
    local myAnimFrame = ringTable[index].animRing.AnimFrame
    if matchedFrame > myAnimFrame or myAnimFrame + solvedAnimOffset > ring_maxAnimLength and matchedFrame <= solvedAnimOffset then
      ringTable[index].animRing:PlayAnimationCycle({Rate = 0.7})
    else
      ringTable[index].animRing:PlayAnimationCycle({Rate = 0.15})
    end
    game.SubObject.Wake(thisObj)
  else
    ringTable[index].animRing:PlayAnimationCycle({Rate = 0.6})
    GameObjects.Riv350_Banter.LuaObjectScript.PuzzleClose()
  end
  if thisLevel.Name == "WAD_Riv350_CalderaVista" then
    _G.PlayGearUnembedSound()
  end
end
function TurnOnDisclock()
  if enabled == false and (runesSolved[1] == false or runesSolved[2] == false or runesSolved[3] == false) then
    enabled = true
    for i = 1, 3 do
      ringTable[i].axeTarget.LuaObjectScript.Enable()
      if ringTable[i].isEmbed == false and ringTable[i].axeTarget.Child:IsAxeEmbedded() == false then
        ringTable[i].axeTarget.LuaObjectScript.TriggerUnembedBehavior()
        ringTable[i].animRing:PlayAnimationCycle({Rate = 0.6})
      end
    end
  end
end
function ShowDebugTable(X, Y, title)
  local debugTable = {}
  debugTable.Title = title or "Runebowl Disclock"
  debugTable.X = X or 120
  debugTable.Y = Y or 10
  debugTable.TitleColor = engine.Vector.New(255, 0, 128)
  for i = 1, 3 do
    debugTable[#debugTable + 1] = {
      "Name : ",
      "Ring" .. i
    }
    debugTable[#debugTable + 1] = {
      "AnimFrame  : ",
      ringTable[i].animRing.AnimFrame
    }
    debugTable[#debugTable + 1] = {
      "isEmbed  : ",
      ringTable[i].isEmbed
    }
  end
  engine.DrawDebugTable(debugTable)
end
local soundEmitter
local soundEvents = {
  Match = "SND_DOOR_Rune_Disc_Line_Up_Success_Gears_Lock",
  Glow = "SND_DOOR_Rune_Disc_Line_Up_Success_Words_Glow",
  Solve = "SND_DOOR_Rune_Disc_Line_Up_Puzzle_Complete_Gears_LP"
}
local ringLoops = {
  "",
  "",
  ""
}
function SoundInit()
  soundEmitter = thisObj:FindSingleSoundEmitterByName("SNDRuneDisk")
  SoundSetup()
end
function SoundSetup(sounds)
  if sounds ~= nil then
    if sounds.SoundEmitter ~= nil then
      soundEmitter = thisObj:FindSingleSoundEmitterByName(sounds.SoundEmitter)
    end
    soundEvents.Match = sounds.Match or soundEvents.Match
    soundEvents.Solve = sounds.Solve or soundEvents.Solve
  end
  for k, v in pairs(soundEvents) do
    LD.SoundDebug(tostring(k) .. ": " .. tostring(v))
  end
end
function StopSoundAllLoops()
  if thisLevel == game.FindLevel("Riv350_CalderaVista") then
    thisLevel:CallScript("StopDiscRotateSound")
  end
end
function PlaySoundMatch()
  LD.PlaySound(soundEmitter, soundEvents.Match)
  LD.PlaySound(soundEmitter, soundEvents.Glow)
end
function PlaySoundSolve()
  LD.PlaySound(soundEmitter, soundEvents.Solve)
end
function StopSoundSolve()
  LD.StopSound(soundEmitter, soundEvents.Solve)
end
function PlaySoundCompleted()
  StopSoundAllLoops()
  StopSoundSolve()
end
function IsAnyGearLocked()
  for i = 1, 3 do
    if ringTable[i].axeTarget.Child:IsAxeEmbedded() == true then
      return true
    end
  end
  return false
end
function ShowDebugText()
  local debugText = ""
  for i = 1, 3 do
    debugText = debugText .. "\n" .. ringTable[i].animRing:GetName() .. [[
:
>>Frame = [ ]] .. tostring(ringTable[i].animRing.AnimFrame) .. " ] , Solved = [ " .. tostring(runesSolved[i]) .. " ] "
    if ringTable[i].matchRingAnimIndex ~= nil then
      debugText = debugText .. [[

 IsSettling true ]]
    end
  end
  engine.DrawTextInWorld(thisObj:GetWorldPosition(), debugText, color.white)
end
