local LD = require("design.LevelDesignLibrary")
local timers = require("level.timer")
local thisObj, cranePlatform, craneCounterWeight, spinnableSystem
local craneObjs = {}
local directionReceived = ""
local anim_swingArm, maxFrame, minFrame
local UsingLockFrame = false
local kratosOnPlatformRefractoryTime = 2
local debugEnabled = false
local jumpsEnabled = false
local cooldownTimer
function OnScriptLoaded(level, obj)
  thisObj = obj
  spinnableSystem = thisObj:FindSingleGOByName("SpinnableSystem").Child
  cranePlatform = thisObj:FindSingleGOByName("cranePlatform")
  craneCounterWeight = thisObj:FindSingleGOByName("craneCounterWeight")
  AddObjectToCraneObjs({
    obj = cranePlatform,
    animPreface = "envSnakeBellyPlatform",
    anim = ""
  })
  AddObjectToCraneObjs({
    obj = craneCounterWeight,
    animPreface = "envSnakeBellyCounterWeight"
  })
  anim_swingArm = GameObjects.anim_swingArm
  if debugEnabled then
    EnableDebug()
  else
    DisableDebug()
  end
end
function OnSaveCheckpoint(level, obj)
  return {jumpsEnabled = jumpsEnabled}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  jumpsEnabled = savedInfo.jumpsEnabled
end
function OnFirstStart(level, obj)
  GameObjects.climbtree_nojump:HideTraversePath()
  GameObjects.climbtree_jump:HideTraversePath()
end
function OnStart(level, obj)
  maxFrame = spinnableSystem.LuaObjectScript.GetMaxFrame()
  minFrame = spinnableSystem.LuaObjectScript.GetMinFrame()
  DisableJump()
  SoundOnStart()
  GameObjects.CooldownCollision:HideCollision()
end
function OnUpdate(level, obj)
  if engine.IsDebug() and LD.IsPositionInFrontOfObject(thisObj:GetWorldPosition(), game.Player.FindPlayer()) and LD.GetDistanceBetweenTwoObjects(game.Player.FindPlayer(), thisObj) < 20 then
    local dt = {}
    table.insert(dt, {
      "thisObj",
      thisObj:GetName()
    })
    table.insert(dt, {"maxFrame: ", maxFrame})
    table.insert(dt, {"minFrame: ", minFrame})
    table.insert(dt, {
      "directionReceived",
      directionReceived
    })
    for _, v in pairs(craneObjs) do
      table.insert(dt, {
        v.obj:GetName() .. "_Anim",
        v.anim
      })
      table.insert(dt, {
        v.obj:GetName() .. "_AnimFrame",
        v.obj.Child.AnimFrame
      })
      if v.callback then
        table.insert(dt, {
          v.obj:GetName() .. "_Callback",
          v.callback
        })
        table.insert(dt, {
          v.obj:GetName() .. "_CallbackState",
          v.callbackState
        })
      end
    end
    dt.TitleColor = engine.Vector.New(33, 33, 128)
    dt.TitleAlpha = 220
    dt.Title = thisObj:GetName() .. " Spinnable System Debug"
    dt.X, dt.Y = 100, 2
    engine.DrawDebugTable(dt)
  end
end
function EnableDebug()
  debugEnabled = true
  game.SubObject.Wake(thisObj)
end
function DisableDebug()
  debugEnabled = false
  game.SubObject.Sleep(thisObj)
end
local kratosOnPlatform = false
local timer_ForgotSomethingBanter
function SetKratosOnPlatform()
  kratosOnPlatform = true
  spinnableSystem.LuaObjectScript.SetRefractoryTime(kratosOnPlatformRefractoryTime)
  if not LD.GetEntityVariable("CAL_MimirEyeAcquired") then
    timer_ForgotSomethingBanter = timers.StartLevelTimer(8, function()
      game.Audio.PlayBanter("SnakePuzzleF", nil, nil, false)
    end)
  end
end
function SetKratosOffPlatform()
  kratosOnPlatform = false
  spinnableSystem.LuaObjectScript.SetRefractoryTime(nil)
end
function GetKratosOnPlatform()
  return kratosOnPlatform
