local opcodeCallbacks = {}
local extendedCallbacks = {}
local extendedJSONCallbacks = {}
local extendedJSONData = {}
local maxPacketSize = 65000

function ProtocolGame:onOpcode(opcode, msg)
  for i, callback in pairs(opcodeCallbacks) do
    if i == opcode then
      callback(self, msg)
      return true
    end
  end
  return false
end

function ProtocolGame:onExtendedOpcode(opcode, buffer)
  local callback = extendedCallbacks[opcode]
  if callback then
    callback(self, opcode, buffer)
  end
  
  callback = extendedJSONCallbacks[opcode]
  if callback then
    local status = buffer:sub(1,1) -- O - just one message, S - start, P - part, E - end
    local data = buffer:sub(2)
    if status ~= "E" and status ~= "P" then
      extendedJSONData[opcode] = ""
    end
    if status ~= "S" and status ~= "P" and status ~= "E" then
      extendedJSONData[opcode] = buffer
    else
      extendedJSONData[opcode] = extendedJSONData[opcode] .. data
    end
    if status ~= "S" and status ~= "P" then
      local json_status, json_data = pcall(function() return json.decode(extendedJSONData[opcode]) end)
      extendedJSONData[opcode] = nil
      if not json_status then
        error("Invalid data in extended JSON opcode (" .. json_status .. "): " .. json_data)
        return
      end
      callback(self, opcode, json_data)
    end
  end
end

function ProtocolGame.registerOpcode(opcode, callback)
  if opcodeCallbacks[opcode] then
    error('opcode ' .. opcode .. ' already registered will be overriden')
  end

  opcodeCallbacks[opcode] = callback
end

function ProtocolGame.unregisterOpcode(opcode)
  opcodeCallbacks[opcode] = nil
end

function ProtocolGame.registerExtendedOpcode(opcode, callback)
  if not callback or type(callback) ~= 'function' then
    error('Invalid callback.')
  end

  if opcode < 0 or opcode > 255 then
    error('Invalid opcode. Range: 0-255')
  end

  if extendedCallbacks[opcode] then
    error('Opcode is already taken.')
  end

  extendedCallbacks[opcode] = callback
end

function ProtocolGame.unregisterExtendedOpcode(opcode)
  if opcode < 0 or opcode > 255 then
    error('Invalid opcode. Range: 0-255')
  end

  if not extendedCallbacks[opcode] then
    error('Opcode is not registered.')
  end

  extendedCallbacks[opcode] = nil
end

function ProtocolGame.registerExtendedJSONOpcode(opcode, callback)
  if not callback or type(callback) ~= 'function' then
    error('Invalid callback.')
  end

  if opcode < 0 or opcode > 255 then
    error('Invalid opcode. Range: 0-255')
  end

  if extendedJSONCallbacks[opcode] then
    error('Opcode is already taken.')
  end

  extendedJSONCallbacks[opcode] = callback
end

function ProtocolGame.unregisterExtendedJSONOpcode(opcode)
  if opcode < 0 or opcode > 255 then
    error('Invalid opcode. Range: 0-255')
  end

  if not extendedJSONCallbacks[opcode] then
    error('Opcode is not registered.')
  end

  extendedJSONCallbacks[opcode] = nil
end

function ProtocolGame:sendExtendedJSONOpcode(opcode, data)
  if opcode < 0 or opcode > 255 then
    error('Invalid opcode. Range: 0-255')
  end

  if type(data) ~= "table" then
    error('Invalid data type, should be table')
  end
  
  local buffer = json.encode(data)  
  local s = {}
  for i=1, #buffer, maxPacketSize do
     s[#s+1] = buffer:sub(i,i+maxPacketSize - 1)
  end
  if #s == 1 then
    self:sendExtendedOpcode(opcode, s[1])
    return
  end
  self:sendExtendedOpcode(opcode, "S" .. s[1])
  for i=2,#s - 1 do
    self:sendExtendedOpcode(opcode, "P" .. s[i])
  end
  self:sendExtendedOpcode(opcode, "E" .. s[#s])
end
