local timer = require("level.timer")
local LD = require("design.LevelDesignLibrary")
local DL = require("design.DesignerLibrary")
local bboardUtil = require("game.BlackboardUtil")
local orbObject
local projectileCount = 26
local projectiles = {}
local origin
local delayedInit = 0
local unitTime = 0.032
local arrowTypeIsLight = true
local arrows = {}
local son
local upgradeLevel = 3
local spawnMaterialDone = true
local spawnMaterialAnimName = "creatureON"
local summonFXName = "Air_Summon_Spawn"
local birdEmitter
local initAudio = false
local diveAudio = false
local impactAudio = false
local trailTable = {}
local rumbleParams = {EffectName = "FFB_MEDIUM", Duration = 0.3}
local shakeParams = {
  EffectName = "FSE_SHAKE_GENERIC_MEDIUM",
  Duration = 0.3
}
function OnScriptLoaded(level, go, ...)
  son = game.AI.FindSon()
  if son:PickupIsAcquired("SonArrow_Shock") then
    arrowTypeIsLight = false
    spawnMaterialAnimName = "creature_shock"
    summonFXName = "SonTeleport_Enter_Shock"
  end
  upgradeLevel = game.Wallets.GetResourceValue("HERO", "SummonBirds")
  local hero = game.Player.FindPlayer()
  if hero:PickupIsAcquired("SummonBird") then
    upgradeLevel = hero:PickupGetStage("SummonBird") + 1
  end
  if upgradeLevel == nil or upgradeLevel <= 0 then
    upgradeLevel = 1
  end
end
function SummonBirds(level, go, spawnLoc, count, location)
  local spawnfx = game.FX.Spawn(summonFXName, level, {AutoDelete = true})
  spawnfx:SetWorldPosition(spawnLoc)
  for _ = 1, count do
    local arrow = {}
    arrow.lifetime = 4
    arrow.speed = 1
    arrow.maxSpeed = math.random() * 15 + 15
    arrow.acceleration = 6
    arrow.timer = 0
    arrow.state = "spawnIntro"
    arrow.attackTimer = 0.63
    arrow.attackTimerMax = 3
    arrow.gameObject = game.FX.Spawn("falcon01", level)
    arrow.direction = engine.Vector.New(math.random() - 0.5, math.random() * 1, math.random() - 0.5):Normalized()
    arrow.rotateSpeed = 120
    arrow.gameObject:SetWorldPosition(spawnLoc + engine.Vector.New(math.random() - 0.1, math.random() * 0.5, math.random() - 0.1))
    arrow.goal = location + engine.Vector.New((math.random() - 0.5) * 3, 0, (math.random() - 0.5) * 3)
    arrow.spawned = false
    table.insert(projectiles, arrow)
  end
end
function SpawnArrows(level, go, location, timer, groundGoal)
  local spawnLocation = son:GetWorldJointPosition(son:GetJointIndex("JORightIndexA1"))
  local arrow = {}
  local distance = (spawnLocation - location):Length()
  arrow.location = location
  arrow.direction = (location - spawnLocation):Normalized()
  arrow.speed = distance / timer
  arrow.timer = timer
  arrow.gameObject = nil
  arrow.groundGoal = groundGoal
  if arrowTypeIsLight then
    arrow.gameObject = game.FX.Spawn("bifrost_projectile", level)
    arrow.gameObject:SetWorldPosition(spawnLocation)
  else
    arrow.gameObject = game.FX.Spawn("ElectricShock_Arrow", level)
    arrow.gameObject:SetWorldPosition(spawnLocation)
  end
  SoundSpawnArrows()
  table.insert(arrows, arrow)
