local LD = require("design.LevelDesignLibrary")
local DL = require("design.DesignerLibrary")
local levelTimer = require("level.timer")
local deer
local timer = 0
local locomotionSetting = {}
locomotionSetting.acceleration = 10
locomotionSetting.direction = engine.Vector.New(0, 0, 1)
locomotionSetting.speed = 1.05
locomotionSetting.maxSpeed = 1.05
locomotionSetting.location = engine.Vector.New(0, 0, 0)
local destination, son, legTrail1, ankleTrail1, legTrail2, ankleTrail2, target
local state = "init"
local lifeTime = 5
local unitTime, lineTrail
local counter = 0
local upgradeLevel = 0
local arrowTypeIsLight = true
local tickTimer = 0.75
local currentTickTimer = 0
local shotRadius = 10
local shockCount = 1
local explosion_walkType01 = "CNC_LIGHT_SUMMON_DEER_EXPLOSION_WALK_LV1_SPIN"
local explosion_walkType02 = "CNC_LIGHT_SUMMON_DEER_EXPLOSION_WALK_LV1_FLIP"
local spawnMaterialDone = true
local spawnMaterialAnimName = "creatureON"
local summonFXName = "Bifrost_Deer_Spawn"
local goSoundEmitter
local rumbleParams = {
  EffectName = "FFB_LOOP_MEDIUM",
  Duration = 0.3
}
local shakeParams = {
  EffectName = "FSE_SHAKE_GENERIC_LARGE",
  Duration = 0.3
}
local rumbleParams2 = {
  EffectName = "FFB_LOOP_SMALL",
  Duration = 0.4
}
local shakeParams2 = {
  EffectName = "FSE_SHAKE_GENERIC_SMALL",
  Duration = 0.4
}
function OnScriptLoaded(level, go)
  son = game.AI.FindSon()
  deer = game.FX.Spawn("deer01", level)
  local hero = game.Player.FindPlayer()
  local camForward = game.Camera.GetOrbitForward()
  camForward.y = 0
  local startPos = game.Camera.GetOrbitPosition()
  startPos.y = son.WorldPosition.y
  destination = hero.WorldPosition
  local navmeshLocation = game.NavMesh.ClosestLocation(destination, son)
  if navmeshLocation ~= nil and navmeshLocation.Position ~= nil then
    destination = navmeshLocation.Position
  end
  locomotionSetting.location = son.WorldPosition + (destination - son.WorldPosition):Normalized() * 2
  upgradeLevel = game.Wallets.GetResourceValue("HERO", "SummonDeer")
  if hero:PickupIsAcquired("SummonDeer") then
    upgradeLevel = hero:PickupGetStage("SummonDeer") + 1
  end
  if upgradeLevel == nil or upgradeLevel <= 0 then
    upgradeLevel = 1
  end
  if son:PickupIsAcquired("SonArrow_Shock") then
    arrowTypeIsLight = false
    spawnMaterialAnimName = "creature_shock"
    summonFXName = "SonTeleport_Enter_Shock"
  end
  if upgradeLevel == 1 then
    lifeTime = 8
    shotRadius = 6
    tickTimer = 0.75
  elseif upgradeLevel == 2 then
    lifeTime = 10
    shotRadius = 8
    tickTimer = 0.75
    shockCount = 2
  elseif upgradeLevel == 3 then
    lifeTime = 12
    shotRadius = 10
    tickTimer = 0.75
    shockCount = 2
  end
  rumbleParams2.Duration = lifeTime
  shakeParams2.Duration = lifeTime
  explosion_walkType01 = "CNC_LIGHT_SUMMON_DEER_EXPLOSION_WALK_LV" .. upgradeLevel .. "_SPIN"
  explosion_walkType02 = "CNC_LIGHT_SUMMON_DEER_EXPLOSION_WALK_LV" .. upgradeLevel .. "_FLIP"
  if arrowTypeIsLight then
    SetExplosionSequence(level)
  end
  deer:SetWorldPosition(locomotionSetting.location)
  local spawnfx = game.FX.Spawn(summonFXName, level, {AutoDelete = true})
  spawnfx:SetWorldPosition(locomotionSetting.location)
  SoundExplosion()
  game.FX.SubmitEffect(rumbleParams)
  game.FX.SubmitEffect(shakeParams)
