local L_Net = {}
local Proto
local white = {}
local C_NetManager = CS.Azur.Gameplay.Core.Net.NetManager
local errorMsgCode = require("proto.ErrCode")

function L_Net:initialize(proto, newWhite)
  Proto = proto
  self.isOpenLog = C_NetManager.IsOpenLog
  white = newWhite or {}
  self.listeners = {}
  self.onceListeners = {}
  self.specailSeqNoMsgIdDic = {
    [1001] = 1,
    [5001] = 1
  }
  self.receiveMessageFunc = handler(self, self._receiveMessage)
  self.clearOnceListenersFunc = handler(self, self._clearOnceListeners)
  local mainChannel = C_NetManager.GetChannel(C_NetChannel.Main)
  if mainChannel then
    local plugin = CS.Azur.Gameplay.Core.Net.NetLuaPlugin(self)
    mainChannel:AddPlugin(plugin)
  end
  C_NetManager.SetErrorCodeHandle(handler(self, self.triggerErrorCode))
  self.network = require("ui/auxiliary/network").new()
  self.network:initialize()
end

function L_Net:printNetLog(str, msgId, msgData, byteData)
  if self.isOpenLog then
    local msgDesc = ""
    str = str or ""
    msgId = msgId or ""
    if not string.isEmpty(msgId) then
      msgDesc = MsgGenCode.desc[msgId]
    end
    if msgData == nil and byteData ~= nil then
      msgData = Proto:decode(msgId, byteData)
    end
    printLog(str .. msgId .. " " .. msgDesc, msgData)
  end
end

function L_Net:connect(sucCallback, failedCallback, ip, port, channel)
  channel = channel or self:getDefaultChannel()
  C_NetManager.Connect(ip, port, channel, sucCallback, failedCallback)
end

function L_Net:dispose(channel)
  channel = channel or self:getDefaultChannel()
  C_NetManager.DisposeSocket(channel)
end

function L_Net:disposeAllSocket()
  C_NetManager.DisposeAllSocket()
end

function L_Net:sendMessage(msgId, data, callback, channel, params)
  params = params or {}
  channel = channel or self:getDefaultChannel()
  if params.isMask == nil then
    params.isMask = true
  end
  if params.isLogin == nil then
    params.isLogin = false
  end
  self:printNetLog("=======[TRY MESSAGE SEND LUA]=======", msgId, data)
  local bytes = Proto:encode(msgId, data)
  local seqNo = C_NetManager.SendMessageLua(msgId, bytes, params.isMask == true, params.isLogin == true, channel)
  self:_setOnceListener(channel, msgId, seqNo, callback)
end

function L_Net:GetMessageKey(msgId, seqNo)
  return (msgId << 32) + (self.specailSeqNoMsgIdDic[msgId] or seqNo)
end

function L_Net:RegisterGameConnectEvent(status, callback)
  if not callback then
    return
  end
  C_NetManager.RegisterGameConnectEvent(status, callback)
end

function L_Net:UnregisterGameConnectEvent(status, callback)
  if not callback then
    return
  end
  C_NetManager.UnregisterGameConnectEvent(status, callback)
end

function L_Net:addListener(msgId, callback, channel)
  if not callback then
    return
  end
  channel = channel or self:getDefaultChannel()
  if not self.listeners[channel] then
    self.listeners[channel] = {}
  end
  if not self.listeners[channel][msgId] then
    self.listeners[channel][msgId] = {}
  end
  table.insert(self.listeners[channel][msgId], callback)
end

function L_Net:removeListener(msgId, callback, channel)
  if not callback then
    return
  end
  channel = channel or self:getDefaultChannel()
  if self.listeners[channel] and self.listeners[channel][msgId] then
    table.removeBy(self.listeners[channel][msgId], callback, true)
  end
end