end
function UpdateArrows(level, go)
  local birdCount = 3
  if upgradeLevel == 1 then
    birdCount = 4
  elseif upgradeLevel == 2 then
    birdCount = 5
  elseif upgradeLevel == 3 then
    birdCount = 6
  end
  local sizeArrows = #arrows
  for i = sizeArrows, 1, -1 do
    local thisProjectile = arrows[i]
    if thisProjectile.timer <= 0 then
      local pos = game.NavMesh.ClosestLocation(thisProjectile.groundGoal, son)
      if pos ~= nil and pos.Position ~= nil then
        SummonBirds(level, go, thisProjectile.gameObject.WorldPosition, birdCount, pos.Position)
        game.FX.SubmitEffect(rumbleParams)
        game.FX.SubmitEffect(shakeParams)
      end
      thisProjectile.gameObject:Destroy()
      table.remove(arrows, i)
    else
      thisProjectile.timer = thisProjectile.timer - unitTime
      local newPos = thisProjectile.gameObject.WorldPosition + thisProjectile.direction * unitTime * thisProjectile.speed
      thisProjectile.gameObject:SetWorldPosition(newPos)
      thisProjectile.gameObject:SetWorldFacing(thisProjectile.direction)
    end
  end
end
function SpawnArrowLogic(level, go)
  local camForward = game.Camera.GetOrbitForward()
  camForward.y = 0
  local playerPos = game.Camera.GetOrbitPosition() + camForward * 3
  playerPos.y = game.Player.FindPlayer().WorldPosition.y
  local spawnlocation, groundGoal
  local totalArrow = upgradeLevel
  for i = 1, totalArrow do
    groundGoal = playerPos + camForward * 4 * i
    spawnlocation = groundGoal + engine.Vector.New(0, 1.5, 0)
    SpawnArrows(level, go, spawnlocation, 0.15 + (i - 1) * 0.2, groundGoal)
  end
end
function OnUpdate(level, go)
  unitTime = level:GetUnitTime()
  if delayedInit == 0 then
    SpawnArrowLogic(level, go)
    delayedInit = delayedInit + 1
    return
  end
  if 0 < #trailTable then
    for i = #trailTable, 1, -1 do
      local thisTrail = trailTable[i]
      if thisTrail ~= nil then
        if thisTrail.Spawned == true then
          thisTrail:Destroy()
          table.remove(trailTable, i)
        end
      else
        table.remove(trailTable, i)
      end
    end
  end
  UpdateArrows(level, go)
  UpdateBirds(level)
  if #projectiles <= 0 and #arrows <= 0 and #trailTable <= 0 then
    go:Destroy()
  end
end
function SetBirdPosition(level, thisArrow)
  if thisArrow.spawned == false then
    thisArrow.spawned = true
    return
  end
  local pos = thisArrow.gameObject.WorldPosition + thisArrow.direction * thisArrow.speed * unitTime
  thisArrow.gameObject:SetWorldPosition(pos)
  thisArrow.gameObject:SetWorldFacing(thisArrow.direction)
