CTCPSocket		*tcp = TCPSocket(AF_INET);
CTCPTreeQueue	*connections = CAlloc(sizeof(CTCPTreeQueue)); // head
U8				*buffer = CAlloc(ETHERNET_FRAME_SIZE);

QueueInit(connections);

U0 ChatServerKill()
{
	CTCPTreeQueue	*conn = connections->next;
	CTCPTreeQueue	*next_conn;

	while (conn != connections)
	{
		"\nClosing socket @ 0x%0X\n", conn->socket;
		TCPSocketClose(conn->socket);
		next_conn = conn->next;
		Free(conn);
		conn = next_conn;
	}

	"\nClosing listening socket.\n";
	TCPSocketClose(tcp);

	return;
}

U0 ChatServerBroadcast(CTCPSocket *tcp_socket, I64 length)
{ // Broadcast length bytes of msg in buffer to all but original socket.
	CTCPTreeQueue	*conn = connections->next;
	CTCPSocket		*dest_socket;
	U8				*ip_string;
	CIPV4Address	 addr;
	U8				*message;
	U8				*message_prefix;

	while (conn != connections)
	{
		dest_socket = conn->socket;

		if (dest_socket != tcp_socket)
		{
			addr.address = EndianU32(dest_socket->destination_address(CSocketAddressIPV4).address.address);

			ip_string = NetworkToPresentation(AF_INET, &addr);
			// TODO: is NetworkToPresentation backwards? or, do socket addrs store BE or LE ?

			"\nBroacasting msg to %s.\n", ip_string;

			addr.address = EndianU32(tcp_socket->destination_address(CSocketAddressIPV4).address.address);

			ip_string = NetworkToPresentation(AF_INET, &addr);
			// TODO: is NetworkToPresentation backwards? or, do socket addrs store BE or LE ?

			message_prefix = MStrPrint("$$BG,PURPLE$$$$BLACK$$<%s>$$FG$$$$BG$$ %%0%dts", ip_string, length);

			message = MStrPrint(message_prefix, buffer);

			TCPSocketSendString(dest_socket, message);

			Free(message);
			Free(message_prefix);
			Free(ip_string);

		}

		conn = conn->next;
	}

	return;
}

U0 ChatServerBroadcastDisconnect()
{
	CTCPTreeQueue	*conn = connections->next;
	CTCPSocket		*conn_socket;
	U8				*message = MStrPrint("$$BG,LTGRAY$$$$DKGRAY$$Client disconnected. Connected clients: %d$$FG$$$$BG$$",
											QueueCount(connections));
	while (conn != connections)
	{
		conn_socket = conn->socket;

		TCPSocketSendString(conn_socket, message);

		conn = conn->next;
	}

	Free(message);

}

U0 ChatServerReceive()
{
	CTCPTreeQueue	*conn = connections->next;
	CTCPTreeQueue	*next_conn;
	CTCPSocket		*socket;
	I64				 message_len;
	U8				*ip_string;
	CIPV4Address	 addr;


	while (conn != connections)
	{
		socket = conn->socket;
		message_len = TCPSocketReceive(socket, buffer, ETHERNET_FRAME_SIZE);

		if (message_len == 0)
		{
			"\nClosing a connection.\n";
			socket->timeout = TCP_TIMEOUT;
			TCPSocketClose(socket);

			next_conn = conn->next;
			QueueRemove(conn);
			Free(conn);
			conn = next_conn;

			ChatServerBroadcastDisconnect();

		}
		else if (message_len > 0)
		{
			addr.address = EndianU32(socket->destination_address(CSocketAddressIPV4).address.address);

			ip_string = NetworkToPresentation(AF_INET, &addr);
			// TODO: is NetworkToPresentation backwards? or, do socket addrs store BE or LE ?

			"\nBroadcasting %d byte msg from %s: %Q\n", message_len, ip_string, buffer;

			//ClassRep(socket);
			ChatServerBroadcast(socket, message_len);
			MemSet(buffer, 0, ETHERNET_FRAME_SIZE);
			conn = conn->next;
		}
		else
		{
			//"\nReceived -1 [error], trying next connection.\n";
			conn = conn->next;
		}
	}

	return;

}

U0 ChatServer()
{
	CSocketAddressIPV4	 socket_addr;
	U8					*port_string = StrGet("Server Port: ");
	I64					 port = Str2I64(port_string);
	CTCPSocket			*new_socket;
	CTCPTreeQueue		*new_conn;
	U8					*join_msg;
	CTCPTreeQueue		*conn;
	CTCPSocket			*conn_socket;


	Free(port_string);

	socket_addr.port			= EndianU16(port);
	socket_addr.family			= AF_INET;
	socket_addr.address.address	= INADDR_ANY;

	tcp->timeout = 0.3 * JIFFY_FREQ;

	"\nTrying to bind socket.\n";
	if (TCPSocketBind(tcp, &socket_addr) == 0)
		"\nSocket bound.\n";
	else
	{
		"\nFailed to bind socket.\n";
		ChatServerKill;
		return;
	}

	"\nTrying to listen on socket.\n";
	if (TCPSocketListen(tcp, 5) == 0)
		"\nSocket now listening.\n";
	else
	{
		"\nFailed to listen on socket.\n";
		ChatServerKill;
		return;
	}

	while (CharScan != CH_SHIFT_ESC)
	{
		new_socket = TCPSocketAccept(tcp);

		if (new_socket > 0)
		{
			"\nNew connection.\n";
			new_conn = CAlloc(sizeof(CTCPTreeQueue));
			new_conn->socket = new_socket;
			new_socket->timeout = 0; // Set new connection non-blocking.

			join_msg = MStrPrint("$$BG,LTGRAY$$$$DKGRAY$$Connected clients: %d$$FG$$$$BG$$", QueueCount(connections) + 1);

			TCPSocketSendString(new_socket, join_msg);
			Free(join_msg);

			join_msg = MStrPrint("$$BG,LTGRAY$$$$DKGRAY$$New connection. Connected clients: %d$$FG$$$$BG$$",
									QueueCount(connections) + 1);

			conn = connections->next;

			while (conn != connections)
			{
				"\nNotifying clients of new connection.\n";
				conn_socket = conn->socket;
				TCPSocketSendString(conn_socket, join_msg);
				conn = conn->next;
			}
			Free(join_msg);

			QueueInsertRev(new_conn, connections);
		}

		ChatServerReceive; // Receive & Broadcast
		Sleep(50);
	}


	ChatServerKill;
}

ChatServer;