local LD = require("design.LevelDesignLibrary")
local timer = require("level.timer")
thisLevel = nil
thisObj = nil
local isPlaying = false
local levelVibrateCrystals = {}
local levelVibrateCrystalsFound = false
local affectRange
local crystalDestroyed = false
local onHitCallbacks, breakableObj, breakableComponent, affectedObjects, aimAssist, buildUpFx, rumbleParams, shakeParams, idleFX, mainObj, light, enabled
local buildupTime = 1
local aimAssistAdded
function OnScriptLoaded(level, obj)
  obj:AddMarkerPointTest("VibrateCrystalObj")
  obj:AddMarker("SonTarget")
  thisLevel = level
  thisObj = obj
  light = obj:FindSingleGOByName("Light")
  affectRange = obj:GetLuaTableAttribute("BlastRadius")
  print("build up", buildUpFx)
  onHitCallbacks = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("OnProjectileHit"))
  aimAssist = obj:GetLuaTableAttribute("AimAssist")
  if obj:FindLuaTableAttribute("AffectedObjects") ~= nil and obj:FindLuaTableAttribute("AffectedObjects") ~= "" then
    affectedObjects = level:FindGameObjects(tostring(obj:FindLuaTableAttribute("AffectedObjects") .. "*"))
  end
  mainObj = obj:FindSingleGOByName("PreMesh")
  if mainObj ~= nil then
    mainObj:JumpAnimToFrame(0)
    mainObj:PauseAnim()
  else
    thisObj:JumpAnimToFrame(0)
    thisObj:PauseAnim()
  end
  idleFX = thisObj:FindSingleGOByName("vibratecrystal_idle")
  breakableObj = thisObj
  breakableComponent = breakableObj:GetBreakable()
  enabled = obj:GetLuaTableAttribute("StartsEnabled")
  rumbleParams = {
    EffectName = "FFB_GROUNDSHAKE_BUILDUP_START",
    Duration = 1
  }
  shakeParams = {
    EffectName = "FSE_CRYSTAL_EXPLODE",
    Duration = 1
  }
  game.SubObject.Sleep(thisObj)
  SoundInit()
end
function OnStart(level, obj)
  SoundOnStart()
  if enabled then
    thisObj:RemoveAimAssist({
      Joint = "aimAssistJ",
      Angle = 12,
      MaxRadius = 1.4
    })
    EnableObject(true)
  end
end
function OnHitByArrow(level, go, arrow_owner, arrow_go, arrow_tweak)
  print("Vibrate arrow info", level, go, arrow_owner, arrow_go)
  if arrow_go:GetName() == "electricshock_arrow_notrail" and enabled then
    print("got hit by Son arrow...", arrow_go, enabled)
    CrystalHitByProjectile(level, go)
  else
    print("Must be something else...", arrow_go, enabled)
  end
end
function OnHitByWeapon(level, obj, weapon, attacker)
  if attacker == game.Player.FindPlayer() then
    game.Audio.IncrementBanterFact("HitBifrost", 1, 10)
    game.Audio.PlayBanterNonCritical("ca_carry_crystal_weapon_hit")
  end
end
function OnBreakableBroken(level, obj)
  crystalDestroyed = true
  ChainEffect()
  ChainEffectBells()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onHitCallbacks, "CrystalHitByProjectile")
  TriggerAffectedObjects()
  local concussionParams = {
    Tweak = "CNC_VibrateExplosion",
    WorldLocation = thisObj:GetWorldPosition(),
    GameObject = thisObj,
    EnemyId = game.Player.FindPlayer():GetID()
  }
  game.Combat.PlayConcussion(concussionParams)
end
function ChainEffectBells()
  local levelBells = thisLevel:FindGameObjectsByMarker("BellTarget")
  local proximityCount = 0
  for i = 1, #levelBells do
    if levelBells[i] ~= thisObj then
      print(game.AIUtil.Distance(thisObj, levelBells[i]))
      if game.AIUtil.Distance(thisObj, levelBells[i]) < affectRange then
        proximityCount = proximityCount + 1
        if proximityCount % 2 == 0 then
          levelBells[i].LuaObjectScript.HitFront()
        else
          levelBells[i].LuaObjectScript.HitBack()
        end
      end
    end
  end
end
function ChainEffect()
  local levelVibrateCrystals = thisLevel:FindGameObjectsByMarker("VibrateCrystalObj")
  for i = 1, #levelVibrateCrystals do
    if levelVibrateCrystals[i] ~= thisObj and levelVibrateCrystals[i].LuaObjectScript ~= nil and levelVibrateCrystals[i].LuaObjectScript.GetCrystalAvailable() and (thisObj:GetWorldPosition() - levelVibrateCrystals[i]:GetWorldPosition()):Length() < affectRange and levelVibrateCrystals[i].LuaObjectScript ~= nil then
      levelVibrateCrystals[i].LuaObjectScript.CrystalHitByProjectile(thisLevel, thisObj)
    end
  end
