---@diagnostic disable: undefined-field, undefined-global
local next,assert,unpack = next,assert,unpack
local table_find,table_remove,table_concat,table_insert,table_create=table.find,table.remove,table.concat,table.insert,table.create
local string_byte=string.byte
local bit32_lshift,bit32_extract,bit32_band=bit32.lshift,bit32.extract,bit32.band
local websocket = {sockets={}}
-- https://gist.github.com/stravant/30dd9442cc9f24a938756192daf9e718
-- can be replaced by a bindableevent if not using the test environment
local signal = (function()
--------------------------------------------------------------------------------
--                  Simple Correct Signal Implementation                      --
-- This is the most straightforwards possible pure Lua implementation of a    --
-- Signal class that correctly implements all of the RBXScriptSignal          --
-- behavior (Connect, Disconnect, and Wait)                                   --
--------------------------------------------------------------------------------
local Signal = {}
Signal.__index = Signal

local Connection = {}
Connection.__index = Connection

function Connection.new(signal, handler)
	return setmetatable({
		_handler = handler,
		_signal = signal,
	}, Connection)
end

function Connection:Disconnect()
	local signal = self._signal
	local index = table.find(signal, self)
	if index then
		table.remove(signal, index)
	end
end

function Signal.new()
	return setmetatable({}, Signal)
end

function Signal:Connect(fn)
	local handler = Connection.new(self, fn)
	table.insert(self, handler)
	return handler
end

function Signal:DisconnectAll()
	table.clear(self)
end

function Signal:Fire(...)
	local handlersCount = #self
	local handlersCopy
	if handlersCount > 0 then
		handlersCopy = table.create(handlersCount)
		table.move(self, 1, handlersCount, 1, handlersCopy)
		for i = handlersCount, 1, -1 do
			task.spawn(handlersCopy[i]._handler, ...)
		end
	end
end

function Signal:Wait()
	local waitingCoroutine = coroutine.running()
	local cn;
	cn = self:Connect(function(...)
		cn:Disconnect()
		task.spawn(waitingCoroutine, ...)
	end)
	return coroutine.yield()
end

function Signal:Once(fn)
	local cn;
	cn = self:Connect(function(...)
		cn:Disconnect()
		fn(...)
	end)
	return cn
end

return Signal
end)()
--
local WebSocket = {}
WebSocket.__index = WebSocket
function WebSocket:Close()
   assert(websocket.initialized,"Not initialized!")
   if self.closed then return end
   self.OnClose:Fire(false)
   self.closed = true
   websocket.rpc_close(self.id)
   if table_find(websocket.sockets,self) then
      table_remove(websocket.sockets,table_find(websocket.sockets,self))
   end
end
local function close_from_server(ws)
   if ws.closed then return end
   ws.OnClose:Fire(true)
   ws.closed = true
   if table_find(websocket.sockets,ws) then
      table_remove(websocket.sockets,table_find(websocket.sockets,ws))
   end
end
local b64_decode,b64_encode
function WebSocket:Send(data)
   assert(websocket.initialized,"Not initialized!")
   if self.closed then return end
   websocket.rpc_send(self.id,b64_encode(data))
end
--
function websocket.connect(url)
   assert(websocket.initialized,"Not initialized!")
   local success,res = websocket.rpc_open(url)
   if res=="Waiting on permission." then
      repeat task.wait(1); success,res = websocket.rpc_open(url) until success or res~="Waiting on permission."
   end
   assert(success,res)
   local new = setmetatable({id=res,OnMessage=signal.new(),OnClose=signal.new()},WebSocket)
   table_insert(websocket.sockets,new)
   return new
end
function websocket.init(rpc2)
   if websocket.initialized then return end
   websocket.initialized = true
   websocket.rpc2 = rpc2
   b64_decode,b64_encode = rpc2.b64_decode,rpc2.b64_encode
   rpc2.before_shutdown(function()
      for _,v in next, websocket.sockets do
         v:Close()
      end
      websocket.sockets = {}
      task.cancel(websocket.poll_thread)
      websocket.rpc2 = nil
      websocket.rpc_open = nil
      websocket.rpc_close = nil
      websocket.rpc_send = nil
      websocket.rpc_poll = nil
   end)
   local rpc = rpc2.rpc
   websocket.rpc_open = rpc.websocket_open
   websocket.rpc_close = rpc.websocket_close
   websocket.rpc_send = rpc.websocket_send
   websocket.rpc_poll = rpc.websocket_poll
   websocket.poll_thread = task.spawn(function()
      while true do
         task.wait(.1)
         if #websocket.sockets > 0 then
	         local ids = {}
	         for _,v in next, websocket.sockets do
	            table_insert(ids,v.id)
	         end
	         local s,r = websocket.rpc_poll(unpack(ids))
	         -- print("poll",s,r)
	         if s then
	     --     	local count = 0
	     --     	for i,v in next, r do
	     --     		count=count+1
						-- print("amount of "..i,#v)
						-- for i2,v2 in next, v do
						-- 	print(i,i2,unpack(v2[1]),v2[2])
						-- end
	     --     	end
	     --     	print("amount of response",count)
		         for _,v in next, websocket.sockets do
		            local evs = r[v.id]
		            if evs then
		               for _,v2 in next, evs do
		                  local flags = v2[1]
		                  local data = b64_decode(v2[2])
		                  if flags[1] then
		                     v.OnMessage:Fire(data)
		                  end
		                  if flags[4] then
		                     close_from_server(v)
		                     break
		                  end
		               end
		            end
		         end
	         end
         end
      end
   end)
   if task and task.do_not_block_exit then task.do_not_block_exit(websocket.poll_thread) end
end
return websocket
