local LD = require("design.LevelDesignLibrary")
local DL = require("design.DesignerLibrary")
local bboardUtil = require("game.BlackboardUtil")
local orbObject
local projectileCount = 6
local lifetime = 7
local projectiles = {}
local origin
local delayedInit = 0
local unitTime = 0.032
local arrows = {}
local arrowTypeIsLight = true
local upgradeLevel = 3
local trailName = "Bifrost_Crows"
local concussionType = "Light_Collision_Random_"
local son
local startBirds = false
local spawnMaterialDone = true
local spawnMaterialAnimName = "creatureON"
local summonFXName = "Air_Summon_Spawn"
local rumbleParams = {EffectName = "FFB_MEDIUM", Duration = 0.3}
local shakeParams = {
  EffectName = "FSE_SHAKE_GENERIC_LARGE",
  Duration = 0.3
}
function OnScriptLoaded(level, go, ...)
  son = game.AI.FindSon()
  if son:PickupIsAcquired("SonArrow_Shock") then
    arrowTypeIsLight = false
    concussionType = "Shock_Collision_Random_"
    trailName = "Electric_Crows"
    spawnMaterialAnimName = "creature_shock"
    summonFXName = "SonTeleport_Enter_Shock"
  end
  upgradeLevel = game.Wallets.GetResourceValue("HERO", "SummonSouls")
  local hero = game.Player.FindPlayer()
  if hero:PickupIsAcquired("SummonSouls") then
    upgradeLevel = hero:PickupGetStage("SummonSouls") + 1
  end
  if upgradeLevel == nil or upgradeLevel <= 0 then
    upgradeLevel = 1
  end
  if upgradeLevel == 1 then
    projectileCount = 6
    lifetime = 5
  elseif upgradeLevel == 2 then
    projectileCount = 9
    lifetime = 6
  else
    projectileCount = 12
    lifetime = 6
  end
  SpawnArrowLogic(level, go)
end
function FindLivingEnemiesExludeMarker(ai, radius, marker)
  local enemies = ai:FindEnemies(radius)
  for i = #enemies, 1, -1 do
    if enemies[i]:GetCreature():GetHitPoints() <= 0 or enemies[i]:HasMarker(marker) or enemies[i]:HasMarker("Dormant") or enemies[i]:HasMarker("DoNotEvaluate") then
      table.remove(enemies, i)
    end
  end
  return enemies
end
function InitBirds(level, go, arrowPos)
  local player = game.Player.FindPlayer()
  origin = arrowPos
  for i = 1, projectileCount do
    local arrow = {}
    arrow.lifetime = lifetime + i * 0.5
    arrow.state = "init"
    arrow.gameObject = game.FX.Spawn("crow10", level)
    arrow.direction = engine.Vector.New(math.random() - 0.5, math.random() * 0.5 - 0.25, math.random() - 0.5):Normalized()
    arrow.gameObject:SetWorldPosition(origin)
    arrow.pursueTarget = nil
    arrow.trail = game.FX.Spawn(trailName, level)
    arrow.trail:SetWorldPosition(origin)
    arrow.sinHeight = 0.15
    arrow.sinValue = math.random() * 6.28
    if arrowTypeIsLight == false then
      arrow.speed = 16 + math.random() * 5
      arrow.rotateSpeed = 720 + math.random() * 120
      arrow.pursueTime = 0.75
    else
      arrow.speed = 10 + math.random() * 5
      arrow.rotateSpeed = 270 + math.random() * 120
      arrow.pursueTime = 1.5
    end
    table.insert(projectiles, arrow)
  end
  local spawnfx = game.FX.Spawn(summonFXName, level, {AutoDelete = true})
  spawnfx:SetWorldPosition(origin)
  game.FX.SubmitEffect(rumbleParams)
  game.FX.SubmitEffect(shakeParams)