end
function OnUpdate(level, go)
  if counter == 0 then
    counter = 1
    return
  end
  local player = game.Player.FindPlayer()
  unitTime = level:GetUnitTime()
  timer = timer + unitTime
  if deer ~= nil then
    local deerForward = deer:GetWorldForward()
    local deerLoc = deer:GetWorldJointPosition(deer:GetJointIndex("JOBack6"))
    local emitLocation = deerForward * 1.5 + engine.Vector.New(0, 0.5, 0) + deerLoc
    MoveLineTrail(level, go, emitLocation)
    if arrowTypeIsLight == false then
      EmitArrows(level, emitLocation)
    end
  end
  if lifeTime <= 0 and state ~= "wait" and state ~= "stop" and state ~= "dead" then
    state = "wait"
  end
  if state == "init" then
    deer:StartMaterialAnim("navWalkFInPlace")
    if spawnMaterialDone then
      deer:StartMaterialAnim(spawnMaterialAnimName)
      SoundSpawnDeer(deer)
    end
    state = "run"
    deer:ShowJoint(deer:GetJointIndex("deer_stream"))
    if arrowTypeIsLight == false then
      ankleTrail1 = game.FX.Spawn("Draugr_BShock_Strong_JOHead", level)
      ankleTrail2 = game.FX.Spawn("Draugr_BShock_Strong_JOHead", level)
    else
      ankleTrail1 = game.FX.Spawn("bifrost_deer_bodyGlow", level)
    end
    if arrowTypeIsLight then
      ankleTrail1:SetWorldPosition(deer:GetWorldJointPosition(deer:GetJointIndex("JOHead1")))
    else
      ankleTrail1:SetWorldPosition(locomotionSetting.location + engine.Vector.New(0, 1.25, 0))
    end
    local direction = (destination - deer.WorldPosition):Normalized()
    if 0 < direction:Length() then
      deer:SetWorldFacing(direction)
    end
  elseif state == "run" then
    local deerForward = deer:GetWorldForward()
    local direction = (destination - deer.WorldPosition):Normalized()
    direction.y = 0
    if 0 < direction:Length() then
      deer:SetWorldFacing(direction)
    end
    ankleTrail1:SetScale(4)
    locomotionSetting.location = locomotionSetting.location + direction * locomotionSetting.speed * level:GetUnitTime()
    deer:SetWorldPosition(locomotionSetting.location)
    if arrowTypeIsLight then
      ankleTrail1:SetWorldPosition(deer:GetWorldJointPosition(deer:GetJointIndex("JOHead1")))
    else
      ankleTrail1:SetWorldPosition(locomotionSetting.location + engine.Vector.New(0, 1.25, 0))
    end
    if (destination - locomotionSetting.location):Length() < 0.25 then
      state = "tryStop"
    end
  elseif state == "tryStop" then
    deer:StartMaterialAnim("navWalkExit")
    state = "wait"
    local spawnfx = game.FX.Spawn(summonFXName, level, {AutoDelete = true, Scale = 3.5})
    spawnfx:SetWorldPosition(locomotionSetting.location)
    SoundExplosion()
    local direction = deer:GetWorldForward()
    deer:SetWorldFacing(direction)
    locomotionSetting.location = locomotionSetting.location + direction * locomotionSetting.speed * level:GetUnitTime()
    deer:SetWorldPosition(locomotionSetting.location)
    if ankleTrail1 ~= nil then
      if arrowTypeIsLight then
        ankleTrail1:SetWorldPosition(deer:GetWorldJointPosition(deer:GetJointIndex("JOHead1")))
      else
        ankleTrail1:Remove()
      end
    end
  elseif state == "wait" then
    if ankleTrail1 ~= nil then
      if arrowTypeIsLight then
        ankleTrail1:SetWorldPosition(deer:GetWorldJointPosition(deer:GetJointIndex("JOHead1")))
      else
        ankleTrail1:Remove()
      end
    end
    if arrowTypeIsLight == false and lifeTime <= 0 then
      state = "stop"
    end
  elseif state == "stop" and lifeTime <= 0 then
    if deer ~= nil then
      local spawnfx = game.FX.Spawn(summonFXName, level, {AutoDelete = true, Scale = 2})
      spawnfx:SetWorldPosition(deer:GetWorldJointPosition(deer:GetJointIndex("JOBack6")))
      deer:Remove()
    end
    SoundExplosion()
    SoundDespawn()
    if lineTrail ~= nil then
      lineTrail:Remove()
    end
    if ankleTrail1 ~= nil then
      ankleTrail1:Remove()
    end
    if ankleTrail2 ~= nil then
      ankleTrail2:Remove()
    end
    go:Destroy()
    state = "dead"
  end
  lifeTime = lifeTime - unitTime
end
function SetExplosionSequence(level)
  for i = 1, lifeTime do
    levelTimer.StartLevelTimer(0.05 + (i - 1) * 1, function()
      EmitExplosion(level, explosion_walkType01)
    end)
  end
  levelTimer.StartLevelTimer(lifeTime + 0, function()
    EmitExplosion(level, explosion_walkType01)
  end)
  levelTimer.StartLevelTimer(lifeTime + 0.5, function()
    EmitExplosion(level, explosion_walkType01)
  end)
  levelTimer.StartLevelTimer(lifeTime + 1, function()
    EmitExplosion(level, explosion_walkType02)
    if deer == nil then
      return
    end
    if spawnMaterialDone then
      deer:StartMaterialAnim("creatureOFF")
    end
  end)
  levelTimer.StartLevelTimer(lifeTime + 1.25, function()
    state = "stop"
  end)
