local LD = require("design.LevelDesignLibrary")
local timer = require("level.timer")
local thisLevel, thisObj, player, frontInteractZone, currentLevel, heroPuppeteer, synchJointFacing, fxIdle, fxIdleIsOn
local attachedToPlayer = false
local levelVibrateCrystalsFound = false
local affectRange = 8
local crystalDestroyed = false
local onHitCallbacks, breakableObj, breakableComponent, playConcussion, affectedObjects, affectResult, aimAssist, enabled, destroyCalled, handleJointIndex
local parentObjectNotified = false
local buildUpFx
local explodeInProgress = false
crystalMesh = nil
local rumbleParams, shakeParams, controllerVibration, concussionParams, screenShake
fxTrail = nil
fxTrailIsOn = false
local buildupTime = 1
local explosionFx
function OnScriptLoaded(level, obj)
  thisLevel = level
  thisObj = obj
  player = game.Player:FindPlayer()
  handleJointIndex = obj:GetJointIndex("handle1")
  breakableComponent = obj:GetBreakable()
  enabled = obj:GetLuaTableAttribute("StartsEnabled")
  affectResult = obj:GetLuaTableAttribute("AffectResult")
  onHitCallbacks = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("OnProjectileHit"))
  frontInteractZone = LD.CreateInteractZone_Throwable_360(obj, "handle1")
  frontInteractZone:SetHintYRange(0.65)
  frontInteractZone:SetOnScreenPercentWeight(0)
  local throwableIconTable = {
    normal = "WORLD_INTERACT_THROWABLE",
    unavailable = "WORLD_INTERACT_UNAVAILABLE",
    locked = "WORLD_INTERACT_LOCKED",
    hint = "WORLD_INTERACT_HINT_THROWABLE"
  }
  frontInteractZone:SetPromptIconSet(throwableIconTable)
  crystalMesh = thisObj:FindSingleGOByName("crystalMesh")
  fxIdle = thisObj:FindSingleGOByName("vibrate_ThrowablePickupFX")
  fxIdle:Hide()
  fxTrail = thisObj:FindSingleGOByName("VibrateThrowable_Trail")
  fxTrail:Hide()
  if obj:FindLuaTableAttribute("AffectedObjects") ~= nil and obj:FindLuaTableAttribute("AffectedObjects") ~= "" then
    affectedObjects = level:FindGameObjects(tostring(obj:FindLuaTableAttribute("AffectedObjects") .. "*"))
    print(obj, "my init affected object(s) are", affectedObjects)
  end
  if not enabled then
    DisableInteract()
  end
  rumbleParams = {
    EffectName = "FFB_GROUNDSHAKE_BUILDUP_START",
    Duration = 1.5
  }
  shakeParams = {
    EffectName = "FSE_CRYSTAL_EXPLODE",
    Duration = 1.2,
    TweenInTime = 1.2
  }
  SoundInit()
  game.SubObject.Sleep(obj)
end
function OnStart(level, obj)
end
local crystalExplodeTimer, interactEnableFromDropTimer, addMarkersTimer, enableTrailTimer
function OnTeardown()
  if crystalExplodeTimer ~= nil then
    crystalExplodeTimer:Stop()
  end
  if interactEnableFromDropTimer ~= nil then
    interactEnableFromDropTimer:Stop()
  end
  if addMarkersTimer ~= nil then
    addMarkersTimer:Stop()
  end
  if enableTrailTimer ~= nil then
    enableTrailTimer:Stop()
  end
end
function OnUseWorld(level, go)
  if frontInteractZone:PlayerCanInteract() then
    player:RequestInteract(thisObj, {InteractZone = frontInteractZone})
  end
end
function OnHitByArrow(level, go, arrow_owner, arrow_go, arrow_tweak)
  if arrow_go:GetName() == "electricshock_arrow_notrail" then
    CrystalHitByProjectile(level, go)
    thisObj:SetScale(1.4)
  end
end
function OnHitGameObject(level, gameObject, attacker, hitGameObject)
  print("OnHitGameObject")
  local handle1Pos = thisObj:GetWorldJointPosition(handleJointIndex)
  concussionParams = {
    Tweak = "CNC_VibrateBreakables",
    WorldLocation = handle1Pos,
    GameObject = thisObj,
    EnemyId = game.Player.FindPlayer():GetID()
  }
  game.Combat.PlayConcussion(concussionParams)
  attachedToPlayer = false
  thisObj:ShowCollision()
  crystalMesh:SetScale(1.2)
  InteractEnable()
  PlayHitFX()
  PlayOnHitSound()
  PlayOnStuckSound()
end
function OnWeaponEmbed(level, go, attacker, weapon)
  attachedToPlayer = false
  PlayHitFX()
  crystalMesh:SetScale(1.2)
  PlayOnStuckSound()
end
function GetCrystalAvailable()
  if enabled then
    return true
  else
    return false
  end
end
function LuaHookSetThrowable()
  crystalMesh:SetScale(1)
  SetThrowable(thisObj)
  LD.CallFunctionAfterDelay(PlayPickupSound, 0.115)
