local LD = require("design.LevelDesignLibrary")
local monitors = require("level.MonitorLibrary")
local timers = require("level.timer")
local thisObj, thisLevel, rotator, extenders, blade_gears, blade, drain, bStartActive, rotationStartAngle, retractSpeed, rotateSpeed, rotateDirection, bRotateOnStart, bRetractedOnStart, bRetracted, bWasRetracted, retractionPercent, bRetractionRunning, bProtractionRunning, bRetractingOnStart, bRotatorActive, bWasRotating, ProtractedDuration, RetractedDuration, OnProtractionCompletedCallbacks, OnProtractionStartedCallbacks, OnRetractionCompletedCallbacks, OnRetractionStartedCallbacks, RetractionMonitor, ProtractionTimer
local bActive = false
local GO_CombatCollision
local isFirstStart = false
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  rotator = thisObj:FindSingleGOByName("rotator")
  extenders = thisObj:FindSingleGOByName("extenders")
  blade_gears = thisObj:FindSingleGOByName("blade_gears")
  blade = thisObj:FindSingleGOByName("blade")
  drain = thisObj:FindSingleGOByName("drain")
  GO_CombatCollision = thisObj:FindSingleGOByName("CombatCollision")
  bStartActive = GetAttribute("bStartActive")
  bRetractedOnStart = GetAttribute("bRetractedOnStart")
  rotationStartAngle = GetAttribute("RotateStartAngle")
  retractSpeed = GetAttribute("RetractSpeed")
  ProtractedDuration = GetAttribute("ProtractedDuration")
  RetractedDuration = GetAttribute("RetractedDuration")
  rotateSpeed = GetAttribute("RotateSpeed")
  rotateDirection = GetAttribute("RotateDirection")
  bRotateOnStart = GetAttribute("bRotateOnStart")
  bRetractingOnStart = GetAttribute("bRetractingOnStart")
  retractionPercent = GetAttribute("RetractionPercent")
  OnProtractionCompletedCallbacks = LD.ExtractCallbacksForEvent(level, obj, GetAttribute("OnProtractionCompletedCallbacks"))
  OnProtractionStartedCallbacks = LD.ExtractCallbacksForEvent(level, obj, GetAttribute("OnProtractionStartedCallbacks"))
  OnRetractionCompletedCallbacks = LD.ExtractCallbacksForEvent(level, obj, GetAttribute("OnRetractionCompletedCallbacks"))
  OnRetractionStartedCallbacks = LD.ExtractCallbacksForEvent(level, obj, GetAttribute("OnRetractionStartedCallbacks"))
  rotator:JumpAnimToFrame(rotationStartAngle)
  rotator:PauseAnim()
  bRetracted = bRetractedOnStart
  bRetractionRunning = false
  bProtractionRunning = false
  if bRetracted then
    extenders:JumpAnimToPercent(retractionPercent)
    extenders:PauseAnim()
    blade_gears:JumpAnimToPercent(retractionPercent)
    blade_gears:PauseAnim()
  end
  RetractionMonitor = monitors.CreateAnimFrameMonitor(blade_gears)
  RetractionMonitor:SetPrintsEnabled(false)
  RetractionMonitor:OnFrameForward(blade_gears.AnimLengthFrames * retractionPercent, OnRetractionComplete)
  RetractionMonitor:OnFrameBackward(0, OnProtractionComplete)
  blade.Child.Child:OnAnimationDone(thisObj, "bladeRotateLoop", {
    Animation = "bladeRotate_Start",
    Force = true
  })
  SoundInit()
  game.SubObject.Sleep(obj)
end
function OverrideRetractedState(newValue)
  bRetracted = newValue
end
function OnFirstStart(level, obj)
  isFirstStart = true
  if bStartActive or drain.LuaObjectScript.GetActive() then
    bActive = false
    Start()
  else
    bActive = true
    Stop()
  end
end
function OnStart(level, obj)
  if not isFirstStart then
    if bRetracted or bRetractionRunning then
      bRetracted = true
      extenders:JumpAnimToPercent(retractionPercent)
      extenders:PauseAnim()
      blade_gears:JumpAnimToPercent(retractionPercent)
      blade_gears:PauseAnim()
    elseif not bRetracted or bProtractionRunning then
      bRetracted = false
      extenders:JumpAnimToPercent(0)
      extenders:PauseAnim()
      blade_gears:JumpAnimToPercent(0)
      blade_gears:PauseAnim()
    end
    if bActive or drain.LuaObjectScript.GetActive() then
      if ProtractionTimer and ProtractionTimer.running then
        ProtractionTimer:Pause()
      end
      bActive = false
      Start()
    else
      bActive = true
      Stop()
    end
    if bRotatorActive then
      bRotatorActive = true
      if rotateDirection == "CW" then
        RotateCW()
      elseif rotateDirection == "CCW" then
        RotateCCW()
      end
    else
      bRotatorActive = false
      bWasRotating = true
    end
  end