end
function SetJumpsEnabledTrue()
  jumpsEnabled = true
  if anim_swingArm.AnimFrame >= maxFrame and spinnableSystem.LuaObjectScript.IsLocked() and spinnableSystem.LuaObjectScript.IsInRefractory() then
    EnableJump()
  else
    DisableJump()
  end
end
function EnableJump()
  if not jumpsEnabled then
    return
  end
  GameObjects.climbtree_jump:ShowTraversePath()
  GameObjects.climbtree_nojump:HideTraversePath()
  GameObjects.CantJumpHint:HideEntityVolume()
end
function DisableJump()
  if not jumpsEnabled then
    return
  end
  GameObjects.climbtree_jump:HideTraversePath()
  GameObjects.climbtree_nojump:ShowTraversePath()
  GameObjects.CantJumpHint:ShowEntityVolume()
end
function AddObjectToCraneObjs(objList)
  table.insert(craneObjs, objList)
end
function OnLock(usingLockFrame, direction)
  UsingLockFrame = usingLockFrame
  if not UsingLockFrame then
    PlayStopAnimBasedOnDirection(direction)
  end
  if anim_swingArm.AnimFrame >= maxFrame and spinnableSystem.LuaObjectScript.IsInRefractory() then
    EnableJump()
  end
  StopSoundSpinningLoops()
end
function OnReachLockFrame(direction)
  if UsingLockFrame and anim_swingArm.AnimFrame > minFrame and anim_swingArm.AnimFrame < maxFrame then
    PlayStopAnimBasedOnDirection(direction)
    if anim_swingArm.AnimFrame >= maxFrame then
      EnableJump()
    end
  end
end
function OnUnlock()
  UsingLockFrame = false
  DisableJump()
  if not LD.GetEntityVariable("CAL_MimirEyeAcquired") and not GetKratosOnPlatform() then
    game.Audio.PlayBanter("SnakePuzzleD", nil, nil, false)
  elseif not LD.GetEntityVariable("CAL_MimirEyeAcquired") and timer_ForgotSomethingBanter then
    timer_ForgotSomethingBanter:Stop()
    timer_ForgotSomethingBanter = nil
  end
end
function Cooldown()
  if GetKratosOnPlatform() then
    GameObjects.CooldownCollision:ShowCollision()
    if cooldownTimer then
      cooldownTimer = nil
    end
    cooldownTimer = timers.StartLevelTimer(8, function()
      GameObjects.CooldownCollision:HideCollision()
    end)
  end
end
function OnForwardStart(direction)
  Cooldown()
  directionReceived = direction
  if anim_swingArm.AnimFrame < maxFrame then
    if direction == "backward" then
      if string.match(craneObjs[1].anim, "StartClockwise") then
        AnimateCraneObjects("StartClockwise", 1, "OnStartClockwiseComplete")
      else
        AnimateCraneObjects("StartCounterClockwise", -2, "OnForwardFromBackwardComplete")
      end
    else
      AnimateCraneObjects("StartClockwise", 1, "OnStartClockwiseComplete")
    end
    PlaySoundForwardLoops()
  end
end
function OnBackwardStart(direction)
  Cooldown()
  directionReceived = direction
  DisableJump()
  if direction == "forward" then
    AnimateCraneObjects("StartClockwise", -2, "OnBackwardFromForwardComplete")
  else
    AnimateCraneObjects("StartCounterClockwise", 1, "OnStartCounterClockwiseComplete")
  end
  if not LD.GetEntityVariable("CAL_MimirEyeAcquired") and anim_swingArm.AnimFrame >= maxFrame - 1 and not GetKratosOnPlatform() then
    game.Audio.PlayBanter("SnakePuzzleB", nil, nil, false)
  end
  PlaySoundBackwardLoops()
end
function OnReachEnd()
  AnimateCraneObjects("StopClockwise", 1, "ReturnToIdle")
  StopSoundSpinningLoops()
end
function OnReachStart()
  AnimateCraneObjects("StopCounterClockwise", 1, "ReturnToIdle")
  StopSoundSpinningLoops()
  if not LD.GetEntityVariable("CAL_MimirEyeAcquired") and not GetKratosOnPlatform() then
    game.Audio.PlayBanter("SnakePuzzleE", nil, nil, false)
  end
