local class = require("core.class")
local profile = require("core.profile")
local thunk = require("core.thunk")
local LoadMonitor = class.Class("LoadMonitor")
local CreateLoadMonitor = function()
  local loadMonitor = LoadMonitor.New()
  loadMonitor.loadCallbacks = {}
  loadMonitor.wadsToMonitor = {}
  loadMonitor.hooksInstalled = false
  return loadMonitor
end
function LoadMonitor:AddCallback(newCallback)
  self:SortAlpha(newCallback.Wads)
  if self.loadCallbacks == nil then
    self.loadCallbacks = {newCallback}
  else
    for _, callback in pairs(self.loadCallbacks) do
      local tableExists = LoadMonitor:TablesEqual(newCallback.Wads, callback.Wads)
      if tableExists then
        for _, fn in pairs(newCallback.Functions) do
          callback.Functions[#callback.Functions + 1] = fn
        end
        return
      end
    end
    self.loadCallbacks[#self.loadCallbacks + 1] = newCallback
  end
  for _, wad in pairs(newCallback.Wads) do
    self:SetInitialWadState(wad)
  end
  local callbacksFired = self:CheckEntriesForCallback()
  if not callbacksFired and not self.hooksInstalled then
    self.hooksInstalled = true
    thunk.Install("OnWadLoaded", function(...)
      self:OnWadLoaded(...)
    end)
    thunk.Install("OnWadUnloaded", function(...)
      self:OnWadUnloaded(...)
    end)
  end
end
function LoadMonitor:OnWadLoaded(level, newLevel)
  if self.loadCallbacks then
    local wadName = string.gsub(newLevel.Name, "WAD_", "")
    if self:ValidateWad(wadName) then
      self:SetWadState(wadName, newLevel)
      self:CheckEntriesForCallback()
    end
  end
end
function LoadMonitor:OnWadUnloaded(level, newLevel)
  if self.loadCallbacks then
    local wadName = string.gsub(newLevel.Name, "WAD_", "")
    if self:ValidateWad(wadName) then
      self:SetWadState(wadName, false)
    end
  end
end
function LoadMonitor:SetInitialWadState(wad)
  local lvl = game.FindLevel(wad)
  if lvl then
    self:SetWadState(wad, lvl)
  else
    self:SetWadState(wad, false)
  end
end
function LoadMonitor:ValidateWad(wad)
  return self.wadsToMonitor[wad] ~= nil
end
function LoadMonitor:CheckEntriesForCallback()
  for k, callback in pairs(self.loadCallbacks) do
    local allWadsLoaded
    local loadedWads = {}
    for _, wad in pairs(callback.Wads) do
      if self:GetWadState(wad) == false then
        allWadsLoaded = false
        break
      else
        loadedWads[wad] = self.wadsToMonitor[wad]
        allWadsLoaded = true
      end
    end
    if allWadsLoaded == true then
      if #callback.Functions > 0 then
        for _, fn in pairs(callback.Functions) do
          fn(loadedWads)
        end
        callback.Functions = {}
      end
      self.loadCallbacks[k] = nil
    end
  end
  if #self.loadCallbacks == 0 then
    self.loadCallbacks = nil
    return true
  end
  return false
end
function LoadMonitor:SetWadState(wad, levelOrFalse)
  self.wadsToMonitor[wad] = levelOrFalse
end
function LoadMonitor:GetWadState(wad)
  return self.wadsToMonitor[wad]
end
function LoadMonitor:SortAlpha(t)
  local CompareAlpha = function(wadA, wadB)
    return wadA < wadB
  end
  table.sort(t, CompareAlpha)
end
function LoadMonitor:TablesEqual(t1, t2)
  if #t1 ~= #t2 then
    return false
  end
  for i = 1, #t1 do
    if t1[i] ~= t2[i] then
      return false
    end
  end
  return true
end
return profile.WrapLibrary({LoadMonitor = LoadMonitor, CreateLoadMonitor = CreateLoadMonitor})
