local y=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local W=require(script.Emitter)local e=require(script.Promise)local T=require(script.Timer)local z=require(script.Transport)local R=setmetatable({},W)R.__index=R function R.new(y,e)local T=setmetatable(W.new(),R)if y then local W=string.match(y,"\094\040\037\119\043\041\058\047\047")local e=string.match(string.gsub(y,"\094\037\119\043\058\047\047",""),"\094\040\091\037\119\037\046-\093\043\058\063\037\100\042\041")T.Host=e T.Secure=W=="\104\116\116\112\115"or W=="\119\115\115"else T.Host="\108\111\099\097\108\104\111\115\116"T.Secure=false end T.Path=e or"\047\101\110\103\105\110\101\046\105\111"T:Open()return T end function R.Open(W)W._Transport=z.new({Host=W.Host;Secure=W.Secure,Path=W.Path})W._Transport:On("\112\097\099\107\101\116",function(e)if e.Type=="\111\112\101\110"then local z=y:JSONDecode(e.Data)W._Transport.Id=z.sid W._PingTimeout=z.pingTimeout/1000 W._PingInterval=z.pingInterval/1000 W._PingTimer=(T.new(function()W:Ping()end)):Start(W._PingInterval)end if e.Type=="\101\114\114\111\114"then W:Close(true)else W:Emit(e.Type,e.Data)end end)W._Transport:On("\101\114\114\111\114",function()W:Close(true)end)end local c={Type="\112\105\110\103"}local S={Type="\099\108\111\115\101"}function R.Ping(y)y:Emit("\112\105\110\103")e.try(function()local W=false;(e.delay(y._PingTimeout)):andThen(function()if not W then y:Close(true)end end)y._Transport:Write(c)y:Wait("\112\111\110\103")W=true end)end function R.Send(W,y)W._Transport:Write({Type="\109\101\115\115\097\103\101";Data=y})end function R.Close(W,y)if y then W:Emit("\099\108\111\115\101",true)else W._Transport:Write(S)W._Transport:Flush(true)end W._Transport:Close()if W._PingTimer then W._PingTimer:Stop()end end return R
local HttpService = game:GetService("HttpService")
local Emitter = require(script.Emitter)
local Promise = require(script.Promise)
local Timer = require(script.Timer)
local Transport = require(script.Transport)

local Socket = setmetatable({}, Emitter)
Socket.__index = Socket

function Socket.new(Uri, Path)
	local self = setmetatable(Emitter.new(), Socket)
	if Uri then
		local Schema = string.match(Uri, "^(%w+)://")
		local Host = string.match(string.gsub(Uri, "^%w+://", ""), "^([%w%.-]+:?%d*)")

		self.Host = Host
		self.Secure = Schema == "https" or Schema == "wss"
	else
		self.Host = "localhost"
		self.Secure = false
	end

	self.Path = Path or "/engine.io"
	self:Open()
	return self
end

function Socket:Open()
	self._Transport = Transport.new {
		Host = self.Host;
		Secure = self.Secure;
		Path = self.Path;
	}

	self._Transport:On("packet", function(Packet)
		if Packet.Type == "open" then
			local Data = HttpService:JSONDecode(Packet.Data)
			self._Transport.Id = Data.sid

			self._PingTimeout = Data.pingTimeout / 1000
			self._PingInterval = Data.pingInterval / 1000

			self._PingTimer = Timer.new(function()
				self:Ping()
			end):Start(self._PingInterval)
		end

		if Packet.Type == "error" then
			self:Close(true)
		else
			self:Emit(Packet.Type, Packet.Data)
		end
	end)

	self._Transport:On("error", function()
		self:Close(true)
	end)
end

local PING_TYPE = {Type = "ping"}
local CLOSE_TYPE = {Type = "close"}

function Socket:Ping()
	self:Emit("ping")
	Promise.try(function()
		local Success = false
		Promise.delay(self._PingTimeout):andThen(function()
			if not Success then
				self:Close(true)
			end
		end)

		self._Transport:Write(PING_TYPE)
		self:Wait("pong")
		Success = true
	end)
end

function Socket:Send(Data)
	self._Transport:Write {
		Type = "message";
		Data = Data;
	}
end

function Socket:Close(Error)
	if Error then
		self:Emit("close", true)
	else
		self._Transport:Write(CLOSE_TYPE)
		self._Transport:Flush(true)
	end

	self._Transport:Close()
	if self._PingTimer then
		self._PingTimer:Stop()
	end
end

return Socket