end
function EmitExplosion(level, cncName)
  if deer == nil then
    return
  end
  local newPos = deer:GetWorldJointPosition(deer:FindJointIndex("JOHead1"))
  local concussionParams = {}
  concussionParams.WorldLocation = newPos
  concussionParams.Creature = game.AI.FindSon()
  concussionParams.EnemyId = DL.HashCreatureID(game.Player.FindPlayer(), "SON00")
  SoundExplosionLight()
  if arrowTypeIsLight then
    concussionParams.Tweak = cncName
  end
  game.Combat.PlayConcussion(concussionParams)
end
function EmitArrows(level, newPos)
  if deer == nil then
    return
  end
  if currentTickTimer >= tickTimer then
    currentTickTimer = 0
    tickTimer = 0.4
    local player = game.Player.FindPlayer()
    local enemies = player:FindEnemies(100)
    local enemiesInRange = {}
    for _, i in ipairs(enemies) do
      if (i.WorldPosition - newPos):Length() < shotRadius then
        table.insert(enemiesInRange, i)
      end
    end
    for _ = 1, shockCount do
      if 0 < #enemiesInRange then
        local randomIndex = math.random(1, #enemiesInRange)
        local son = game.AI.FindSon()
        local arrowData = {}
        arrowData.Tweak = "ARR_ARROW_SHOCK_ARC_MINI_LUA"
        arrowData.Creator = son
        local angle = DL.GetAngleFromVectors(son:GetWorldForward(), engine.Vector.New(1, 0, 0))
        arrowData.EmitOffset = (newPos - son.WorldPosition - engine.Vector.New(0, 0, -1)):RotateXZ(angle - 90) + engine.Vector.New(0, 0.5, 0)
        arrowData.Target = enemiesInRange[randomIndex]
        local coreJoint = arrowData.Target:FindJointIndex("WeakspotCoreJoint")
        if coreJoint ~= nil then
          arrowData.TargetJointId = coreJoint
          arrowData.JointSpaceTargetPosition = arrowData.Target:GetWorldJointPosition(coreJoint)
        end
        game.Combat.EmitArrow(arrowData)
        SoundEmitArrow()
      end
    end
  end
  currentTickTimer = currentTickTimer + level:GetUnitTime()
end
function EmitShockWave(level, newPos)
  if currentTickTimer >= tickTimer then
    currentTickTimer = 0
    local concussionParams = {}
    concussionParams.WorldLocation = newPos
    concussionParams.Creature = game.AI.FindSon()
    concussionParams.EnemyId = DL.HashCreatureID(game.Player.FindPlayer(), "SON00")
    if arrowTypeIsLight then
      concussionParams.Tweak = "CNC_LIGHT_SUMMON_DEER_EXPLOSION_WALK_LV1_SPIN"
    end
    game.Combat.PlayConcussion(concussionParams)
    game.FX.SubmitEffect(rumbleParams)
    game.FX.SubmitEffect(shakeParams)
  end
  currentTickTimer = currentTickTimer + level:GetUnitTime()
end
local newlyspawned = true
function MoveLineTrail(level, go, newPos)
  if lineTrail == nil then
    if arrowTypeIsLight == false then
      lineTrail = game.FX.Spawn("Electric_Ground_Shock", level)
    else
      lineTrail = game.FX.Spawn("bifrost_bodyGlow_intense", level)
    end
  end
  if newlyspawned == false then
    lineTrail:SetScale(0.75)
  end
  lineTrail:SetWorldPosition(newPos)
  newlyspawned = false
end
function DestroySummons()
  state = "stop"
  lifeTime = 0
end
function SoundSpawnDeer(animal)
  goSoundEmitter = animal:FindSingleSoundEmitterByName("SNDdeer00")
  if arrowTypeIsLight == true then
    LD.PlaySoundRingOut(goSoundEmitter, "SND_CHR_Son_Magic_Summon_Deer_A_Spawn")
    LD.PlaySound(goSoundEmitter, "SND_CHR_Son_Magic_Summon_Deer_A_LP")
  else
    LD.PlaySoundRingOut(goSoundEmitter, "SND_CHR_Son_Magic_Summon_Deer_B_Spawn")
    LD.PlaySound(goSoundEmitter, "SND_CHR_Son_Magic_Summon_Deer_B_LP")
  end
end
function SoundExplosion()
  if arrowTypeIsLight ~= true then
    LD.PlaySoundRingOut(goSoundEmitter, "SND_CHR_Son_Magic_Summon_Deer_B_Energy_Blast")
  end
end
function SoundExplosionLight()
  LD.PlaySoundRingOut(goSoundEmitter, "SND_CHR_Son_Magic_Summon_Deer_A_Energy_Blast")
end
function SoundEmitArrow()
end
function SoundDespawn()
  if arrowTypeIsLight == true then
    LD.PlaySoundRingOut(goSoundEmitter, "SND_CHR_Son_Magic_Dissapate_A")
  else
    LD.PlaySoundRingOut(goSoundEmitter, "SND_CHR_Son_Magic_Dissapate_B")
  end
end