end
function UpdateBirds(level)
  local sizeProjectile = #projectiles
  local player = game.Player:FindPlayer()
  for i = sizeProjectile, 1, -1 do
    local thisProjectile = projectiles[i]
    if thisProjectile.state == "spawnIntro" then
      thisProjectile.state = "spawnAnim"
    elseif thisProjectile.state == "spawnAnim" then
      local randomCount = math.random(1, 3)
      if i == 1 then
        randomCount = 1
      end
      thisProjectile.gameObject:StartMaterialAnim("navFlyFlapDive" .. randomCount .. "m")
      thisProjectile.state = "burstAway"
      local pos = thisProjectile.gameObject.WorldPosition
      local direction = (thisProjectile.goal - pos):Normalized()
      direction.y = 0
      thisProjectile.direction = direction
      thisProjectile.gameObject:SetWorldFacing(thisProjectile.direction)
      thisProjectile.gameObject:ShowJoint(thisProjectile.gameObject:GetJointIndex("falcon_stream"))
      if spawnMaterialDone then
        thisProjectile.gameObject:StartMaterialAnim(spawnMaterialAnimName)
      end
      SoundSpawnBird(thisProjectile)
      if arrowTypeIsLight then
        thisProjectile.trail = game.FX.Spawn("bifrost_projectile", level)
      else
        thisProjectile.trail = game.FX.Spawn("ElectricShock_Arrow", level)
      end
      thisProjectile.trail:SetWorldPosition(thisProjectile.gameObject:GetWorldJointPosition(thisProjectile.gameObject:GetJointIndex("JOPelvis1")) + thisProjectile.gameObject:GetWorldUp() * 0.25)
      thisProjectile.trail:SetWorldFacing(direction)
    elseif thisProjectile.state == "burstAway" then
      local pos = thisProjectile.gameObject.WorldPosition
      local direction = (thisProjectile.goal - pos):Normalized()
      thisProjectile.attackTimer = thisProjectile.attackTimer - unitTime
      if 0 >= thisProjectile.attackTimer then
        thisProjectile.state = "divebombEnter"
      end
      thisProjectile.trail:SetWorldPosition(thisProjectile.gameObject:GetWorldJointPosition(thisProjectile.gameObject:GetJointIndex("JOPelvis1")) + thisProjectile.gameObject:GetWorldUp() * 0.25)
      thisProjectile.trail:SetWorldFacing(direction)
    elseif thisProjectile.state == "divebombEnter" then
      local pos = thisProjectile.gameObject.WorldPosition
      local direction = (thisProjectile.goal - pos):Normalized()
      direction = (direction + engine.Vector.New(0, 2, 0)):Normalized()
      thisProjectile.direction = direction
      thisProjectile.speed = thisProjectile.speed * 0.75
      thisProjectile.timer = thisProjectile.timer - unitTime
      thisProjectile.trail:SetWorldPosition(thisProjectile.gameObject:GetWorldJointPosition(thisProjectile.gameObject:GetJointIndex("JOPelvis1")) + thisProjectile.gameObject:GetWorldUp() * 0.25)
      thisProjectile.trail:SetWorldFacing(direction)
      if 0 >= thisProjectile.timer then
        thisProjectile.state = "divebomb"
      end
      SoundDive()
    elseif thisProjectile.state == "divebomb" then
      local pos = thisProjectile.gameObject:GetWorldJointPosition(thisProjectile.gameObject:GetJointIndex("JOPelvis1"))
      local direction = (thisProjectile.goal - pos):Normalized()
      thisProjectile.direction = direction
      thisProjectile.speed = 18
      thisProjectile.maxSpeed = 18
      thisProjectile.trail:SetWorldPosition(pos + thisProjectile.gameObject:GetWorldUp() * 0.25)
      thisProjectile.trail:SetWorldFacing(direction)
      if pos.y - thisProjectile.goal.y <= 0.35 then
        thisProjectile.state = "attack"
        thisProjectile.timer = 0.35
        local concussionParams = {}
        concussionParams.WorldLocation = thisProjectile.gameObject:GetWorldJointPosition(thisProjectile.gameObject:GetJointIndex("JOPelvis1"))
        concussionParams.Creature = game.AI.FindSon()
        concussionParams.EnemyId = DL.HashCreatureID(game.Player.FindPlayer(), "SON00")
        if arrowTypeIsLight then
          concussionParams.Tweak = "CNC_SON_RAINBIRD_EXPLOSION_" .. math.random(1, 3)
        else
          concussionParams.Tweak = "CNC_SON_RAINBIRD_EXPLOSION_SHOCK_" .. math.random(1, 3)
        end
        game.Combat.PlayConcussion(concussionParams)
        thisProjectile.lifetime = 0
        SoundImpact()
        game.FX.SubmitEffect(rumbleParams)
        game.FX.SubmitEffect(shakeParams)
      end
    elseif thisProjectile.state == "attack" then
      thisProjectile.timer = thisProjectile.timer - unitTime
      thisProjectile.speed = 10
      thisProjectile.maxSpeed = 10
      if thisProjectile.direction.y > -0.3 then
        thisProjectile.direction.y = thisProjectile.direction.y * 0.65
        thisProjectile.direction = thisProjectile.direction:Normalized()
      else
        thisProjectile.direction.y = thisProjectile.direction.y + 0.6
        thisProjectile.direction = thisProjectile.direction:Normalized()
      end
      if 0 >= thisProjectile.timer then
        thisProjectile.state = "return"
        thisProjectile.timer = 0.15
      end
    elseif thisProjectile.state == "return" then
      thisProjectile.timer = thisProjectile.timer - unitTime
      thisProjectile.speed = 16
      thisProjectile.maxSpeed = 16
      thisProjectile.sinValue = thisProjectile.sinValue + unitTime * 6.28
      thisProjectile.encircleRotation = thisProjectile.encircleRotation + thisProjectile.encircleRotationSpeed * unitTime
      local pos = engine.Vector.New(0, 0, thisProjectile.encircleDistance):RotateXZ(thisProjectile.encircleRotation) + engine.Vector.New(0, thisProjectile.height + math.sin(thisProjectile.sinValue) * 0.75, 0)
      local direction = (pos + thisProjectile.target.WorldPosition - thisProjectile.gameObject.WorldPosition):Normalized()
      thisProjectile.direction = direction
      if 0 >= thisProjectile.timer then
        thisProjectile.state = "encircle"
        thisProjectile.maxSpeed = 8
      end
    elseif thisProjectile.state == "death" then
      thisProjectile.speed = 8
    else
      thisProjectile.state = "death"
      thisProjectile.lifetime = 0.45 + math.random()
    end
    thisProjectile.lifetime = thisProjectile.lifetime - unitTime
    thisProjectile.speed = thisProjectile.speed + thisProjectile.acceleration * unitTime
    if thisProjectile.speed > thisProjectile.maxSpeed then
      thisProjectile.speed = thisProjectile.maxSpeed
    end
    if 0 >= thisProjectile.lifetime then
      thisProjectile.gameObject:Destroy()
      if thisProjectile.trail ~= nil then
        if thisProjectile.trail.Spawned == true then
          thisProjectile.trail:Destroy()
        else
          table.insert(trailTable, thisProjectile.trail)
        end
      end
      table.remove(projectiles, i)
    end
  end