function L_Net:_receiveMessage(netMsgData)
  local channel = C_NetChannel.Main
  local msgId = netMsgData.msgId
  local seqNo = netMsgData.seqNo
  local byteData = netMsgData.bytes
  local errorCode = netMsgData.errorId
  if self.isOpenLog and errorCode ~= 0 then
    self:printNetLog(string.format("===MsgError=== MsgId:%s, ErrorCode:%s SeqNo:%s", msgId, errorCode, seqNo))
  end
  local netMsg
  local key = self:GetMessageKey(msgId, seqNo)
  local callback = self.onceListeners[channel] and self.onceListeners[channel][key]
  local listeners = self.listeners[channel][msgId]
  if callback or listeners and 0 < #listeners then
    local error
    netMsg, error = Proto:decode(msgId, byteData)
    if not netMsg and msgId == 5011 then
      netMsg, error = Proto:decode(5007, byteData)
    end
    if not netMsg then
      errorf("ProtoError:" .. error)
    end
    if callback then
      self.onceListeners[channel][key] = nil
      xpcall(callback, function(str)
        errorf(str, 2)
      end, netMsg, errorCode)
    end
    if listeners and 0 < #listeners then
      for _, listener in ipairs(listeners) do
        xpcall(listener, function(str)
          errorf(str, 2)
        end, netMsg, errorCode)
      end
    end
  end
  if string.isEmpty(MsgGenCode.rsp[msgId]) and netMsgData.protoData == nil then
    if 0 < string.len(byteData) then
      errorf("\229\174\162\230\136\183\231\171\175\231\188\186\229\176\145\229\141\143\232\174\174 proto: " .. msgId)
    else
      warnf("\229\141\143\232\174\174 proto:" .. msgId .. " rsp \228\184\186\231\169\186\239\188\140\228\184\141\229\186\148\230\148\182\229\136\176\229\155\158\229\140\133")
    end
  end
  if self.isOpenLog and errorCode ~= 0 then
    printLog(string.format("triggerErrorCode msgId:%s, errorCode:%s", msgId, errorCode))
  end
  if errorCode ~= 0 and not white[msgId] then
    self:triggerErrorCode(msgId, errorCode)
  end
end

function L_Net:getDefaultChannel()
  return C_NetChannel.Main
end

function L_Net:hasSocket(channel)
  channel = channel or self:getDefaultChannel()
  return C_NetManager.HasSocket(channel)
end

function L_Net:triggerErrorCode(msgId, errorCode)
  if errorCode == L_Const.localCallBackErrorCode then
    return
  end
  local tpl = L_GameTpl:getErrorCodeTpl():getTplById(errorCode)
  local func = function()
    if errorCode >= errorMsgCode.ErrCodeSysBusy and errorCode <= errorMsgCode.ErrCodeServerNotReach then
      self:onKicked()
      return
    end
    if errorCode == errorMsgCode.ErrCodeKickByGS then
      self:onKicked()
      return
    end
  end
  local content = L_GameTpl:getErrorCodeTpl():getName(tpl) .. (tpl and "" or errorMsgCode[errorCode] or "" .. "  " .. errorCode)
  self:showNetCommonTip({
    txtContent = content,
    closeCallback = func,
    hideConfirm = true,
    hideCancel = true
  })
end

function L_Net:showNetCommonTip(data)
  L_GameUtil.showNetCommonTip(data)
end

function L_Net:onLogin(reconnect, errorCode, fullData)
  C_NetManager.OnLogin(reconnect, errorCode, fullData)
end

function L_Net:_setOnceListener(channel, msgId, seqNo, cb)
  if not cb then
    return
  end
  if not self.onceListeners[channel] then
    self.onceListeners[channel] = {}
  end
  local key = self:GetMessageKey(msgId, seqNo)
  self.onceListeners[channel][key] = cb
end

function L_Net:_clearOnceListeners(channel)
  local listeners = self.onceListeners[channel]
  if listeners then
    self.onceListeners[channel] = {}
    local empty = {}
    for _, v in pairs(listeners) do
      xpcall(v, function(str)
        errorf(str, 2)
      end, empty, L_Const.localCallBackErrorCode)
    end
  end
end

function L_Net:isWaitingNetOption()
  local st = L_UI:getPageStatus("pageNetworkWaiting")
  if st and st ~= "closed" then
    return true
  end
  local st = L_UI:getPageStatus("pageNetCommonTip")
  if st and st ~= "closed" then
    return true
  end
  return false
end

return L_Net
