---@class LegacyModule: ModuleType
local LegacyModule = ModuleBase:createModule('Legacy');
LegacyModule.sharedContext = {
  math = {},
  string = {},
  table = {},
  VaildChar = Char.IsValidCharIndex,
  NL = {},
  Char = {},
  Battle = {},
  Item = {},
  Pet = {},
  Recipe = {},
  Field = {},
  Data = {},
  NLG = {},
  Protocol = {},
  Obj = {},
  SQL = {},
  Map = {},
  Http = {},
  Iconv = {},
  Setup = {},
  Skill = {},
  Stall = {},
  Tech = {},
  TechArea = {},
};
local sharedContext = LegacyModule.sharedContext;

function LegacyModule:loadFile(file, cb, name)
  if not cb then
    self:logError('callback is null', name, file, cb);
    error('callback is null');
  end
  if file then
    local moduleName = findLegacyModuleName(file);
    if not moduleName then
      loadModule(moduleName or file, { path = file, simpleModule = true, forceReload = true, absolutePath = true });
    end
  end
  local key = '__callInCtx' .. cb;
  _G[key] = function(...)
    return self:callInCtx(cb, ...);
  end
  return key;
end

function LegacyModule:callInCtx(name, ...)
  if self.sharedContext[name] == nil then
    error(string.format('%s not found in sharedContext', name));
  end
  return self.sharedContext[name](...)
end

sharedContext.NL.CreateNpc = function(file, cb)
  return NL.CreateNpc(nil, LegacyModule:loadFile(file, cb, 'NL.CreateNpc'));
end

sharedContext.NL.CreateNPC = sharedContext.NL.CreateNpc;

sharedContext.Protocol.OnRecv = function(file, cb, header)
  return Protocol.OnRecv(nil, LegacyModule:loadFile(file, cb, 'Protocol.OnRecv'), header);
end

for f, n in pairs({ Char = Char, Battle = Battle }) do
  for i, v in pairs(n) do
    if string.sub(i, 1, 3) == 'Set' and string.sub(i, #i - 4) == 'Event' then
      sharedContext[f][i] = function(file, cb, ...)
        return v(nil, LegacyModule:loadFile(file, cb, string.format('%s.%s', f, i)), ...)
      end
    end
  end
end

for i, v in pairs(NL) do
  if string.sub(i, 1, 3) == 'Reg' then
    sharedContext.NL[i] = function(file, cb, ...)
      return v(nil, LegacyModule:loadFile(file, cb, string.format('NL.%s', i)), ...)
    end
  end
end

sharedContext.math = setmetatable(sharedContext.math, { __index = math });
sharedContext.string = setmetatable(sharedContext.string, { __index = string });
sharedContext.table = setmetatable(sharedContext.table, { __index = table });
sharedContext.NL = setmetatable(sharedContext.NL, { __index = NL });
sharedContext.Char = setmetatable(sharedContext.Char, { __index = Char });
sharedContext.Battle = setmetatable(sharedContext.Battle, { __index = Battle });
sharedContext.Item = setmetatable(sharedContext.Item, { __index = Item });
sharedContext.Pet = setmetatable(sharedContext.Pet, { __index = Pet });
sharedContext.Recipe = setmetatable(sharedContext.Recipe, { __index = Recipe });
sharedContext.Field = setmetatable(sharedContext.Field, { __index = Field });
sharedContext.Data = setmetatable(sharedContext.Data, { __index = Data });
sharedContext.NLG = setmetatable(sharedContext.NLG, { __index = NLG });
sharedContext.Protocol = setmetatable(sharedContext.Protocol, { __index = Protocol });
sharedContext.Obj = setmetatable(sharedContext.Obj, { __index = Obj });
sharedContext.SQL = setmetatable(sharedContext.SQL, { __index = SQL });
sharedContext.Map = setmetatable(sharedContext.Map, { __index = Map });
sharedContext.Http = setmetatable(sharedContext.Http, { __index = Http });
sharedContext.Iconv = setmetatable(sharedContext.Iconv, { __index = Iconv });
sharedContext.Setup = setmetatable(sharedContext.Setup, { __index = Setup });
sharedContext.Skill = setmetatable(sharedContext.Skill, { __index = Skill });
sharedContext.Stall = setmetatable(sharedContext.Stall, { __index = Stall });
sharedContext.Tech = setmetatable(sharedContext.Tech, { __index = Tech });
sharedContext.TechArea = setmetatable(sharedContext.TechArea, { __index = TechArea });

setmetatable(sharedContext, { __index = _G })

---@return LegacyModule
function LegacyModule:new(name)
  ---@class LegacyModule
  local o = {};
  o = setmetatable(o, LegacyModule)
  o.name = LegacyModule.name .. ':' .. name;
  o.rawName = name;
  o.callbacks = {};
  o.lastIx = 0;
  o:createContext();
  return o;
end

function LegacyModule:createDelegate()
  local Delegate = {};
  self.Delegate = Delegate;
  for k, _ff in pairs(NL) do
    if string.sub(k, 1, 3) == 'Reg' then
      local key = string.sub(k, 4);
      Delegate['RegDel' .. key] = function(fn)
        if type(fn) == 'string' then
          if self['_t_' .. key] then
            return
          end
          self['_t_' .. key] = fn;
          self:regCallback(key, function(...)
            return self:callInCtx(fn, ...);
          end)
        elseif type(fn) == 'function' then
          self:regCallback(key, fn);
        end
      end
      Delegate['Reg' .. key] = Delegate['RegDel' .. key];
    end
  end
  Delegate.RegProtocolOnRecv = function(callback, head)
    local fn = callback
    local key = 'ProtocolOnRecv'
    if type(fn) == 'string' then
      if self['_t_' .. key] then
        return
      end
      self['_t_' .. key] = fn;
      self:regCallback(key, function(...)
        return self:callInCtx(fn, ...);
      end, head)
    elseif type(fn) == 'function' then
      self:regCallback(key, fn, head);
    end
  end
  Delegate.RegAllOutEvent = function(callback)
    Delegate.RegLogoutEvent(callback);
    Delegate.RegDropEvent(callback);
  end
  Delegate.RegDelAllOutEvent = Delegate.RegAllOutEvent
  Delegate.RegDelProtocolOnRecv = Delegate.RegProtocolOnRecv;
  return Delegate;
end

function LegacyModule:createContext()
  self.context = {};
  setmetatable(self.context, {
    __index = self.sharedContext,
    __newindex = function(table, key, value)
      if rawget(table, key) then
        rawset(table, key, value)
        return
      end
      self.sharedContext[key] = value;
    end
  })
  self.context.dofile = function(path)
    return loadfile(path, 'bt', self.context)();
  end
  self.context.load = function(path, name, mode, env)
    return load(path, name, mode or 'bt', env or self.context);
  end
  self.context.loadfile = function(path, mode, env)
    return loadfile(path, mode or 'bt', env or self.context);
  end
  self.context.currentModule = self;
  self.context.print = function(msg, ...)
    if msg == nil then
      msg = ''
    end
    self:logInfo(msg, ...)
  end
  self.context.Delegate = self:createDelegate();
end

function LegacyModule:onLoad()
  self:logInfo('onLoad');
  local r, msg = pcall(function()
    local fn, m = loadfile(self.___aPath, 'bt', self.context);
    if m then
      error(m);
    end
    fn();
  end)
  if r == false then
    self:logError('load error', msg);
  end
end

function LegacyModule:onUnload()
  self:logInfo('onUnload');
end

_G.LegacyModule = LegacyModule; 