end
function DestroySummons()
  local sizeProjectile = #projectiles
  for i = sizeProjectile, 1, -1 do
    local thisProjectile = projectiles[i]
    if thisProjectile ~= nil then
      thisProjectile.lifetime = 0
    end
  end
end
function SoundSpawnArrows()
end
function SoundSpawnBird(bird)
  if initAudio ~= true then
    birdEmitter = bird.gameObject:FindSingleSoundEmitterByName("SNDfalcon00")
    if arrowTypeIsLight == true then
      LD.PlaySoundRingOut(birdEmitter, "SND_CHR_Son_Magic_Summon_Falcons_A_Spawn")
      LD.PlaySound(birdEmitter, "SND_CHR_Son_Magic_Summon_Falcons_A_LP")
    else
      LD.PlaySoundRingOut(birdEmitter, "SND_CHR_Son_Magic_Summon_Falcons_B_Spawn")
      LD.PlaySound(birdEmitter, "SND_CHR_Son_Magic_Summon_Falcons_B_LP")
    end
    initAudio = true
  end
end
function SoundDive()
  if diveAudio ~= true then
    diveAudio = true
    if arrowTypeIsLight == true then
      LD.PlaySoundRingOut(birdEmitter, "SND_CHR_Son_Magic_Summon_Falcons_A_Dive")
    else
      LD.PlaySoundRingOut(birdEmitter, "SND_CHR_Son_Magic_Summon_Falcons_B_Dive")
    end
  end
end
function SoundImpact()
  if impactAudio ~= true then
    if arrowTypeIsLight == true then
      LD.PlaySoundRingOut(birdEmitter, "SND_CHR_Son_Magic_Summon_Falcons_A_Explo")
    else
      LD.PlaySoundRingOut(birdEmitter, "SND_CHR_Son_Magic_Summon_Falcons_B_Explo")
    end
    impactAudio = true
  end
end