end
function CrystalHitByProjectile()
  if light ~= nil then
    light:Hide()
  end
  buildUpFx = game.FX.Spawn("vibrate_buildUp", thisLevel, {
    GameObject = thisObj,
    Joint = "aimAssistJ",
    Follow = true,
    AutoDelete = true
  })
  game.FX.SubmitEffect(rumbleParams)
  game.FX.SubmitEffect(shakeParams)
  idleFX:Hide()
  if mainObj ~= nil then
    mainObj:PlayAnimToEnd()
  else
    thisObj:PlayAnimToEnd()
  end
  local explodeTimer = StartLevelTimer(buildupTime, CrystalExplode)
  PlayVibrateSound()
end
function CrystalExplode()
  if breakableComponent.Broken == false then
    local explosionShake = {
      EffectName = "FSE_CRYSTAL_EXPLODE_FINAL",
      Duration = 0.1
    }
    game.FX.SubmitEffect(explosionShake)
    EnableObject(false)
    thisObj:ShowPhysics()
    thisObj:RemoveAimAssist({
      Joint = "aimAssistJ",
      Angle = 12,
      MaxRadius = 1.4
    })
    breakableComponent:Break()
  end
end
function TriggerAffectedObjects()
  if affectedObjects ~= nil then
    for i = 1, #affectedObjects do
      if affectedObjects[i] ~= nil and affectedObjects[i] ~= "" then
        if affectedObjects[i].Child ~= nil then
          if affectedObjects[i].Child:GetBreakable() ~= nil then
            local breakableComponent = affectedObjects[i].Child:GetBreakable()
            if breakableComponent.Broken == false then
              breakableComponent:Break()
            end
          end
        elseif affectedObjects[i]:GetBreakable() ~= nil then
          local breakableComponent = affectedObjects[i]:GetBreakable()
          if breakableComponent.Broken == false then
            breakableComponent:Break()
          end
        end
        affectedObjects[i]:JumpAnimToFrame(0)
        affectedObjects[i]:PlayAnimToEnd()
      end
    end
  end
end
function Enable()
  enabled = true
end
function EnableObject(data)
  if data == false then
    enabled = false
    aimAssistAdded = false
    thisObj:RemoveAimAssist({
      Joint = "aimAssistJ",
      Angle = 12,
      MaxRadius = 1.4
    })
  elseif data == true then
    enabled = true
    if aimAssist and not aimAssistAdded then
      aimAssistAdded = true
      thisObj:AddAimAssist({
        Joint = "aimAssistJ",
        Angle = 12,
        MaxRadius = 1.4,
        ZoomSnapScreenAngle = 12,
        ZoomSnapRadius = 1.4,
        MaxDistance = -1
      })
    end
  end
end
function GetCrystalAvailable()
  if enabled then
    return true
  else
    return false
  end
end
function OnSaveCheckpoint(level)
  return {enabled = enabled, temp_crystalDestroyed = crystalDestroyed}
end
function OnRestoreCheckpoint(level, obj, tab)
  crystalDestroyed = tab.temp_crystalDestroyed
  enabled = tab.enabled or enabled
  if crystalDestroyed then
    TriggerAffectedObjects()
  end
end
local soundEmitter
local soundEvents = {
  isLooping = false,
  IdleLoop = "SND_WPN_Bifrost_Bomb_Idle_LP",
  OnVibrate = "SND_WPN_Bifrost_Bomb_Explo_Build_Up_LP"
}
function SoundInit()
  soundEmitter = thisObj:FindSingleSoundEmitterByName("SNDBifrostVibrateFractured_Pristine")
end
function SoundOnStart()
  if not crystalDestroyed and thisObj:IsModelShown() then
    LD.PlayRestartableSoundLoop(soundEmitter, soundEvents.IdleLoop)
  end
end
function SoundSetup(sounds)
  if sounds ~= nil then
    if sounds.SoundEmitter ~= nil then
      soundEmitter = thisObj:FindSingleSoundEmitterByName(sounds.SoundEmitter)
    end
    for key in pairs(soundEvents) do
      for newKey, newValue in pairs(sounds) do
        if newKey == key and newValue ~= nil and newValue ~= "" then
          soundEvents[key] = newValue
        end
      end
      LD.SoundDebug(tostring(key) .. ": " .. tostring(soundEvents[key]))
    end
  end
end
function PlayVibrateSound()
  StopIdleSoundLoop()
  LD.PlaySound(soundEmitter, soundEvents.OnVibrate)
end
function StopIdleSoundLoop()
  LD.StopRestartableSoundLoop(soundEmitter, soundEvents.IdleLoop)
end
