#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>

#include <curl/curl.h>


int main(int ac, char **av)
{
	int			nrequests;
	int			elapsedcount = 0;
	int			prev_running = 0;
	int			still_running = 0;
	int			started = 0;
	int			performed;
	int			msginqueue;
	
	int			cont;
	int			ret;

	int			maxfds;
	
	char		fname[120];


	CURLcode	curlret;
	
	CURL		** connections;
	CURLM		* curlm;
	CURLMsg		* msg;
	CURL		* finishedconn;


	FILE		** files;

	fd_set		readfds, writefds, exceptfds;
	struct timeval	tv;


	setbuf(stdout, NULL);
	setbuf(stderr, NULL);
	
	if (ac != 3)
	{
		fprintf(stderr, "Usage: %s URL nrequests\n", av[0]);

		return 1;
	}

	if (!(nrequests = atoi(av[2])))
	{
		fprintf(stderr, "nrequests MUST be > 0 !\n");

		return 1;
	}

	if ((connections = malloc(sizeof(CURL *) * nrequests)) == NULL)
	{
		fprintf(stderr, "could not allocate connections vector\n");

		return 1;
	}
	memset(connections, 0, sizeof(CURL *) * nrequests);

	if ((files = malloc(sizeof(FILE *) * nrequests)) == NULL)
	{
		fprintf(stderr, "could not allocate files vector\n");

		return 1;
	}
	memset(files, 0, sizeof(FILE *) * nrequests);

	if (curl_global_init(CURL_GLOBAL_ALL))
	{
		perror("curl_global_init");

		return 1;
	}

	if ((curlm = curl_multi_init()) == NULL)
	{
		perror("curl_multi_init");

		return 1;
	}

	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_ZERO(&exceptfds);

	for (;;)
	{
		if ((started == nrequests) && !still_running)
		{
			printf("All connections completed !\n");

			break;
		}

		prev_running = still_running;

		tv.tv_sec = 0;
		tv.tv_usec = 10000;

		printf("started/still running connections: [%d/%d]\n", started, still_running);

		curl_multi_fdset(curlm, &readfds, &writefds, &exceptfds, &maxfds);

		printf("highest fd set by curl_multi_fdset: [%d]\n", maxfds);

		if (started < nrequests)
		{
			ret = select(maxfds+1, &readfds, &writefds, &exceptfds, &tv);
		}
		else
		{
			printf("blocking select\n");

			ret = select(maxfds+1, &readfds, &writefds, &exceptfds, NULL);
		}

		if (ret)
		{
			printf("data is available !\n");

			performed = 0;

			while (curl_multi_perform(curlm, &still_running) == CURLM_CALL_MULTI_PERFORM)
			{
				performed++;
			}

			printf("curl_multi_perform called %d times\n", performed+1);
		}
		else
		{
			printf("timeout elapsed\n");

			elapsedcount++;

			if (elapsedcount <= nrequests)
			{
				sprintf(fname, "conn%d.txt", elapsedcount);

				if ((files[elapsedcount-1] = fopen(fname, "w")) == NULL)
				{
					fprintf(stderr, "WARNING: connection #%d: fopen error\n", elapsedcount);

					continue;
				}

				if ((connections[elapsedcount-1] = curl_easy_init()) == NULL)
				{
					fprintf(stderr, "WARNING: connection #%d: curl_easy_init error\n", elapsedcount);

					continue;
				}

				printf("connection #%d: curl_easy_init done\n", elapsedcount);

				curl_easy_setopt(connections[elapsedcount-1], CURLOPT_URL, av[1]);
				curl_easy_setopt(connections[elapsedcount-1], CURLOPT_WRITEDATA, files[elapsedcount-1]);
				/*      curl_easy_setopt(connections[elapsedcount-1], CURLOPT_FOLLOWLOCATION, TRUE);    */
				curl_easy_setopt(connections[elapsedcount-1], CURLOPT_SSL_VERIFYPEER, FALSE);
				curl_easy_setopt(connections[elapsedcount-1], CURLOPT_SSL_VERIFYHOST, 1);

				curl_multi_add_handle(curlm, connections[elapsedcount-1]);

				performed = 0;

				while (curl_multi_perform(curlm, &still_running) == CURLM_CALL_MULTI_PERFORM)
				{
					performed++;
				}

				printf("connection #%d: curl_multi_perform called %d times\n", elapsedcount, performed+1);

				started++;
			}
			/*
			else
			{
				curl_multi_perform(curlm, &still_running);
			}
			*/
		}

		printf("prev_running/still_running: [%d/%d]\n", prev_running, still_running);

		if (started && (prev_running > still_running))
		{
			printf("checking finished connections\n");

			while ((msg = curl_multi_info_read(curlm, &msginqueue)) != NULL)
			{
				if (msg->msg == CURLMSG_DONE)
				{
					finishedconn = msg->easy_handle;

					for (cont = 0; cont < nrequests; cont++)
					{
						if (connections[cont] == finishedconn)
						{
							printf("connection #%d finished with code [%d]\n", cont, msg->data.result);

							connections[cont] = NULL;
							fclose(files[cont]);

							break;
						}
					}

					if (cont == nrequests)
					{
						printf("WARNING: unknown connection finished\n");
					}

					curl_multi_remove_handle(curlm, msg->easy_handle);
				}
			}
		}
	}

	curl_multi_cleanup(curlm);

	for (cont = 0; cont < nrequests; cont++)
	{
		if (connections[cont] != NULL)
		{
			printf("cleaning up connection #%d\n", cont);

			curl_easy_cleanup(connections[cont]);
			fclose(files[cont]);
		}
	}

	curl_global_cleanup();

	return 0;
}