end
function LuaHookOnThrowRelease()
  PlayThrowSound()
  InteractEnable()
end
function LuaHookDrop()
  PlayDropSound()
  interactEnableFromDropTimer = timer.StartLevelTimer(2, InteractEnableFromDrop)
end
function SetThrowable(obj)
  local throwableTemplate = {
    name = "Light",
    attachName = "ATT_Throwable",
    standardName = "Throwable",
    range = 15,
    maxThrowRange = 30,
    reticleOffset = 0.3,
    bounciness = 0.1,
    rotationSpeed = 0.1
  }
  thisObj = obj
  attachedToPlayer = true
  DisableIdleFX()
  player = game.Player:FindPlayer()
  player:SetThrowable(thisObj, throwableTemplate, "reticle")
  game.Interact.DisableTags("ThrowableCrystal")
  addMarkersTimer = timer.StartLevelTimer(0.1, AddMarkers)
  enableTrailTimer = timer.StartLevelTimer(0.5, EnableTrail)
  if frontInteractZone then
    frontInteractZone:Disable()
  end
end
function AddMarkers()
  thisObj:AddMarkerPointTest("VibrateCrystalObj")
  thisObj:AddMarker("SonTarget")
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.GetCrystalAvailable() and (thisObj:GetWorldPosition() - levelVibrateCrystals[i]:GetWorldPosition()):Length() < affectRange then
      levelVibrateCrystals[i].LuaObjectScript.CrystalHitByProjectile(thisLevel, thisObj)
    end
  end
end
function PlayEmbedFX()
  print("VibrateCrystal: PlayEmbedFX")
  EnableIdleFX()
end
function PlayHitFX()
  print("VibrateCrystal: PlayHitFX")
  EnableIdleFX()
end
function EnableTrail()
  print("VibrateCrystal: EnableTrail")
  if not fxTrailIsOn then
    fxTrail:Show()
    fxTrailIsOn = true
  end
end
function DisableTrail()
  print("VibrateCrystal: DisableTrail")
  fxTrailIsOn = false
  fxTrail:HideParticleEmitter()
  fxTrail:Hide()
end
function EnableIdleFX()
  print("VibrateCrystal: EnableIdleFX")
  fxIdle:Show()
  fxIdleIsOn = true
end
function DisableIdleFX()
  print("VibrateCrystal: DisableIdleFX")
  if fxIdleIsOn then
    fxIdleIsOn = false
    fxIdle:HideParticleEmitter()
    fxIdle:Hide()
  end
end
function CrystalHitByProjectile()
  if not explodeInProgress and crystalDestroyed ~= true then
    explodeInProgress = true
    if frontInteractZone then
      frontInteractZone:Disable()
    end
    game.FX.SubmitEffect(rumbleParams)
    game.FX.SubmitEffect(shakeParams)
    buildUpFx = game.FX.Spawn("vibrate_buildUp", thisLevel, {
      GameObject = thisObj,
      Joint = "handle1",
      Follow = true,
      AutoDelete = true
    })
    PlayVibrateSound()
    crystalExplodeTimer = _G.StartLevelTimer(buildupTime, function()
      CrystalExplode()
    end)
  end
end
function CrystalExplode()
  if fxTrailIsOn then
    DisableTrail()
  end
  if buildUpFx ~= nil and buildUpFx.Spawned then
    buildUpFx:HideParticleEmitter()
    buildUpFx:Remove()
  end
  if attachedToPlayer then
    print("ENABLING TAG FROM EXPLODE")
    game.Interact.EnableTags("ThrowableCrystal")
  end
  if breakableComponent.Broken == false then
    breakableComponent:Break()
  end
end
function SetParentAffectedObjects(affectTable)
  affectedObjects = affectTable
end
function SetParentOnHitCallback(hitTable)
  onHitCallbacks = hitTable
end
function OnBreakableBroken(level, obj)
  ChainEffect()
  ChainEffectBells()
  thisObj:RemoveMarker("VibrateCrystalObj")
  thisObj:RemoveMarker("SonTarget")
  crystalDestroyed = true
  print("Something triggered Break")
  local handle1Pos = obj:GetWorldJointPosition(handleJointIndex)
  concussionParams = {
    Tweak = "CNC_VibrateExplosion",
    WorldLocation = handle1Pos,
    GameObject = thisObj,
    EnemyId = game.Player.FindPlayer():GetID()
  }
  game.Combat.PlayConcussion(concussionParams)
  if frontInteractZone then
    frontInteractZone:Disable()
  end
  PlaySoundDetonate()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onHitCallbacks, "CrystalHitByProjectile")
  if affectedObjects ~= nil then
    for i = 1, #affectedObjects do
      if affectedObjects[i] ~= nil and affectedObjects[i] ~= "" then
        if affectResult == "Breakable" 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
        elseif affectResult == "PlayAnimToEnd" then
          affectedObjects[i]:JumpAnimToFrame(0)
          affectedObjects[i]:PlayAnimToEnd()
        elseif affectResult == "PlayAnimCycle" then
          affectedObjects[i]:PlayAnimCycle()
        end
      end
    end
  end