end
function SetBirdPosition(level, thisArrow)
  local pos = thisArrow.gameObject.WorldPosition + thisArrow.direction * thisArrow.speed * unitTime + engine.Vector.New(0, math.sin(thisArrow.sinValue) * thisArrow.sinHeight, 0)
  thisArrow.gameObject:SetWorldPosition(pos)
  thisArrow.trail:SetWorldPosition(pos + engine.Vector.New(0, 0.35, 0))
  thisArrow.gameObject:SetWorldFacing(thisArrow.direction)
  thisArrow.trail:SetWorldFacing(thisArrow.direction)
  if arrowTypeIsLight == false then
    thisArrow.trail:SetScale(1.25)
  else
    thisArrow.trail:SetScale(0.5)
  end
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 == "init" then
      thisProjectile.gameObject:StartMaterialAnim("navFlapFast")
      thisProjectile.state = "findTarget"
      thisProjectile.gameObject:ShowJoint(thisProjectile.gameObject:GetJointIndex("crow00_stream"))
      if spawnMaterialDone then
        thisProjectile.gameObject:StartMaterialAnim(spawnMaterialAnimName)
        SoundSpawnBirds(thisProjectile)
      end
    elseif thisProjectile.state == "findTarget" then
      thisProjectile.state = "pursue"
      SoundLoop(thisProjectile)
      local targets = FindLivingEnemiesExludeMarker(player, 50, "DoNotTarget")
      local numTargets = #targets
      if 0 < numTargets then
        thisProjectile.pursueTarget = targets[math.random(1, numTargets)]
        thisProjectile.pursueTime = 1.5
        local combatCollision = thisProjectile.gameObject:FindSingleGOByName("CombatCollisionManager")
        combatCollision.LuaObjectScript.Enable()
        combatCollision.LuaObjectScript.ChangeCombatCollisionType(concussionType .. math.random(0, 3))
      end
    elseif thisProjectile.state == "pursue" then
      local target = player.WorldPosition + player:GetWorldForward() * 10
      if thisProjectile.pursueTarget ~= nil and thisProjectile.pursueTarget:IsDoingSyncMove() == false then
        local jindex = thisProjectile.pursueTarget:FindJointIndex("JOHead1")
        if jindex == nil then
          jindex = thisProjectile.pursueTarget:GetJointIndex("zerojoint")
        end
        target = thisProjectile.pursueTarget:GetWorldJointPosition(jindex)
        local multi = (target - thisProjectile.gameObject.WorldPosition):Length() / 3
        if 1 < multi then
          multi = 1
        end
        thisProjectile.sinHeight = 0.15 * multi
      end
      local projectileRight = -thisProjectile.gameObject:GetWorldLeft()
      local projectileToTarget = (thisProjectile.gameObject.WorldPosition - target):Normalized()
      local scalarValue = projectileToTarget:Dot(projectileRight)
      if 0.001 < scalarValue then
        thisProjectile.direction = thisProjectile.direction:RotateXZ(-thisProjectile.rotateSpeed * unitTime)
      elseif scalarValue < -0.001 then
        thisProjectile.direction = thisProjectile.direction:RotateXZ(thisProjectile.rotateSpeed * unitTime)
      end
      thisProjectile.direction.y = (target.y - thisProjectile.gameObject.WorldPosition.y) * 0.1
      thisProjectile.direction = thisProjectile.direction:Normalized()
      thisProjectile.pursueTime = thisProjectile.pursueTime - unitTime
      if 0 >= thisProjectile.pursueTime then
        thisProjectile.state = "findTarget"
      end
    end
    thisProjectile.lifetime = thisProjectile.lifetime - unitTime
    thisProjectile.sinValue = thisProjectile.sinValue + unitTime * 3.14
    SetBirdPosition(level, thisProjectile)
    if 0 >= thisProjectile.lifetime then
      local spawnfx = game.FX.Spawn(summonFXName, level, {AutoDelete = true})
      spawnfx:SetWorldPosition(thisProjectile.gameObject.WorldPosition)
      SoundDespawn(thisProjectile)
      thisProjectile.gameObject:Destroy()
      if thisProjectile.trail ~= nil then
        thisProjectile.trail:Destroy()
      end
      table.remove(projectiles, i)
    end
  end
end
function SpawnArrows(level, go, location, timer)
  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
  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
  table.insert(arrows, arrow)
  SoundSpawnArrows()
end
function UpdateArrows(level, go)
  local sizeArrows = #arrows
  for i = sizeArrows, 1, -1 do
    local thisProjectile = arrows[i]
    if thisProjectile.timer <= 0 then
      startBirds = true
      InitBirds(level, go, thisProjectile.gameObject.WorldPosition)
      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 sonSpawnArrowLocation = game.Camera.GetOrbitPosition() + camForward * 7 + engine.Vector.New(0, 1, 0)
  SpawnArrows(level, go, sonSpawnArrowLocation, 0.15)
end
function OnUpdate(level, go)
  unitTime = level:GetUnitTime()
  UpdateArrows(level, go)
  if startBirds then
    if 0 < delayedInit then
      UpdateBirds(level)
      if #projectiles <= 0 then
        go:Destroy()
      end
    end
    delayedInit = 1
  end
end
function DestroySummons()
  local sizeProjectile = #projectiles
  for i = sizeProjectile, 1, -1 do
    local thisProjectile = projectiles[i]
    thisProjectile.lifetime = i * 0.2
  end
end
function SoundSpawnArrows()
end
function SoundSpawnBirds(crow)
  if arrowTypeIsLight == true then
    LD.PlaySoundRingOut(crow.gameObject:FindSingleSoundEmitterByName("SNDCrow00"), "SND_CHR_Son_Magic_Summon_Crows_A_Spawn")
  else
    LD.PlaySoundRingOut(crow.gameObject:FindSingleSoundEmitterByName("SNDCrow00"), "SND_CHR_Son_Magic_Summon_Crows_B_Spawn")
  end
end
function SoundLoop(crow)
  if arrowTypeIsLight == true then
    LD.PlaySound(crow.gameObject:FindSingleSoundEmitterByName("SNDCrow00"), "SND_CHR_Son_Magic_Summon_Crows_A_LP")
  else
    LD.PlaySound(crow.gameObject:FindSingleSoundEmitterByName("SNDCrow00"), "SND_CHR_Son_Magic_Summon_Crows_B_LP")
  end
end
function SoundDespawn(crow)
  if arrowTypeIsLight == true then
    LD.PlaySoundRingOut(crow.gameObject:FindSingleSoundEmitterByName("SNDCrow00"), "SND_CHR_Son_Magic_Dissapate_A")
  else
    LD.PlaySoundRingOut(crow.gameObject:FindSingleSoundEmitterByName("SNDCrow00"), "SND_CHR_Son_Magic_Dissapate_B")
  end
end
