local Class = require "luanode.class"
local EventEmitter = require "luanode.event_emitter"
local Net = require "luanode.net"
local merge = require "socket-io-client.utils".merge
local Data = require "socket-io-client.data"
local XHRPolling = require "socket-io-client.transports.xhr-polling"
local json = require "json"

local ipairs, pcall, require, type, console = ipairs, pcall, require, type, console

local Io = Class.InheritsFrom(EventEmitter)

function Io:__init (host, options)

	options = options or {}
	local newClient = Class.construct(Io)

	newClient.host = host
	newClient.port = options.port or 80
	newClient.options = {
		secure = false,
		--document
		port = 80,
		resource = "socket.io",
		transports = { "websocket", "flashsocket", "htmlfile", "xhr-multipart", "xhr-polling" },
		transportOptions = {
			["xhr-polling"] = {
				timeout = 25000	-- based on polling duration default
			},
			["jsonp-polling"] = {
				timeout = 25000
			}
		},
		connectTimeout = 5000,
		tryTransportsOnConnectTimeout = true,
		rememberTransport = true
	}

	merge(newClient.options, options)

	newClient.connected = false
	newClient.connecting = false
	newClient._events = {}

	if options.logger then
		newClient.logger = options.logger
	end

	newClient.logger = newClient.logger or {
		debug = console.debug,
		info = console.info,
		warn = console.warn,
		error = console.error,
		fatal = console.fatal,
		profile = console.info
	}

	newClient.transport = newClient:getTransport()

	return newClient
end

---
--
function Io:getTransport (override)
	local transports = override or self.options.transports
	local match

	if self.options.rememberTransport and not override then
		-- blablabla, como que esto no aplica?
	end

	for _, transport in ipairs(transports) do
		local ok, transport_module = pcall(require, "socket-io-client.transports." .. transport)
		if ok and transport_module.check() then
			return transport_module(self, self.options.transportOptions[transport] or {})
		end
	end

	return nil
end

---
--
function Io:connect ()

	--self.m_server_connection = Net.createConnection(self.port, self.host)
	--self.transport = XHRPolling(self.host, self.port)

	self.transport:connect()
end

---
--
function Io:write (message, atts)
	if not self.transport or not self.transport.connected then
		return self:_queue(message, atts)
	end
	self.transport:write(message, atts)
	return self
end

---
--
function Io:send (message, atts)
	atts = atts or {}
	if type(message) == "table" then
		atts.j = nil
		message = json.encode(message)
	end
	self:write("1", Data.encodeMessage(message, atts))
	return self
end

---
--
function Io:disconnect ()
	self.transport:disconnect()
	return self
end

---
--
function Io:_queue (message, atts)
	self._queueStack = self._queueStack or {}
	table.insert(self._queueStack, { message, atts })
	return self
end

---
--
function Io:_doQueue ()
	if not self._queueStack or #self._queueStack == 0 then
		return self
	end
	for _, v in ipairs(self._queueStack) do
		self.transport:write(v)
	end
	self._queueStack = {}
	return self
end

---
--
function Io:_onConnect ()
	self.connected = true
	self.connecting = false
	self:_doQueue()
	if self.options.rememberTransport then
		-- guardar self.transport.type en algun lado
	end
	self:emit("connect")
end

---
--
function Io:_onMessage (...)
	self:emit("message", ...)
end

---
--
function Io:_onDisconnect ()
	local wasConnected = self.connected
	self.connected = false
	self.connecting = false
	self._queueStack = {}
	if wasConnected then
		self:emit("disconnect")
	end
end


return Io
