/*
	i/o completion ports are supported in winnt and up.
	win2k introduced thread pool functions which make high perf stuff even easier.

	the thread pool has the idea of i/o and non-i/o threads.  non-i/o threads will
	be destroyed and created as needed to ensure optimal cpu and smp usage.  i/o
	threads are never destroyed.  async operations are be cancelled if the thread
	that made the call is destroyed, so we always use an i/o thread to begin an
	operation.

	win2k also intruced some lightweight timer objects (which integrate with the
	thread pool) that could be used for handling timeouts.

	this uses the win2k thread pool functions so _WIN32_WINNT must be >= 0x0500
*/

#define _WIN32_WINNT 0x0500

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>

// context info for all operations.
typedef struct tag_socket_data {
	OVERLAPPED ovl; // winsock uses this internally, we tag the rest of the data on.
	void (*handler)(int,struct tag_socket_data*);
	SOCKET sock;
} socket_data_t;

///////////////////////////////////////////////////////////
/// Sending

static void on_send(int res, socket_data_t *data) {
	if(res!=-1) {
		puts("send finished successfully!");
		// check to see if all the bytes were sent.. blah blah.

		// begin recieving.
	}
	else puts("send failed!");
}

// this is called in an i/o thread.
static DWORD WINAPI beginsend_thread(socket_data_t *data) {
	WSABUF buf;
	DWORD dwlen, flags=0;
	int ret;

	buf.buf="GET / HTTP/1.0\r\n\r\n";
	buf.len=16;

	// dwlen must be given but we can ignore it.  flags can be the normal recv() flags.
	ret=WSASend(data->sock, &buf, 1, &dwlen, 0, &data->ovl, NULL);
	if(ret!=SOCKET_ERROR || WSAGetLastError()==WSA_IO_PENDING) {
		puts("send began successfully!");
	}
	else {
		free(data);
		puts("unable to begin recv!");
	}

	return 0;
}

// this performs as much work as possible then shoots it off to an i/o thread
// to begin the async operation.
static void begin_send(SOCKET sock) {
	socket_data_t *sd;
	BOOL ret;

	// calloc() as the OVERLAPPED struct must be zeroed.
	sd=calloc(1, sizeof(socket_data_t));

	sd->handler=on_send;
	sd->sock=sock;

	// queue up a work item to be executed in an i/o thread.
	ret=QueueUserWorkItem(beginsend_thread, sd, WT_EXECUTEINIOTHREAD);
	if(!ret) {
		free(sd);
		puts("unable to queue send work item!");
	}
}

// this thunks our calls out to on_send (or on_recv etc).
// note: this is a non-i/o thread.
static void CALLBACK on_iocp(DWORD err, DWORD transfered, OVERLAPPED *ovl) {
	socket_data_t *data=(socket_data_t*)ovl;
	int res=(err==ERROR_SUCCESS)?(int)transfered:-1;

	data->handler(res, data);
}

static void cleanup(void) {
	WSACleanup();
}

int main(void) {
	SOCKET sock;

	{
		WSADATA data;
		WSAStartup(WINSOCK_VERSION, &data);
	}
	atexit(cleanup);

	// you would likely want to push getaddrinfo() into the thread pool.

	// create the socket as overlapped
	sock=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if(sock==INVALID_SOCKET) {
		puts("WSASocket failed!");
		return -1;
	}

	// bind it to the builtin threadpool.
	if(!BindIoCompletionCallback((HANDLE)sock, on_iocp, 0)) {
		closesocket(sock);

		puts("BindIoCompletionCallback failed!");
		return -1;
	}

	{
		struct sockaddr_in addr;

		addr.sin_family=PF_INET;
		addr.sin_addr.s_addr=inet_addr("66.102.7.99");
		addr.sin_port=htons(80);

		if(connect(sock, (const struct sockaddr*)&addr, sizeof(addr))==SOCKET_ERROR) {
			closesocket(sock);

			puts("connect failed!");
			return -1;
		}
		/* todo: winxp comes with an async connect that can be checked for at runtime.
			if it doesn't exist, async must be simulated using non-blocking i/o and
			select in a thread. */
	}

	begin_send(sock);

	puts("press enter to continue...");
	getc(stdin);

	closesocket(sock);

	return 0;
}



















