#include <stdio.h>
#include <pthread.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <sys/stat.h>
#include "../htlib.h"

#define FALSE 0
#define TRUE 1

static void*
doSession(void* arg);

int
main(int argc, char* argv[])
{
	int port = 8000;

	int s = socket(PF_INET, SOCK_STREAM, 0);
	if (s == -1) {
		perror("socket");
		return 2;
	}

	int on = 1;
	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))==-1) {
		perror("setsockopt");
		return 3;
	}
	
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);

	if (bind(s, (struct sockaddr*)&addr, sizeof(addr))==-1) {
		perror("bind");
		return 3;
	}
	if (listen(s, 5)==-1) {
		perror("listen");
		return 4;
	}

	fprintf(stderr, "%s start to listen on the port %d...\n", argv[0], port);

	int soc;
	struct sockaddr_storage ss;
	pthread_t th;
	socklen_t slen = sizeof(ss);
	while ((soc=accept(s, (struct sockaddr*)&ss, &slen))!=-1) {
		if ((errno=pthread_create(&th, NULL, doSession, (void*)soc))!=0) {
			perror("pthread_create");
			return 5;
		}
		pthread_detach(th);
	}
	return 6;
}

static void*
doSession(void* arg)
{
	HTLIB ht;
	HTLIB_ERROR err;

	char send_buffer[1000];
	char rec_buffer[1000];

	if (HTLIB_Init(&ht,
				   send_buffer, sizeof(send_buffer),
				   rec_buffer, sizeof(rec_buffer), &err)==FALSE) {

		fprintf(stderr, "HTLIB_Init failed err=%d:%s\n",
				err, HTLIB_GetErrorMessage(err));
		return NULL;
	}
	ht.agent_or_server_name = "HtLib Sample Server/0.9";
	
	HTLIB_Attach(&ht, (int)arg);
	
	while(TRUE) {
		const char* url;
		const char* method;
		HTLIB_Header header_buffer[30];
		HTLIB_USHORT blen = 30;
		
		if ((url=HTLIB_ReceiveRequest(&ht,
									  10000,
									  &method,
									  header_buffer, &blen,
									  NULL,
									  &err))==NULL) {
			if (err == HTLIB_E_TIMEOUT) {
				continue;
			}
			fprintf(stderr, "HTLIB_ReceiveRequest failed err=%d:%s\n",
					err, HTLIB_GetErrorMessage(err));
			break;
		}

		FILE* f;
		if (*++url == 0) {
			f=fopen("index.html", "rb");
		} else {
			f=fopen(url, "rb");
		}
		if (f == NULL) {
			if (HTLIB_SendResponse(&ht, -1,
								   404, NULL,
								   NULL, 0,
								   NULL, 0, &err)==FALSE) {
				fprintf(stderr, "HTLIB_SendResponse failed err=%d:%s\n",
						err, HTLIB_GetErrorMessage(err));
				goto ferr;
			}
			continue;
		}
		
		struct stat st;
		if (fstat(fileno(f), &st)==-1) {
			goto ferr;
		}
		
		HTLIB_Header hd[] = {
			{ "Content-Type", "application/octet-stream", 0, NULL },
		};

		if (HTLIB_SendResponse(&ht, -1,
							   200, NULL,
							   hd, 1,
							   NULL, st.st_size, &err)==FALSE) {
			fprintf(stderr, "HTLIB_SendResponse failed err=%d:%s\n",
					err, HTLIB_GetErrorMessage(err));
			goto ferr;
		}
		
		int len;
		char line[1000];
		while (st.st_size>0) {
			len = sizeof(line);
			if (st.st_size < len) {
				len = st.st_size;
			}
			len = fread(line, 1, len, f);
			if (len == 0) {
				break;
			}
			if (HTLIB_SendBody(&ht, -1, line, len, &err)==FALSE) {
				fprintf(stderr, "HTLIB_SendBody failed err=%d:%s\n",
					err, HTLIB_GetErrorMessage(err));
				goto ferr;
			}
			st.st_size -= len;
		}
		fclose(f);
		continue;
		
	ferr:
		fclose(f);
		break;
	}
	
	HTLIB_Uninit(&ht);
	return NULL;
}
