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 bUseJointTracking
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 = "Troll"
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 troll = {}
MoveDatabase.Troll = troll
troll.Stunned = {}
troll.Stunned.jointName = "JOHead1"
troll.Stunned.tweenIn = 0.5
troll.Stunned.lockonDuration = 1
troll.Stunned.tweenOut = 5
troll.Stunned.cooldownDuration = 15
troll.Stunned.marker = {}
troll.Stunned.marker[1] = "HitStagger"
troll.FootStomp = {}
troll.FootStomp.jointName = "JOLeftAnkle1"
troll.FootStomp.tweenIn = 1.2
troll.FootStomp.lockonDuration = 0.1
troll.FootStomp.tweenOut = 2
troll.FootStomp.cooldownDuration = 5
troll.FootStomp.marker = {}
troll.FootStomp.marker[1] = "Stomp"
troll.FootStomp.blends = {}
troll.FootStomp.blends.fov = 81
troll.FootStomp.blends.Roll = -3
troll.FootStomp.blends.Distance = 3.2
troll.Fireball = {}
troll.Fireball.jointName = "JORightMiddleC1Tip"
troll.Fireball.tweenIn = 2
troll.Fireball.lockonDuration = 0.5
troll.Fireball.tweenOut = 2
troll.Fireball.cooldownDuration = 5
troll.Fireball.marker = {}
troll.Fireball.marker[1] = "Magic"
troll.Fireball.marker[2] = "Hurl"
troll.Fireball.blends = {}
troll.Fireball.blends.fov = 60
troll.Fireball.blends.FocusDistance = 8.9
troll.Fireball.blends.FStop = 2.8
troll.TwoHandedSlam = {}
troll.TwoHandedSlam.jointName = "JORightMiddleC1Tip"
troll.TwoHandedSlam.tweenIn = 1.2
troll.TwoHandedSlam.lockonDuration = 1
troll.TwoHandedSlam.tweenOut = 2
troll.TwoHandedSlam.cooldownDuration = 6
troll.TwoHandedSlam.marker = {}
troll.TwoHandedSlam.marker[1] = "Slam"
troll.TwoHandedSlam.blends = {}
troll.TwoHandedSlam.blends.Distance = 4.6
troll.TwoHandedSlam.blends.fov = 81
troll.TwoHandedSlam.blends["PlayerFrame.Top"] = -0.2
troll.TwoHandedSlam.blends["PlayerFrame.Bottom"] = -0.2
troll.Spike = {}
troll.Spike.jointName = "JORightMiddleC1Tip"
troll.Spike.tweenIn = 0.5
troll.Spike.lockonDuration = 1.25
troll.Spike.tweenOut = 0.6
troll.Spike.cooldownDuration = 30
troll.Spike.marker = {}
troll.Spike.marker[1] = "Spike"
troll.E3FinalSmash = {}
troll.E3FinalSmash.jointName = "JORightMiddleC1Tip"
troll.E3FinalSmash.tweenIn = 1.2
troll.E3FinalSmash.lockonDuration = 2
troll.E3FinalSmash.tweenOut = 1
troll.E3FinalSmash.cooldownDuration = 5
troll.E3FinalSmash.marker = {}
troll.E3FinalSmash.marker[1] = "E3FinalSmash"
troll.E3FinalSmash.blends = {}
troll.E3FinalSmash.blends.fov = 100
troll.E3FinalSmash.blends.FocusDistance = 8.9
troll.E3FinalSmash.blends.FStop = 2.8
troll.E3FinalSmash.blends["PlayerFrame.Top"] = -0.2
troll.E3FinalSmash.blends["PlayerFrame.Bottom"] = -0.2
local CreatureDatabase = {
  draugr00 = "Draugr",
  jotunn10 = "Jotunn",
  son00 = "Son",
  traveler00 = "Traveler",
  troll00 = "Troll",
  troll10 = "Troll",
  troll20 = "Troll",
  valkyrie00 = "Valkyrie",
  witch00 = "Witch",
  witch10 = "Witch",
  witch20 = "Witch",
  wolf00 = "Wolf",
  wolf10 = "Wolf",
  wulver00 = "Wulver"
}
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)
  bUseJointTracking = false
  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 == "troll00" 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_VeryLarge" or camera.name == "CBT_SuperClose_VeryLarge_Alt2" or camera.name == "CBT_SuperClose_VeryLarge_Alt1") and bUseJointTracking == true 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 == "Troll" 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
