local LD = require("design.LevelDesignLibrary")
local monitors = require("level.MonitorLibrary")
local thisObj, thisLevel, player
local enabled = true
local FXArray = {}
local numFX, IsAutoDispersed, EndFrameOfAnimLoop, DispersalRadius, DispersalCallbacks
local finishCount = 0
local startedDispersal = false
local distanceMonitor, axeDistanceMonitor
local function RecursivePrintParents(obj)
  if obj.Parent then
    RecursivePrintParents(obj.Parent)
  end
  print(tostring(obj))
  for _, x in ipairs(obj.Children) do
    print("    " .. tostring(x))
  end
end
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  player = game.Player.FindPlayer()
  if obj:FindLuaTableAttribute("StartEnabled") ~= nil then
    enabled = obj:FindLuaTableAttribute("StartEnabled")
  end
  if obj:FindLuaTableAttribute("DisableAutoDispersal") ~= nil then
    IsAutoDispersed = not obj:FindLuaTableAttribute("DisableAutoDispersal")
  end
  if obj:FindLuaTableAttribute("EndFrameOfAnimLoop") ~= nil then
    EndFrameOfAnimLoop = obj:FindLuaTableAttribute("EndFrameOfAnimLoop")
  end
  if obj:FindLuaTableAttribute("DispersalRadius") ~= nil then
    DispersalRadius = obj:FindLuaTableAttribute("DispersalRadius")
  end
end
function OnStart(level, obj)
  local rawFXArray = LD.ConvertStringListToTable(thisObj:FindLuaTableAttribute("Emitters"))
  numFX = #rawFXArray
  for _, x in ipairs(rawFXArray) do
    local newFX = FindGOInObjectHierarchy(thisObj, x)
    if newFX then
      table.insert(FXArray, newFX)
    end
  end
  TestFXArray()
  for _, x in ipairs(FXArray) do
    x:JumpAnimationToFrame(0)
    x:PlayAnimationToFrame(EndFrameOfAnimLoop)
  end
  FXArray[1]:OnAnimationDone(thisObj, "Callback_AnimationLoopFinished", {Force = true})
  if obj:FindLuaTableAttribute("DispersalCallbacks") ~= nil then
    DispersalCallbacks = LD.ExtractCallbacksForEvent(thisLevel, thisObj, obj:FindLuaTableAttribute("DispersalCallbacks"))
  end
  SoundInit()
  thisObj:PauseAnimation()
  if enabled then
    Enable()
  else
    Disable()
  end
end
function Callback_AnimationLoopFinished()
  for _, x in ipairs(FXArray) do
    x:JumpAnimationToFrame(0)
    x:PlayAnimationToFrame(EndFrameOfAnimLoop)
  end
  FXArray[1]:OnAnimationDone(thisObj, "Callback_AnimationLoopFinished", {Force = true})
end
function Callback_AnimationTotallyFinished(_, _, fxObj)
  FXArray[1]:ClearAllAnimationCallbacks()
  LD.ExecuteCallbacksForEvent(thisLevel, thisObj, DispersalCallbacks, "DispersalCallbacks")
  Disable()
end
local Disperse = function(_, _, rate)
  if not startedDispersal then
    startedDispersal = true
    for _, x in ipairs(FXArray) do
      x:JumpAnimationToFrame(EndFrameOfAnimLoop)
      x:PlayAnimationToEnd(rate or 1)
    end
    FXArray[1]:OnAnimationDone(thisObj, "Callback_AnimationTotallyFinished", {Force = true})
    PlaySoundOnDisperse()
  end
end
function OnHitByWeapon(_, _, _, _)
  WeaponDisperse()
end
function OnHitByArrow(_, _, _, _, _)
  WeaponDisperse()
end
function WeaponDisperse()
  if enabled and not startedDispersal then
    Disperse(nil, nil, 2)
  end
  thisObj:PlayAnimToEnd()
end
function Enable()
  enabled = true
  if IsAutoDispersed then
    distanceMonitor = monitors.CreateDistanceMonitor(player, thisObj)
    distanceMonitor:OnCloserThan(DispersalRadius, Disperse)
  end
  thisObj:Show()
  for _, x in ipairs(FXArray) do
    x:Show()
    x:JumpAnimToFrame(0)
    x:PlayAnimToFrame(EndFrameOfAnimLoop)
  end
end
function Disable()
  enabled = false
  distanceMonitor:Stop()
  distanceMonitor = nil
  for _, x in ipairs(FXArray) do
    x:Hide()
  end
  thisObj:Hide()
end
function FindGOInObjectHierarchy(startingObj, searchValue)
  local obj = startingObj
  local searchObj
  while searchObj == nil and obj.Parent ~= nil do
    searchObj = obj:FindSingleGOByName(searchValue)
    obj = obj.Parent
  end
  return searchObj
end
function TestFXArray()
  for _, x in pairs(FXArray) do
    assert(x ~= nil, "Some FX object was not found. Make sure table value is the same as object name.")
  end
  assert(#FXArray == numFX, thisObj.Parent.Parent.Parent:GetName() .. ": Not All FX were found. " .. tostring(#FXArray) .. " found. Should be " .. tostring(numFX))
end
local soundEmitter, soundObj
local soundEvents = {
  OnDisperse = "SND_AMB_EMIT_Insect_Butterflies_01"
}
function SoundInit()
  soundObj = FindGOInObjectHierarchy(thisObj, "soundObj")
  if soundObj ~= nil then
    soundEmitter = soundObj:FindSingleSoundEmitterByName("SNDFlower")
  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
    end
  end
end
function PlaySoundOnDisperse()
  if soundObj ~= nil and soundObj.AnimFrame ~= nil then
    soundObj:JumpAnimToFrame(0)
    soundObj:PlayAnimToEnd(0.25)
  end
  LD.PlaySound(soundEmitter, soundEvents.OnDisperse)
end
