local timer = require("level.timer")
local DL = require("design.DesignerLibrary")
local CL = require("camera.CameraLibrary")
local EL = require("camera.EasingLibrary")
local LD = require("design.LevelDesignLibrary")
local reticleTarget, targetDistance, orbitElevation
local playerCreature = game.Player.FindPlayer()
local tbl_attackAdjust = {}
EL.SetupEaseTable(tbl_attackAdjust)
local tbl_attackAdjustReturn = {}
EL.SetupEaseTable(tbl_attackAdjustReturn)
local bPlayerAttacking = false
local timer_attackCooldown = timer.StartLevelTimer(2)
timer_attackCooldown:Stop()
local bTriggered = false
local kfCameraMaxFightRadius = 1000
local bOneTimeDone = false
local bDoneBlending = false
local tbl_AutoAdjust = {}
local tbl_Snapshot = {}
local g_CreatureType = "EarthDragon"
local CooldownTimer = {}
function AddTimerToTable(timer, name)
  CooldownTimer[name] = timer
end
local PrintCooldownTimers = function()
  print("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
  for k, timer in pairs(CooldownTimer) do
    local str = "CooldownTimer[" .. k .. "]"
    if timer then
      str = str .. " still has " .. timer:GetRemainingTime() .. " to go. Sorry!"
    end
    print(str)
  end
  print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
end
local IsCooledDown = function(markername)
  local timer = CooldownTimer[markername]
  if timer then
    local remainingTime = timer:GetRemainingTime()
    if 0 < remainingTime then
      return false
    end
  end
  return true
end
local MoveDatabase = {}
local dragon = {}
MoveDatabase.Dragon = dragon
dragon.Struggle = {}
dragon.Struggle.jointName = "JOLeftAnkle1"
dragon.Struggle.tweenIn = 0.5
dragon.Struggle.lockonDuration = 3
dragon.Struggle.tweenOut = 5
dragon.Struggle.cooldownDuration = 7
dragon.Struggle.marker = {}
dragon.Struggle.marker[1] = "DRAGONSTRUGGLE"
local earthdragon = {}
MoveDatabase.EarthDragon = earthdragon
earthdragon.BreathIntro = {}
earthdragon.BreathIntro.jointName = "JOHead1"
earthdragon.BreathIntro.tweenIn = 0.5
earthdragon.BreathIntro.lockonDuration = 3
earthdragon.BreathIntro.tweenOut = 5
earthdragon.BreathIntro.cooldownDuration = 7
earthdragon.BreathIntro.marker = {}
earthdragon.BreathIntro.marker[1] = "BREATHINTRO"
earthdragon.Bite1 = {}
earthdragon.Bite1.jointName = "JOHead1"
earthdragon.Bite1.tweenIn = 0.5
earthdragon.Bite1.lockonDuration = 3
earthdragon.Bite1.tweenOut = 5
earthdragon.Bite1.cooldownDuration = 7
earthdragon.Bite1.marker = {}
earthdragon.Bite1.marker[1] = "DRAGONBITE1"
earthdragon.Bite2 = {}
earthdragon.Bite2.jointName = "JOHead1"
earthdragon.Bite2.tweenIn = 0.5
earthdragon.Bite2.lockonDuration = 3
earthdragon.Bite2.tweenOut = 5
earthdragon.Bite2.cooldownDuration = 7
earthdragon.Bite2.marker = {}
earthdragon.Bite2.marker[1] = "DRAGONBITE2"
earthdragon.ElectricBreath = {}
earthdragon.ElectricBreath.jointName = "JOHead1"
earthdragon.ElectricBreath.tweenIn = 0.5
earthdragon.ElectricBreath.lockonDuration = 3
earthdragon.ElectricBreath.tweenOut = 5
earthdragon.ElectricBreath.cooldownDuration = 7
earthdragon.ElectricBreath.marker = {}
earthdragon.ElectricBreath.marker[1] = "ELECTRICBREATH"
earthdragon.Chase = {}
earthdragon.Chase.jointName = "JOHead1"
earthdragon.Chase.tweenIn = 0.5
earthdragon.Chase.lockonDuration = 3
earthdragon.Chase.tweenOut = 5
earthdragon.Chase.cooldownDuration = 7
earthdragon.Chase.marker = {}
earthdragon.Chase.marker[1] = "DRAGONCHASE"
local CreatureDatabase = {
  dragon00 = "Dragon",
  earthdragon00 = "EarthDragon"
}
local GetCreatureType = function(creature)
  local name = creature:GetName()
  name = string.lower(name)
  local creatureType = CreatureDatabase[name]
  return creatureType
end
local GetCreaturesMoves = function(creaturetype)
  local moves = {}
  local moveIndex = 0
  for k, _ in pairs(MoveDatabase[creaturetype]) do
    moveIndex = moveIndex + 1
    moves[moveIndex] = k
  end
  return moves
end
local timer_StopLockon
local bLockOnActivated = false
local largeCreatureTarget, largeCreatureMoveName
function OnScriptLoaded(level, camera)
  camera.TiltUp = camera.initial.TiltUp
  camera.TiltDown = camera.initial.TiltUp
  camera.AngleOfView = camera.initial.AngleOfView
  camera.Distance = camera.initial.Distance
  camera.FStop = camera.initial.FStop
  camera.Roll = camera.initial.Roll
  camera.PlayerFrame.Top = camera.initial.PlayerFrame.Top
  camera.PlayerFrame.Bottom = camera.initial.PlayerFrame.Bottom
  playerCreature = game.Player.FindPlayer()
  EL.SetupEaseTable(tbl_AutoAdjust)
  EL.SetCallback(tbl_AutoAdjust, DoneBlendingCallback)
end
function OnUpdate(level, camera)
  local t = MoveDatabase
  local orbitElevation = game.Camera.GetOrbitElevation()
  local adjustedPlayerFrame
  if playerCreature == nil then
    playerCreature = game.Player.FindPlayer()
  end
  if bPlayerAttacking == false and timer_attackCooldown.running == false then
    CL.CameraUpdateAttribute(camera, orbitElevation, -10, 32, "PlayerFrame.Right", -0.55)
    CL.CameraUpdateAttribute(camera, orbitElevation, -10, 32, "PlayerFrame.Left", -0.55)
    CL.CameraUpdateAttribute(camera, orbitElevation, -10, 32, "PlayerFrame.Top", -0.5)
    CL.CameraUpdateAttribute(camera, orbitElevation, -10, 32, "PlayerFrame.Bottom", -0.5)
  end
  if camera.name == "CBT_SuperClose_VeryLarge_Alt2" then
    if playerCreature:HasMarker("Attacking") then
      local attackTarget = playerCreature:GetTargetCreature()
      local attackTargetName
      if attackTarget ~= nil then
        attackTargetName = attackTarget:GetName()
      end
      if bPlayerAttacking == false and (attackTargetName == "dragon00" or attackTargetName == "earthdragon00") then
        EL.SetBlendValue(camera, tbl_attackAdjust, "Distance", 4)
        EL.SetBlendValue(camera, tbl_attackAdjust, "PlayerFrame.Top", -0.5)
        EL.SetBlendValue(camera, tbl_attackAdjust, "PlayerFrame.Bottom", -0.5)
        EL.SetStartValue(camera, tbl_attackAdjust)
        local duration = 0.5
        EL.SetDuration(tbl_attackAdjust, duration)
        EL.SetProgress(tbl_attackAdjust, true)
        bPlayerAttacking = true
        timer_attackCooldown:Reset()
        timer_attackCooldown:Stop()
      end
    elseif bPlayerAttacking == true then
      EL.SetBlendValue(camera, tbl_attackAdjust, "Distance", camera.initial.Distance)
      EL.SetBlendValue(camera, tbl_attackAdjust, "PlayerFrame.Top", camera.initial.PlayerFrame.Top)
      EL.SetBlendValue(camera, tbl_attackAdjust, "PlayerFrame.Bottom", camera.initial.PlayerFrame.Bottom)
      EL.SetStartValue(camera, tbl_attackAdjust)
      local duration = 1
      EL.SetDuration(tbl_attackAdjust, duration)
      EL.SetProgress(tbl_attackAdjust, true)
      bPlayerAttacking = false
      timer_attackCooldown:Start()
    end
    EL.EaseEverything(level, tbl_attackAdjust, camera, "InAndOut", "Quad")
  end
  if camera.name == "CBT_SuperClose_Dragon" then
    if largeCreatureTarget == nil then
      ListenForMoves(camera)
    else
      UniversalAutoAdjust(level, camera)
    end
    EL.EaseEverything(level, tbl_AutoAdjust, camera, "InAndOut", "Quad")
    EL.EaseEverything(level, tbl_Snapshot, camera, "InAndOut", "Quad")
    if bDoneBlending and not bOneTimeDone then
      timer_StopLockon:Start()
      bOneTimeDone = true
      bLockOnActivated = true
    end
    if bTriggered and timer_StopLockon.fired == true then
      timer_StopLockon:Reset()
      timer_StopLockon:Stop()
      EL.SetStartValue(camera, tbl_Snapshot)
      EL.SetProgress(tbl_Snapshot, true)
      bDoneBlending = false
      bOneTimeDone = false
      bLockOnActivated = false
    end
  end
end
function OnTeardown(level, camera)
  EL.Reset()
end
function OnSaveCheckpoint(level)
end
function OnRestoreCheckpoint(level, go, tab)
end
function DoneBlendingCallback()
  bDoneBlending = true
end
function AddMoveToCooldownTable(creaturetype, movename)
  local move = movename
  local cooldownDuration = MoveDatabase[creaturetype][move].cooldownDuration
  if CooldownTimer[move] == nil then
    local timer_Cooldown = timer.StartLevelTimer(cooldownDuration)
    AddTimerToTable(timer_Cooldown, move)
    timer_Cooldown:Start()
  else
    local timer = CooldownTimer[move]
    local remainingTime = timer:GetRemainingTime()
    if remainingTime < 0 then
      timer:Reset()
      timer:Stop()
      timer:Start()
    end
  end
end
function LockonCallback()
  bTriggered = false
  largeCreatureTarget = nil
end
function SetLockonTimer(creaturetype, movename)
  local lockonDuration = MoveDatabase[creaturetype][movename].lockonDuration
  timer_StopLockon = timer.StartLevelTimer(lockonDuration)
  timer_StopLockon:Stop()
end
function HasMarkerForMove(creaturetype, move, gameobject)
  local markers = MoveDatabase[creaturetype][move].marker
  local index = 0
  local numTimesFoundTrue = 0
  local bHasMarker = false
  for _, markername in pairs(markers) do
    index = index + 1
    if gameobject:HasMarker(markername) then
      numTimesFoundTrue = numTimesFoundTrue + 1
    end
  end
  if numTimesFoundTrue == index then
    bHasMarker = true
  end
  return bHasMarker
end
function ListenForMoves(camera)
  if playerCreature == nil then
    playerCreature = game.Player.FindPlayer()
  end
  local enemiesNearby = playerCreature:FindEnemies(kfCameraMaxFightRadius)
  if largeCreatureTarget == nil then
    local enemy
    local bFoundTheMove = false
    for _, thisEnemy in ipairs(enemiesNearby) do
      local creaturetype = GetCreatureType(thisEnemy)
      if thisEnemy and (creaturetype == "EarthDragon" or creaturetype == "Dragon") then
        local moves = GetCreaturesMoves(creaturetype)
        bFoundTheMove = false
        for _, move in pairs(moves) do
          bFoundTheMove = HasMarkerForMove(g_CreatureType, move, thisEnemy)
          if bFoundTheMove then
            largeCreatureMoveName = move
            enemy = thisEnemy
            break
          end
        end
        if bFoundTheMove then
          break
        end
      end
    end
    if bFoundTheMove and IsCooledDown(largeCreatureMoveName) then
      local halfOfCameraFov = camera.fov / 2
      local frontAngleXZ = CL.FrontAngleXZ(enemy:GetWorldPosition())
      if halfOfCameraFov > frontAngleXZ and frontAngleXZ > -halfOfCameraFov then
        largeCreatureTarget = enemy
      end
      SetLockonTimer(g_CreatureType, largeCreatureMoveName)
    end
  end
end
function UniversalAutoAdjust(level, camera)
  local moveDB = MoveDatabase[g_CreatureType][largeCreatureMoveName]
  assert(moveDB, "moveDB = MoveDatabase[g_CreatureType][largeCreatureMoveName]")
  local jointName = moveDB.jointName
  local jointIndex = largeCreatureTarget:GetJointIndex(jointName)
  local jointPosition = largeCreatureTarget:GetWorldJointPosition(jointIndex)
  local jointAngle, jointAngleRad = CL.FrontAngleYZ(jointPosition)
  local adjustedAngle = jointAngle - game.Camera.GetOrbitElevation()
  local magicOffsetAngle = 7.5
  if largeCreatureTarget and not bTriggered then
    bTriggered = true
    EL.RemoveTheAttributes(tbl_AutoAdjust)
    EL.RemoveTheAttributes(tbl_Snapshot)
    local duration_TweenIn = moveDB.tweenIn
    EL.SetDuration(tbl_AutoAdjust, duration_TweenIn)
    EL.SetBlendValue(camera, tbl_AutoAdjust, "TiltUp", adjustedAngle - magicOffsetAngle)
    EL.SetBlendValue(camera, tbl_AutoAdjust, "TiltDown", adjustedAngle - magicOffsetAngle)
    if HasBlendsForMove(g_CreatureType, largeCreatureMoveName) then
      AddTheBlends(camera, tbl_AutoAdjust, g_CreatureType, largeCreatureMoveName)
    end
    EL.CopyBlendTable(tbl_AutoAdjust, tbl_Snapshot)
    EL.TakeSnapshot(camera, tbl_Snapshot)
    EL.SetCallback(tbl_Snapshot, LockonCallback)
    local duration_TweenOut = moveDB.tweenOut
    EL.SetDuration(tbl_Snapshot, duration_TweenOut)
    EL.SetProgress(tbl_AutoAdjust, true)
    AddMoveToCooldownTable(g_CreatureType, largeCreatureMoveName)
    bTriggered = true
    return
  end
  if bLockOnActivated then
    camera.TiltUp = adjustedAngle - magicOffsetAngle
    camera.TiltDown = adjustedAngle - magicOffsetAngle
  elseif bTriggered then
    EL.AdjustBlendValue(camera, tbl_AutoAdjust, "TiltUp", adjustedAngle - magicOffsetAngle)
    EL.AdjustBlendValue(camera, tbl_AutoAdjust, "TiltDown", adjustedAngle - magicOffsetAngle)
  end
end
function HasBlendsForMove(creaturetype, move)
  local blends = MoveDatabase[creaturetype][move].blends
  if blends == nil then
    return false
  end
  return true
end
function AddTheBlends(camera, table, creaturetype, move)
  local blends = MoveDatabase[creaturetype][move].blends
  for attribute, value in pairs(blends) do
    EL.SetBlendValue(camera, table, attribute, value)
  end
  return true
end
