local profile = require("core.profile")
local monitors = require("level.MonitorLibrary")
local RegisterForCineUpdate = function(level, args)
  local globalVar = "CompletedCineNumber"
  local globalVal = game.Level.GetVariable(globalVar)
  local levelVar = level.Name .. "_" .. globalVar
  local levelVal
  local ErrorChecks = function()
    if game.Level.GetVariable(levelVar) == nil then
      engine.Error("Level entity variable " .. levelVar .. " does not exist in level " .. level.Name)
    end
  end
  local SetLevelValToGlobalVal = function(val)
    game.Level.SetVariable(levelVar, val or game.Level.GetVariable(globalVar))
  end
  local Initialize = function()
    levelVal = game.Level.GetVariable(levelVar)
    if levelVal <= globalVal then
      for _, v in ipairs(args) do
        if v.at > levelVal and v.at <= globalVal then
          if not v.immediateFn then
            engine.Error("No immediateFn defined for cine update " .. levelVar .. " at cine number" .. v.at .. " in level " .. level.Name)
          end
          v.immediateFn()
        elseif v.at > globalVal and v.fn then
          if not _G.EntityVariableMessaging_CineUpdateMonitor then
            _G.EntityVariableMessaging_CineUpdateMonitor = monitors.CreateEntityVariableMonitor(globalVar)
          end
          _G.EntityVariableMessaging_CineUpdateMonitor:OnValue(v.at, function()
            if not v.fn then
              engine.Error("No fn defined for cine update " .. levelVar .. " at cine number" .. v.at .. " in level " .. level.Name)
            end
            local val = game.Level.GetVariable(globalVar)
            v.fn()
            SetLevelValToGlobalVal(val)
          end)
        end
      end
    end
    game.Level.SetVariable(levelVar, globalVal)
  end
  ErrorChecks()
  Initialize()
end
local RegisterForVariableUpdate = function(level, baseVar, args)
  local globalVar, levelVar, globalVal, levelVal, globalVarType, levelVarType
  local ErrorChecks = function()
    if type(baseVar) ~= "string" then
      engine.Error("Attemping to register for an entity variable without passing a variable name that is a string.")
    end
    if string.sub(baseVar, 1, 5) == "_GBL_" then
      globalVar = baseVar
      baseVar = string.sub(baseVar, 6)
    else
      globalVar = "_GBL_" .. baseVar
    end
    if game.Level.GetVariable(globalVar) == nil then
      engine.Error("Attempting to register for an entity variable that doesn't exist.")
    end
    levelVar = level.Name .. "_" .. baseVar
    if game.Level.GetVariable(levelVar) == nil then
      engine.Error("Level entity variable " .. levelVar .. " does not exist in level " .. level.Name)
    end
    globalVal = game.Level.GetVariable(globalVar)
    levelVal = game.Level.GetVariable(levelVar)
    globalVarType = type(globalVal)
    levelVarType = type(levelVal)
    if globalVarType ~= levelVarType then
      engine.Error([[
				Attempting to register for a variable update with mismatching variable types.
				The global variable ]] .. globalVar .. " has a type of " .. type(globalVal) .. "\t\t\t\tThe level variable " .. levelVar .. " on level " .. level.Name .. " has a type of " .. type(levelVal))
    end
  end
  local SetLevelValToGlobalVal = function()
    game.Level.SetVariable(levelVar, game.Level.GetVariable(globalVar))
  end
  local RunImmediateUpdate = function()
    if args.immediateFn then
      args.immediateFn()
      SetLevelValToGlobalVal()
    elseif args.fn then
      engine.Error("\t\t\t\tNo immediateFn defined for variable update " .. levelVar .. " in level " .. level.Name .. [[
.
				Falling back to using args.fn]])
      args.fn()
      SetLevelValToGlobalVal()
    else
      engine.Error("No immediateFn defined for variable update " .. levelVar .. " in level " .. level.Name)
    end
  end
  local CreateMonitor = function()
    local mon = monitors.CreateEntityVariableMonitor(globalVar)
    if globalVarType == "boolean" then
      mon:OnValue(true, function()
        args.fn()
        SetLevelValToGlobalVal()
        mon:Stop()
        mon:Terminate()
        mon = nil
      end)
    elseif globalVarType == "number" then
      mon:OnVariableChange(function()
        args.fn()
        SetLevelValToGlobalVal()
      end)
    end
  end
  local Initialize = function()
    if globalVarType == "boolean" then
      if globalVal == true then
        if levelVal == false then
          RunImmediateUpdate()
        end
      else
        CreateMonitor()
      end
    elseif globalVarType == "number" then
      levelVal = tonumber(levelVal)
      if globalVal ~= levelVal then
        RunImmediateUpdate()
      end
      CreateMonitor()
    end
  end
  ErrorChecks()
  Initialize()
end
return profile.WrapLibrary({RegisterForCineUpdate = RegisterForCineUpdate, RegisterForVariableUpdate = RegisterForVariableUpdate})