end
function OnInteractStart(level, obj, creature)
  player:MarkCurrentWeaponMode()
  EndPickupThrowable()
  heroPuppeteer = game.Puppeteer.NewForce(obj, "Throwable Pickup", player)
  heroPuppeteer:WeaponEquip({weaponMode = "Bare"})
  heroPuppeteer:StartBranch("BRA_PickUpThrowable")
  heroPuppeteer:OnComplete(EndPickupThrowable)
end
function OnInteractAbort(level, obj, creature)
  print("got interact Abort msg")
  game.Interact.EnableTags("ThrowableCrystal")
end
function OnInteractFinish(level, obj, creature)
end
function OnInteractDone(level, obj, creature)
  print("got interact done msg")
end
function EnableObject(data)
  if data == false then
    enabled = false
  elseif data == true then
    enabled = true
  end
end
function InteractEnableFromDrop()
  enabled = true
  frontInteractZone:Enable()
end
function InteractEnable()
  enabled = true
  game.Interact.EnableTags("ThrowableCrystal")
  frontInteractZone:Enable()
end
function DisableInteract()
  frontInteractZone:Disable()
end
function EndPickupThrowable()
  if heroPuppeteer ~= nil then
    heroPuppeteer:Clear()
    heroPuppeteer:DetachPuppet()
    heroPuppeteer = nil
  end
end
function OnSpawnedObjectDestroyed(level, spawn)
  game.Interact.EnableTags("ThrowableCrystal")
end
local soundEmitter, soundEmitterBroken
local soundEvents = {
  OnIdle = "SND_WPN_Bifrost_Bomb_Idle_LP",
  OnPickup = "SND_WPN_Bifrost_Bomb_Pick_Up",
  OnHeld = "SND_WPN_Bifrost_Bomb_Hold_Active_LP",
  OnThrow = "SND_WPN_Bifrost_Bomb_Throw",
  OnThrowLP = "SND_WPN_Bifrost_Bomb_Throw_LP",
  OnDrop = "SND_WPN_Bifrost_Bomb_Drop_Release",
  OnHit = "SND_WPN_Bifrost_Bomb_Hit_Surface",
  OnStuck = "SND_WPN_Bifrost_Bomb_Throw_Hit_Latch",
  OnVibrate = "SND_WPN_Bifrost_Bomb_Explo_Build_Up_LP",
  OnDetonate = "SND_WPN_Bifrost_Bomb_Explo"
}
function SoundInit()
  soundEmitter = thisObj:FindSingleSoundEmitterByName("SNDThrowableVibrate_Pristine")
  soundEmitterBroken = thisObj:FindSingleSoundEmitterByName("SNDThrowableVibrate_Broken")
  LD.CallFunctionAfterDelay(PlaySpawnSound, 0.3)
end
function PlaySpawnSound()
  print("PlaySpawnSound")
  LD.PlaySound(soundEmitter, soundEvents.OnHeld)
end
function PlayPickupSound()
  print("PlayPickupSound")
  LD.PlaySound(soundEmitter, soundEvents.OnPickup)
  LD.PlaySound(soundEmitter, soundEvents.OnHeld)
  LD.StopSound(soundEmitter, soundEvents.OnIdle)
end
function PlayThrowSound()
  print("PlayThrowSound")
  LD.PlaySound(soundEmitter, soundEvents.OnThrow)
  LD.PlaySound(soundEmitter, soundEvents.OnThrowLP)
  LD.StopSound(soundEmitter, soundEvents.OnHeld)
end
function PlayOnHitSound()
  print("PlayOnHitSound")
  LD.PlaySound(soundEmitter, soundEvents.OnHit)
  LD.StopSound(soundEmitter, soundEvents.OnThrowLP)
  LD.PlaySound(soundEmitter, soundEvents.OnIdle)
end
function PlayOnStuckSound()
  print("PlayOnStuckSound")
  LD.PlaySound(soundEmitter, soundEvents.OnStuck)
end
function PlayDropSound()
  print("PlayDropSound")
  LD.PlaySound(soundEmitter, soundEvents.OnDrop)
  LD.StopSound(soundEmitter, soundEvents.OnHeld)
  LD.PlaySound(soundEmitter, soundEvents.OnIdle)
end
function PlayVibrateSound()
  print("PlayVibrateSound")
  LD.PlaySound(soundEmitter, soundEvents.OnVibrate)
end
function PlaySoundDetonate()
  print("PlaySoundDetonate")
  LD.PlaySoundAfterDelay(soundEmitterBroken, soundEvents.OnDetonate, 0.1)
  LD.StopSoundAfterDelay(soundEmitter, soundEvents.OnVibrate, 0.1)
  LD.StopSoundAfterDelay(soundEmitter, soundEvents.OnIdle, 0.1)
end