end
function AnimateCraneObjects(animName, rate, callback)
  if not GetKratosOnPlatform() then
    local callbackAssigned = false
    for i = 1, #craneObjs do
      craneObjs[i].obj.Child:ClearAllAnimCallbacks()
      local anim = craneObjs[i].animPreface .. animName
      if rate < 0 then
        craneObjs[i].obj.Child:SetAnimationAtFrame(anim, 0)
        craneObjs[i].obj.Child:JumpAnimToFrame(craneObjs[i].obj.Child.AnimLengthFrames)
      end
      craneObjs[i].obj.Child:PlayAnimationToEnd({Animation = anim, Rate = rate})
      if callback and not callbackAssigned then
        callbackAssigned = true
        craneObjs[i].callback = callback
        craneObjs[i].callbackState = "Registered"
        craneObjs[i].obj.Child:OnAnimationDone(thisObj, callback, {Animation = anim, Force = true})
      end
      craneObjs[i].anim = anim
    end
  end
end
function AnimateCycleCraneObjects(animName, rate)
  for i = 1, #craneObjs do
    local anim = craneObjs[i].animPreface .. animName
    craneObjs[i].obj.Child:StartAnim(anim)
    craneObjs[i].obj.Child:PlayAnimCycle(rate or 1)
    craneObjs[i].anim = anim
  end
end
function PlayStopAnimBasedOnDirection(direction)
  if direction == "forward" then
    AnimateCraneObjects("HardLockSlowClockwise", 1, "ReturnToIdle")
  elseif direction == "backward" then
    AnimateCraneObjects("HardLockSlowCounterClockwise", 1, "ReturnToIdle")
  end
end
function OnStartClockwiseComplete()
  AnimateCycleCraneObjects("MovingIdleClockwise")
end
function OnStartCounterClockwiseComplete()
  AnimateCycleCraneObjects("MovingIdleCounterClockwise")
end
function ReturnToIdle()
  AnimateCycleCraneObjects("Idle")
end
function OnBackwardFromForwardComplete()
  AnimateCraneObjects("StartCounterClockwise", 1, "OnStartCounterClockwiseComplete")
end
function OnForwardFromBackwardComplete()
  AnimateCraneObjects("StartClockwise", 1, "OnStartClockwiseComplete")
end
local spinnableDriverSoundOverrides = {
  SoundEmitter = nil,
  OnSpinForward = "",
  OnSpinBackward = "",
  OnReachStart = "",
  OnReachEnd = "",
  OnEmbed = "SND_MECH_Crane_Spinner_Hit_Cal800"
}
local craneCenterEmitter
local craneCenterSpinningSoundLoop = "SND_MECH_Crane_Rotate_Cal800_LP"
local spinnerGearEmitter
local spinnerGearSpinningSoundLoop = "SND_MECH_Crane_Gear_Rotate_Cal800_LP"
local spinnerDriverScript
function SoundOnStart()
  craneCenterEmitter = thisObj:FindSingleGOByName("anim_swingArm"):FindSingleSoundEmitterByName("SNDCraneCenter")
  spinnerGearEmitter = thisObj:FindSingleGOByName("SpinnableGears1"):FindSingleGOByName("Front_Gear").Child:FindSingleSoundEmitterByName("SNDWallGears")
  spinnerDriverScript = thisObj:FindSingleGOByName("SpinnableDriver1").LuaObjectScript
  spinnerDriverScript.SoundSetup(spinnableDriverSoundOverrides)
end
function PlaySoundForwardLoops()
  PlaySoundBiDirectionalLoops()
end
function PlaySoundBackwardLoops()
  PlaySoundBiDirectionalLoops()
end
function PlaySoundBiDirectionalLoops()
  LD.PlaySound(craneCenterEmitter, craneCenterSpinningSoundLoop)
  LD.PlaySound(spinnerGearEmitter, spinnerGearSpinningSoundLoop)
end
function StopSoundSpinningLoops()
  LD.StopSound(craneCenterEmitter, craneCenterSpinningSoundLoop)
  LD.StopSound(spinnerGearEmitter, spinnerGearSpinningSoundLoop)
end
