local LD = require("design.LevelDesignLibrary")
local timer = require("level.timer")
local monitors = require("level.MonitorLibrary")
local thisObject
local currentRotation = 0
local turnTimer
local fullRotationFrames = 360
local segmentTurnFrames = 90
local startAtFrame = 0
local turnTime = 3
local numberOfSegments = 4
local bTurning = false
local bTurningLeft = false
local bCanTurnLeft = true
local bCanTurnRight = true
local bStopper = false
local lastTurnedRight = false
local lastTurnedLeft = false
local speedMultiplier = 1
local rotationMonitor
function OnScriptLoaded(level, obj)
  thisObject = obj
  fullRotationFrames = obj:FindLuaTableAttribute("NumberOfFramesInFullRotation") or 360
  numberOfSegments = obj:FindLuaTableAttribute("NumberOfSegments") or 8
  segmentTurnFrames = fullRotationFrames / numberOfSegments
  turnTime = obj:FindLuaTableAttribute("SegmentTurnTime") or 3
  startAtFrame = obj:FindLuaTableAttribute("StartAtFrame") or 0
  SetToFrame(startAtFrame)
  game.SubObject.Sleep(obj)
  game.SubObject.SetForgetOnCheckpoint(obj)
end
function OnStart(level, obj)
  thisObject:JumpAnimToFrame(currentRotation * segmentTurnFrames)
  thisObject:PauseAnim()
  if bTurning then
    if bTurningLeft then
      StartTurningLeft()
    else
      StartTurning()
    end
  end
end
function TurnLeft(stop)
  CorrectFrames(thisObject.AnimFrame)
  local nextRotation
  local debug = thisObject.AnimFrame
  if bCanTurnLeft then
    StopTurning()
    if currentRotation == 0 then
      thisObject:JumpAnimToFrame(fullRotationFrames)
      nextRotation = numberOfSegments - 1
    end
    if 0 < currentRotation then
      nextRotation = currentRotation - 1
    end
    if bStopper and stop ~= nil then
      if stop <= thisObject.AnimFrame and stop > nextRotation * segmentTurnFrames then
        thisObject:PlayAnimToFrame(stop, -1 * speedMultiplier)
        bCanTurnLeft = false
      else
        thisObject:PlayAnimToFrame(nextRotation * segmentTurnFrames, -1 * speedMultiplier)
      end
    else
      thisObject:PlayAnimToFrame(nextRotation * segmentTurnFrames, -1 * speedMultiplier)
    end
    currentRotation = nextRotation
    bCanTurnRight = true
    lastTurnedRight = false
    lastTurnedLeft = true
  end
end
function TurnRight(stop)
  CorrectFrames(thisObject.AnimFrame)
  local debug = thisObject.AnimFrame
  if bCanTurnRight then
    local nextRotation
    StopTurning()
    if currentRotation < numberOfSegments then
      nextRotation = currentRotation + 1
    else
      nextRotation = 1
    end
    if bStopper and stop ~= nil then
      if stop >= thisObject.AnimFrame and stop < nextRotation * segmentTurnFrames then
        thisObject:PlayAnimToFrame(stop, speedMultiplier)
        bCanTurnRight = false
      else
        thisObject:PlayAnimToFrame(nextRotation * segmentTurnFrames, speedMultiplier)
      end
    else
      thisObject:PlayAnimToFrame(nextRotation * segmentTurnFrames, speedMultiplier)
    end
    currentRotation = nextRotation
    bCanTurnLeft = true
    lastTurnedRight = true
    lastTurnedLeft = false
  end
end
function StartTurning()
  CorrectFrames(thisObject.AnimFrame)
  bTurning = true
  bTurningLeft = false
  TurnRight()
  turnTimer = timer.StartLevelTimer(turnTime / speedMultiplier, StartTurning)
end
function StartTurningLeft()
  CorrectFrames(thisObject.AnimFrame)
  bTurning = true
  bTurningLeft = true
  TurnLeft()
  turnTimer = timer.StartLevelTimer(turnTime / speedMultiplier, StartTurningLeft)
end
function SetSpeedMultiplier(value)
  speedMultiplier = value
end
function StopTurning()
  if turnTimer ~= nil then
    turnTimer:Stop()
    turnTimer = nil
  end
  bTurning = false
end
function GetCurrentFrame()
  return currentRotation
end
function Reset()
  if lastTurnedLeft then
    thisObject:PlayAnimToFrame(fullRotationFrames, 1)
  elseif lastTurnedRight then
    thisObject:PlayAnimToEnd(-1)
  else
    thisObject:PlayAnimToEnd(-3)
  end
end
function SetToFrame(frame)
  thisObject:JumpAnimToFrame(frame)
  thisObject:PauseAnim()
  CorrectFrames(frame)
end
function TurnToFrame(frame, speed)
  local currentFrame = thisObject.AnimFrame
  if frame >= currentFrame then
    speed = math.abs(speed)
  else
    speed = -1 * speed
  end
  StopTurning()
  thisObject:PlayAnimToFrame(frame, speed)
  CorrectFrames(frame)
end
function TurnToFrameDirect(frame, speed)
  local currentFrame = thisObject.AnimFrame
  StopTurning()
  if 0 < speed and frame <= currentFrame then
    thisObject:PlayAnimToFrame(fullRotationFrames, speed)
    rotationMonitor = monitors.CreateAnimFrameMonitor(thisObject)
    rotationMonitor:OnFrameForward(fullRotationFrames, CompleteFrameForward, frame, speed)
    rotationMonitor:SetTriggerCountLimit(1)
  elseif speed < 0 and frame >= currentFrame then
    thisObject:PlayAnimToFrame(0, speed)
    rotationMonitor = monitors.CreateAnimFrameMonitor(thisObject)
    rotationMonitor:OnFrameBackward(0, CompleteFrameBackward, frame, speed)
    rotationMonitor:SetTriggerCountLimit(1)
  else
    thisObject:PlayAnimToFrame(frame, speed)
  end
  CorrectFrames(frame)
end
function CompleteFrameForward(timer, frame, speed)
  thisObject:JumpAnimToFrame(0)
  thisObject:PauseAnim()
  TurnToFrame(frame, speed)
  rotationMonitor = nil
  CorrectFrames(frame)
end
function CompleteFrameBackward(timer, frame, speed)
  thisObject:JumpAnimToFrame(fullRotationFrames)
  thisObject:PauseAnim()
  TurnToFrame(frame, speed)
  rotationMonitor = nil
  CorrectFrames(frame)
end
function CorrectFrames(frame)
  currentRotation = round(frame / segmentTurnFrames, 0)
end
function round(num, idp)
  local mult = 10 ^ (idp or 0)
  return math.floor(num * mult + 0.5) / mult
end
function OnSaveCheckpoint(level, obj)
  return {
    thisRotation = currentRotation,
    ICanTurnLeft = bCanTurnLeft,
    ICanTurnRight = bCanTurnRight,
    mySpeed = speedMultiplier,
    IAmTurning = bTurning,
    IAmTurningLeft = bTurningLeft
  }
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  currentRotation = savedInfo.thisRotation
  bCanTurnLeft = savedInfo.ICanTurnLeft
  bCanTurnRight = savedInfo.ICanTurnRight
  speedMultiplier = savedInfo.mySpeed
end
