//..............................................................................
//
// WebSocket utilities
//

import "io_websocket.jncx"
import "io_WebSocket.jnc"
import "log_Writer.jnc"
import "ui_WebSocketUi.jnc"
import "ui_Dictionary.jnc"
import "WebSocketLog/WebSocketLogRecordCode.jnc"

namespace iox {

//! \addtogroup common-io
//! @{

//..............................................................................

bool isValidUtf8(
	void const* p0,
	size_t size
) {
	char const* p = p0;
	char const* end = p + size;
	while (p < end) {
		if ((*p & 0x80) == 0) { // 0xxxxxxx
			p++;
		} else if ((*p & 0xe0) == 0xc0) { // 110xxxxx 10xxxxxx
			if (p + 2 > end ||
				(p[1] & 0xc0) != 0x80)
				return false;

			p += 2;
		} else if ((*p & 0xf0) == 0xe0) { // 1110xxxx 10xxxxxx 10xxxxxx
			if (p + 3 > end ||
				(p[1] & 0xc0) != 0x80 ||
				(p[2] & 0xc0) != 0x80)
				return false;

			p += 3;
		} else if ((*p & 0xf8) == 0xf0) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
			if (p + 4 > end ||
				(p[1] & 0xc0) != 0x80 ||
				(p[2] & 0xc0) != 0x80 ||
				(p[3] & 0xc0) != 0x80)
				return false;

			p += 4;
		} else {
			return false;
		}
	}

	return true;
}

size_t errorcode transmitWebSocketData(
	io.WebSocket* socket,
	ui.WebSocketTransmitMode mode,
	log.Writer* logWriter,
	void const* p,
	size_t size
) {
	size_t result;
	switch (mode) {
	case ui.WebSocketTransmitMode.Auto:
		result = isValidUtf8(p, size) ?
			socket.writeText(string_t(p, size)) :
			socket.writeBinary(p, size);
		break;

	case ui.WebSocketTransmitMode.Text:
		result = socket.writeText(string_t(p, size));
		break;

	case ui.WebSocketTransmitMode.Binary:
		result = socket.writeBinary(p, size);
		break;
	}

	if (result)
		logWriter.write(log.StdRecordCode.Tx, p, result);

	return result;
}

bool processWebSocketIncomingData(
	io.WebSocket* socket,
	log.Writer* logWriter,
	void* readBuffer,
	size_t readBufferSize
) {
	io.WebSocketFrameOpcode opcode;
	size_t result = socket.read(&opcode, readBuffer, readBufferSize);

	switch (opcode) {
	case io.WebSocketFrameOpcode.Text:
	case io.WebSocketFrameOpcode.Binary:
		if (!result)
			return false;

		logWriter.write(log.StdRecordCode.Rx, readBuffer, result);
		break;

	case io.WebSocketFrameOpcode.Ping:
		logWriter.write(WebSocketLogRecordCode.RxPing, readBuffer, result);

		if (!(socket.m_activeEvents & io.WebSocketEvents.TcpDisconnected)) { // still connected
			socket.writePong(readBuffer, result);
			logWriter.write(WebSocketLogRecordCode.TxPong, readBuffer, result);
		}

		break;

	case io.WebSocketFrameOpcode.Pong:
		logWriter.write(WebSocketLogRecordCode.RxPong, readBuffer, result);
		break;

	case io.WebSocketFrameOpcode.Close:
		logWriter.write(WebSocketLogRecordCode.RxClose);

		if (!(socket.m_activeEvents & (
			io.WebSocketEvents.WebSocketCloseCompleted | // close request
			io.WebSocketEvents.TcpDisconnected           // still connected
		))) {
			socket.writeClose();
			logWriter.write(WebSocketLogRecordCode.TxClose);
		}

		break;

	default:
		return false;
	};

	return true;
}

io.WebSocketHandshakeHeaders const* buildWebSocketHeaders(ui.DictionaryEntry const* dictionary) {
	if (!dictionary)
		return null;

	io.WebSocketHandshakeHeaders headers;
	for (ui.DictionaryEntry const* entry = dictionary; entry; entry = entry.m_next)
		if (entry.m_isChecked)
			headers.add(entry.m_key, entry.m_value);

	return headers;
}

//..............................................................................

//! @}

} // namespace iox