end
function OnSaveCheckpoint(level, obj)
  return {
    bActive = bActive,
    bProtractionRunning = bProtractionRunning,
    bRetractionRunning = bRetractionRunning,
    bRetracted = bRetracted,
    rotateDirection = rotateDirection,
    bRotatorActive = bRotatorActive
  }
end
function OnRestoreCheckpoint(level, obj, p)
  bActive = p.bActive
  bRetracted = p.bRetracted
  bProtractionRunning = p.bProtractionRunning
  bRetractionRunning = p.bRetractionRunning
  rotateDirection = p.rotateDirection
  bRotatorActive = p.bRotatorActive
end
function Protract()
  if drain.LuaObjectScript.GetActive() and (bRetracted or bRetractionRunning) then
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, OnProtractionStartedCallbacks)
    RetractionMonitor:Start()
    bRetractionRunning = false
    bProtractionRunning = true
    extenders:PlayAnimToEnd(-retractSpeed)
    blade_gears:PlayAnimToEnd(-retractSpeed)
  end
end
function OnProtractionComplete()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, OnProtractionCompletedCallbacks)
  RetractionMonitor:Stop()
  bProtractionRunning = false
  bRetracted = false
  ProtractionTimer = timers.StartLevelTimer(ProtractedDuration, Retract)
end
function Retract()
  if drain.LuaObjectScript.GetActive() and (not bRetracted or bProtractionRunning) then
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, OnRetractionStartedCallbacks)
    RetractionMonitor:Start()
    bProtractionRunning = false
    bRetractionRunning = true
    extenders:PlayAnimToFrame(extenders.AnimLengthFrames * retractionPercent, retractSpeed)
    blade_gears:PlayAnimToFrame(blade_gears.AnimLengthFrames * retractionPercent, retractSpeed)
  end
end
function OnRetractionComplete()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, OnRetractionCompletedCallbacks)
  RetractionMonitor:Stop()
  bRetractionRunning = false
  bRetracted = true
  ProtractionTimer = timers.StartLevelTimer(RetractedDuration, Protract)
end
function Start()
  if bActive then
    return
  end
  bActive = true
  if drain.LuaObjectScript.GetActive() or bRetractingOnStart or bRetractionRunning or bProtractionRunning then
    bRetractingOnStart = true
    StartRetractor()
  end
  if bRotateOnStart or bWasRotating then
    StartRotator()
  end
  StartSpin()
end
function Stop(bAxe)
  if not bActive then
    return
  end
  bWasRotating = bRotatorActive
  bActive = false
  StopSpin(bAxe)
  StopRotator()
  StopRetractor()
end
local bSpinning
function StartSpin()
  bSpinning = true
  blade.Child.Child:StartAnim("bladeRotate_Start")
  GO_CombatCollision.LuaObjectScript.Enable()
  PlayBladesRotationSound()
end
function bladeRotateLoop()
  if bSpinning then
    blade.Child.Child:StartAnim("bladeRotate")
    blade.Child.Child:PlayAnimCycle(1)
  end
  blade.Child.Child:OnAnimationDone(thisObj, "bladeRotateLoop", {
    Animation = "bladeRotate_Start",
    Force = true
  })
end
function StopSpin(bAxe)
  bSpinning = false
  if bAxe then
    blade.Child.Child:StartAnim("bladeRotate_Stop_Axe")
  else
    blade.Child.Child:StartAnim("bladeRotate_Stop")
  end
  StopBladesRotationSound()
  GO_CombatCollision.LuaObjectScript.Disable()
end
function StartRetractor()
  StartSpin()
  if ProtractionTimer ~= nil then
    if not ProtractionTimer.running then
      ProtractionTimer.fired = false
      ProtractionTimer:Unpause()
    end
  elseif bRetracted then
    ProtractionTimer = timers.StartLevelTimer(RetractedDuration, Protract)
  else
    ProtractionTimer = timers.StartLevelTimer(ProtractedDuration, Retract)
  end
