#include "game_websocket_client.h"
#include "../common/messagedispatcher.hpp"

#include <sstream>

#define DEFAULT_PORT 8000

static void OutPut(LPCWSTR fmt, ...)
{
	va_list args;
	va_start(args, fmt);

	wchar_t buffer[1024];
	wchar_t * msg = buffer;
	int len = _vsnwprintf_s(buffer, ARRAYSIZE(buffer) - 2, _TRUNCATE, fmt, args); //留2个字节给\r\n
	if (len < 0)
	{
		len = _vscwprintf(fmt, args);
		msg = new wchar_t[len + 4];
		len = _vsnwprintf_s(msg, len + 4, _TRUNCATE, fmt, args);
	}
	msg[len++] = '\r';
	msg[len++] = '\n';
	msg[len] = '\0';
	OutputDebugStringW(msg);
	if (msg != buffer) { delete msg; }
}


game_websocket_client::game_websocket_client()
{
}

game_websocket_client::~game_websocket_client()
{
	mg_mgr_free(&m_mg_mgr);
}

std::string wcs_to_mbs(const wchar_t* src, size_t len = -1, int codepage = CP_UTF8)
{
	const wchar_t* pSrc = src;
	int dst_len = WideCharToMultiByte(codepage, 0, pSrc, len, NULL, 0, NULL, NULL);
	std::string mbstr;
	mbstr.resize(dst_len);
	WideCharToMultiByte(codepage, 0, pSrc, len, &mbstr[0], dst_len, NULL, NULL);

	auto pos = mbstr.find('\0');
	if (pos != std::string::npos)
		mbstr.resize(pos);

	return mbstr;
}

bool game_websocket_client::start_websocket(const std::wstring& openid, 
	const std::wstring& port,
	connect_cb on_connect, 
	receive_cb on_received, 
	brocken_cb on_broken)
{
	
	m_ws_cb.on_connect = std::move(on_connect);
	m_ws_cb.on_received = std::move(on_received);
	m_ws_cb.on_broken = std::move(on_broken);

	std::string port_conv = wcs_to_mbs(port.c_str());
	std::string openid_conv = wcs_to_mbs(openid.c_str());

	std::string m_ws_url = "http://127.0.0.1:" + port_conv;
	m_ws_url += "/websocket/";
	m_ws_url += openid_conv;

	OutPut(L"[qqgamews]connect: %S", m_ws_url);

	bool done = false;        // Event handler flips it to true
	mg_mgr_init(&m_mg_mgr);        // Initialise event manager
	m_mg_c = mg_ws_connect(&m_mg_mgr, m_ws_url.c_str(), callback, this, NULL);     // Create client

	if (m_mg_c != nullptr)
	{
		run_in_separate_thread([this]()
		{
			for (;;)
			{
				mg_mgr_poll(&m_mg_mgr, 1000);
			}
		});
	}

	return true;
}


void game_websocket_client::callback(struct mg_connection *c, int ev, void *ev_data, void *fn_data)
{
	game_websocket_client* ws_client = static_cast<game_websocket_client*>(fn_data);
	if (ws_client != nullptr)
	{
		ws_client->handle_event(c, ev, ev_data);
	}
}

void game_websocket_client::handle_event(struct mg_connection *c, int ev, void *ev_data)
{
	if (ev == MG_EV_WS_OPEN)
	{
		OutPut(L"[qqgamews]连接成功");

		run_in_main_thread([this]()
		{
			if (m_ws_cb.on_connect)
			{
				m_ws_cb.on_connect(true);
			}
		});
	}
	else if (ev == MG_EV_CLOSE) 
	{
		OutPut(L"[qqgamews]连接断开");
		
		run_in_main_thread([this]()
		{
			if (m_ws_cb.on_broken)
			{
				m_ws_cb.on_broken();
			}
		});
	}
	else if (ev == MG_EV_WS_MSG)
	{
		// Got websocket frame. Received data is wm->data. 
		struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
		std::string data(wm->data.ptr, wm->data.len);
		OutPut(L"[qqgamews]received: %S", data.c_str());

		run_in_main_thread([this, data]()
		{
			if (m_ws_cb.on_received)
			{
				m_ws_cb.on_received(data);
			}
		});

		mg_iobuf_delete(&c->recv, c->recv.len);
	}
}


bool game_websocket_client::send_data(const std::string& data)
{

	if (m_mg_c)
	{
		size_t sent = mg_ws_send(m_mg_c, data.c_str(), data.size(), WEBSOCKET_OP_TEXT);
		OutPut(L"[qqgamews]Send(len=%u): %S", sent, data.c_str());

		return true;

	}
	return false;

}

bool game_websocket_client::is_connected()
{
	return m_mg_c
		&& m_mg_c->is_websocket
		&& !m_mg_c->is_closing;
}


void game_websocket_client::run_in_main_thread(std::function<void()>&& func)
{
	if (m_msg_dispatcher)
	{
		m_msg_dispatcher->DispatchMsg(0, std::move(func));
	}
}

void game_websocket_client::run_in_separate_thread(std::function<void()>&& func)
{
	if (!m_msg_dispatcher)
	{
		m_msg_dispatcher.reset(new mjz::MsgDispatcher());
	}
	if (m_msg_dispatcher)
	{
		m_msg_dispatcher->RunInWorkThread(std::move(func));
	}
}