end
function StopRetractor()
  if ProtractionTimer and ProtractionTimer.running then
    ProtractionTimer:Pause()
    if RetractionMonitor then
      RetractionMonitor:Stop()
    end
  end
end
function StartRotator()
  StartSpin()
  if not bRotatorActive then
    bRotatorActive = true
    if rotateDirection == "CW" then
      RotateCW()
    elseif rotateDirection == "CCW" then
      RotateCCW()
    end
  end
end
function StopRotator()
  StopSpin()
  if bRotatorActive then
    bRotatorActive = false
    rotator:PauseAnim()
  end
end
function StopRotator_KeepSpinning()
  if bRotatorActive then
    bRotatorActive = false
    rotator:PauseAnimation()
  end
end
function GetAttribute(strAttributeName)
  return thisObj:GetLuaTableAttribute(strAttributeName)
end
function RotateCW()
  rotateDirection = "CW"
  rotateSpeed = -math.abs(rotateSpeed)
  rotator:PauseAnim()
  rotator:PlayAnimCycle(rotateSpeed)
end
function RotateCCW()
  rotateDirection = "CCW"
  rotateSpeed = math.abs(rotateSpeed)
  rotator:PauseAnim(rotateSpeed)
  rotator:PlayAnimCycle(rotateSpeed)
end
function GoToSafeState(angle)
  local newangle = angle or 180
  RetractionMonitor:Stop()
  ProtractionTimer = nil
  bWasRotating = bRotatorActive
  bRetractionRunning = false
  bActive = false
  StopRotator_KeepSpinning()
  StopRetractor()
  bRetracted = true
  extenders:PlayAnimToPercent(retractionPercent, retractSpeed)
  blade_gears:PlayAnimToPercent(retractionPercent, retractSpeed)
  rotator:PlayAnimToFrame(newangle, 3)
end
function DrainEmbed()
  if drain.LuaObjectScript.GetActive() then
    StopRetractor()
  end
end
function DrainUnembed()
  if drain.LuaObjectScript.GetActive() then
    StartRetractor()
  end
end
local soundEmitter
local retractorAnimSounds = {
  {
    frame = 1,
    direction = "forward",
    soundEvent = "SND_TRAP_Saw_Blades_Shift_Released"
  },
  {
    frame = 99,
    direction = "forward",
    soundEvent = "SND_TRAP_Saw_Blades_Shift_Locked"
  },
  {
    frame = 99,
    direction = "backward",
    soundEvent = "SND_TRAP_Saw_Blades_Shift_Released"
  },
  {
    frame = 1,
    direction = "backward",
    soundEvent = "SND_TRAP_Saw_Blades_Shift_Locked"
  }
}
local soundEvents = {
  SNDBlades = "SND_TRAP_Saw_Blades_LP"
}
function SoundInit()
  soundEmitter = thisObj:FindSingleGOByName("blade_gears"):FindSingleSoundEmitterByName("SNDSawBlade")
  AddSoundsToTriggerOnRetractor(retractorAnimSounds)
end
function PlayBladesRotationSound()
  print("Playing SawBlade_Drain sound!")
  LD.PlayRestartableSoundLoop(soundEmitter, soundEvents.SNDBlades)
end
function StopBladesRotationSound()
  LD.StopRestartableSoundLoop(soundEmitter, soundEvents.SNDBlades)
end
function AddSoundsToTriggerOnRetractor(animSoundTable)
  for i = 1, #animSoundTable do
    if animSoundTable[i].direction == "forward" then
      RetractionMonitor:OnFrameForward(animSoundTable[i].frame, function()
        LD.PlaySound(soundEmitter, animSoundTable[i].soundEvent)
      end)
    elseif animSoundTable[i].direction == "backward" then
      RetractionMonitor:OnFrameBackward(animSoundTable[i].frame, function()
        LD.PlaySound(soundEmitter, animSoundTable[i].soundEvent)
      end)
    else
      LD.SoundDebug("Unable to add the following sound action to the Retractor Anim Monitor " .. animSoundTable[i].frame .. " " .. animSoundTable[i].direction .. " " .. animSoundTable[i].soundEvent)
    end
  end
end
function SoundSetup(sounds)
  if sounds ~= nil then
    if sounds.SoundEmitter ~= nil then
      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 ~= "" then
          soundEvents[key] = newValue
        end
      end
      LD.SoundDebug(tostring(key) .. ": " .. tostring(value))
    end
  end
end
