#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include "projcommon.c"
#define OKAY_TEST_COUNT (16)


/********************************************************************8******/
/******* OLDER CODE, that may be useful, or it may not, we'll see **********/
/********************************************************************8******/

/* 
** FORMAT:
**    --------------------------------------------------------------
**   |    8 bit     |   32 bit    |    32 bit   |   variable length |
**   | packet type  |   sequence  |    length   |      payload      |
**    --------------------------------------------------------------
**
*/
uint8_t* packet_to_buffer_old(struct packet *packet, unsigned int *packet_size){
	uint8_t  *buffer;
	uint8_t *buffer_start;
	*packet_size = (HEADER_SIZE + packet->length) * sizeof(uint8_t);
	buffer_start = buffer = malloc(*packet_size);
	/* Insert packet type */
	*buffer = packet->type;
	buffer += PACKET_TYPE_SIZE;
	/* Insert 32 bit sequence */
	num_to_buffer32(buffer, htonl(packet->sequence));
	buffer += PACKET_SEQUENCE_SIZE;
	/* Insert 32 bit length   */
	num_to_buffer32(buffer, htonl(packet->length));
	buffer += PACKET_LENGTH_SIZE;
	/* Insert variable length payload */
	(void) memcpy(buffer, packet->payload, packet->length);
	return buffer_start;
}

struct packet* buffer_to_packet_old(uint8_t *buffer){
	struct packet *packet = malloc(sizeof(struct packet));
	/* Get packet type */
	packet->type = (char) *buffer;
	buffer += PACKET_TYPE_SIZE;
	/* Extract 32 bit sequence */
	packet->sequence = ntohl(buffer_to_num32(buffer));
	buffer += PACKET_SEQUENCE_SIZE;
	/* Extract 32 bit length   */
	packet->length   = ntohl(buffer_to_num32(buffer));
	buffer += PACKET_LENGTH_SIZE;
	/* Extract variable length payload */
	packet->payload = malloc(packet->length * sizeof(uint8_t));
	(void) memcpy(packet->payload, buffer, packet->length);
	return packet;
}




char num_to_buffer_utest(){
	uint8_t *b;
	b = malloc(8);
	uint32_t original = 1235457890;
	uint32_t result;
	uint16_t original16 = 12354;
	uint16_t result16;
	char success = 1;
	num_to_buffer32(b, original);
	result = buffer_to_num32(b);
	if(result != original){
		printf("Correct: %i, Result: %i", original, result);
		printf("num_to_buffer & buffer_to_num (32): FAIL\n");
		success = 0;
	} else {
		printf("num_to_buffer & buffer_to_num (32): success\n");
	}
	b = malloc(8);
	num_to_buffer16(b, original16);
	result16 = buffer_to_num16(b);
	if(result16 != original16){
		printf("Correct: %i, Result: %i", original16, result16);
		printf("num_to_buffer & buffer_to_num (16): FAIL\n");
		success = 0;
	} else {
		printf("num_to_buffer & buffer_to_num (16): success\n");
	}
	return success;
}
int imhere=0;
void f(){ fflush(stderr); }
void IM_HERE(){ fprintf(stderr, "%04i I'm here.\n", imhere++); f();}


char packet_to_buffer_utest(){
	uint8_t *buffer;
	struct packet *original_packet = malloc(sizeof(struct packet));
	struct packet *result_packet;
	int i;
	unsigned int buffer_size;
	original_packet->priority = 'P'; 
	original_packet->type = 'R';
	original_packet->sequence = 0xDEADBEEF;
	original_packet->length = 512;
	original_packet->payload = malloc(original_packet->length);
	original_packet->source.sin_addr.s_addr = 0xFEE1900D;
	original_packet->source.sin_port = 1337;
	original_packet->destination.sin_addr.s_addr = 0x900DFEE1;
	original_packet->destination.sin_port = 6969;
	while(i < original_packet->length){
		/* Make an example message which is just % 256 */ 
		*(original_packet->payload+i) = i % 256;
		i++;
	}
	buffer = packet_to_buffer(original_packet, &buffer_size);

	result_packet = buffer_to_packet(buffer);
	if(result_packet->priority != original_packet->priority) {
		printf("priority: Correct: %c, Result: %c\n", original_packet->priority,
				result_packet->priority);
		printf("packet_to_buffer & buffer_to_packet: FAIL");
		return 0;
	}
	/* SOURCE */
	if(result_packet->source.sin_addr.s_addr != 
			original_packet->source.sin_addr.s_addr) {
		printf("source.");
		printf("sin_addr: Correct: %016x, Result: %016x\n", 
				original_packet->source.sin_addr.s_addr,
				result_packet->source.sin_addr.s_addr);
		printf("packet_to_buffer & buffer_to_packet: FAIL");
		return 0;
	}

	if(result_packet->source.sin_port != 
			original_packet->source.sin_port) {
		printf("source.");
		printf("sin_port: Correct: %016x, Result: %016x\n", 
				original_packet->source.sin_port,
				result_packet->source.sin_port);
		printf("packet_to_buffer & buffer_to_packet: FAIL");
		return 0;
	}
	/* DESTINATION */
	if(result_packet->destination.sin_addr.s_addr != 
			original_packet->destination.sin_addr.s_addr) {
		printf("destination.");
		printf("sin_addr: Correct: %016x, Result: %016x\n", 
				original_packet->destination.sin_addr.s_addr,
				result_packet->destination.sin_addr.s_addr);
		printf("packet_to_buffer & buffer_to_packet: FAIL");
		return 0;
	}

	if(result_packet->destination.sin_port != 
			original_packet->destination.sin_port) {
		printf("destination.");
		printf("sin_port: Correct: %016x, Result: %016x\n", 
				original_packet->destination.sin_port,
				result_packet->destination.sin_port);
		printf("packet_to_buffer & buffer_to_packet: FAIL");
		return 0;
	}



	if(result_packet->type != original_packet->type) {
		printf("type: Correct: %c, Result: %c\n", original_packet->type,
				result_packet->type);
		printf("packet_to_buffer & buffer_to_packet: FAIL");
		return 0;
	}
	if(result_packet->sequence != original_packet->sequence) {
		printf("sequence: Correct: %016x, Result: %016x\n", 
				original_packet->sequence,
				result_packet->sequence);
		printf("packet_to_buffer & buffer_to_packet: FAIL");
		return 0;
	}
	if(result_packet->length   != original_packet->length  ) {
		printf("length: Correct: %016x, Result: %016x\n", 
				original_packet->length  ,
				result_packet->length  );
		printf("packet_to_buffer & buffer_to_packet: FAIL");
		return 0;
	}
	
	i=0 ;
	while(i<original_packet->length){
		if(*(original_packet->payload + i) != 
				*(result_packet->payload+i)){
			printf("payload: Payload does not match at %i, should be: %02x", i,
					*(result_packet->payload+i) );
			printf("packet_to_buffer & buffer_to_packet: FAIL");
			return 0;
		}
		i++;
	}
	
	printf("packet_to_buffer & buffer_to_packet: success\n");
	return 1;	
}

int packet_q_utest(){
	struct packet original_packet[OKAY_TEST_COUNT];
	struct packet *next;
	struct packet other_packet;
	int i;
	int bad_sequence_number = 13;
	/* make a queue */
	struct packet_q q;
	packet_q_new(&q);
	/* make a bunch of pakcets... */
	i = 0;
	while(i < OKAY_TEST_COUNT){
		original_packet[i].priority = 1; 
		original_packet[i].type = 'R';
		original_packet[i].sequence = i;
		original_packet[i].length = 512 + i;
		original_packet[i].payload = malloc(original_packet->length);
		original_packet[i].source.sin_addr.s_addr = 0xFEE1900D;
		original_packet[i].source.sin_port = 1337 + i;
		original_packet[i].destination.sin_addr.s_addr = 0x900DFEE1;
		original_packet[i].destination.sin_port = 6969 + i;
		i++;
	}
	if(q.length != 0){
		printf("packet_q: length should be zero.");
		return 0;
	}
	packet_q_reset_iter(&q);
	if(packet_q_next(&q) != NULL){
		printf("packet_q: next should start as null for empty list.");
		return 0;
	}
	/* add all the packets to the queue */
	i = 0;
	while(i < OKAY_TEST_COUNT){
		packet_q_enqueue(&q, &original_packet[i]);
		i++;
	}
	if(q.length != OKAY_TEST_COUNT){
		printf("packet_q: length incorrect.");
		return 0;
	}

	/* check looping and that the enqueueing was successful */
	i = 0;
	packet_q_reset_iter(&q);
	while(i < OKAY_TEST_COUNT){
		next = packet_q_next(&q);
		if(next == NULL){
			printf("packet_q: next is null before end of list.");
			return 0;
		}
		if(original_packet[i].sequence != next->sequence){
			printf("packet_q: wrong packet, sequence numbers do not match up.");
			return 0;
		}
		i++;
	}
	next = packet_q_next(&q);
	if(next != NULL){
		printf("packet_q: next should be null.");
		return 0;
	}
	/* remove a packet with sequence number 13 */
	i = 0;
	packet_q_reset_iter(&q);
	while(i < OKAY_TEST_COUNT){
		next = packet_q_next(&q);
		if(next == NULL){
			printf("packet_q: next is null before end of list. (2)");
			return 0;
		}
		if(original_packet[i].sequence != next->sequence){
			printf("packet_q: wrong packet, sequence numbers do not match up. (2)");
			return 0;
		}
		if(next->sequence == bad_sequence_number){
			packet_q_remove_current(&q);
		}
		i++;
	}
	next = packet_q_next(&q);
	if(next != NULL){
		printf("packet_q: next should be null. (2)");
		return 0;
	}
	/* makes sure that it is removed */
	if(q.length != OKAY_TEST_COUNT-1){
		printf("packet_q: remove failed, length incorrect.(3)");
		return 0;
	}
	i = 0;
	packet_q_reset_iter(&q);
	while(i < OKAY_TEST_COUNT-1){
		next = packet_q_next(&q);
		if(next == NULL){
			printf("packet_q: next is null before end of list. (3)");
			return 0;
		}
		if(next->sequence == bad_sequence_number){
			printf("packet_q: sequence number not removed! (3)"); 
			return 0;
		}
		i++;
	}
	next = packet_q_next(&q);
	if(next != NULL){
		printf("packet_q: next should be null. (3)");
		return 0;
	}
	/* enqueues another */
	other_packet = original_packet[0];
	other_packet.sequence = 1337;
	packet_q_enqueue(&q, &other_packet);
	if(q.length != OKAY_TEST_COUNT){
		printf("packet_q: enqueue failed, length incorrect. (4)");
		return 0;
	}
	packet_q_reset_iter(&q);
	next = packet_q_next(&q);
	if(next == NULL){
		printf("packet_q: enqueue failed, null. (3)");
		return 0;
	}
	/* test normal iteration method, and that current time is as expected */
	struct packet* packet;
	packet_q_reset_iter(&q);
	i = 0;
	double last_current_time = 0;
	while((packet = packet_q_next(&q))){
		if(i==OKAY_TEST_COUNT && next->sequence != other_packet.sequence){
			printf("packet_q: enqueue failed, sequence not match: %i!=%i (4)",
					next->sequence, other_packet.sequence);
			return 0;
		}
		if(i++>OKAY_TEST_COUNT) {
			printf("packet_q: next failed to work normally. (4.5)");
			return 0;
		}
		if(last_current_time > q.current_time){
			printf("packet_q: current time went up!? %lf>%lf (4.5)", 
					last_current_time, q.current_time);
			return 0;
		}
		last_current_time = q.current_time;
	}
	/* remove them all */
	i = 0;
	packet_q_reset_iter(&q);
	while(i < OKAY_TEST_COUNT){
		next = packet_q_next(&q);
		if(next == NULL){
			printf("packet_q: next is null before end of list. (5)");
			return 0;
		}
		if(q.length != OKAY_TEST_COUNT - i){
			printf("packet_q: length does not match. (5)");
			return 0;
		}
		packet_q_remove_current(&q);
		i++;
	}
	next = packet_q_next(&q);
	if(next != NULL){
		printf("packet_q: next should be null. (5)");
		return 0;
	}
	if(q.length != 0){
		printf("packet_q: length should be zero. (5)");
		return 0;
	}
	packet_q_reset_iter(&q);
	if(packet_q_next(&q) != NULL){
		printf("packet_q: next should start as null for empty list. (5)");
		return 0;
	}
	/* NOW WE TEST the sequence number thing */
	/* adds them all again in a arbitrary order */ 
	packet_q_add_by_sequence(&q, &original_packet[10]);
	packet_q_add_by_sequence(&q, &original_packet[3]);
	packet_q_add_by_sequence(&q, &original_packet[1]);
	packet_q_add_by_sequence(&q, &original_packet[4]);
	packet_q_add_by_sequence(&q, &original_packet[2]);
	packet_q_add_by_sequence(&q, &original_packet[6]);
	packet_q_add_by_sequence(&q, &original_packet[9]);
	packet_q_add_by_sequence(&q, &original_packet[7]);
	packet_q_add_by_sequence(&q, &original_packet[5]);
	packet_q_add_by_sequence(&q, &original_packet[4]);
	packet_q_add_by_sequence(&q, &original_packet[0]);
	packet_q_add_by_sequence(&q, &original_packet[2]);
	packet_q_add_by_sequence(&q, &original_packet[12]);
	packet_q_add_by_sequence(&q, &original_packet[11]);
	packet_q_add_by_sequence(&q, &original_packet[15]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[12]);
	packet_q_add_by_sequence(&q, &original_packet[14]);
	packet_q_add_by_sequence(&q, &original_packet[15]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[13]);
	packet_q_add_by_sequence(&q, &original_packet[12]);
	packet_q_add_by_sequence(&q, &original_packet[8]);
	packet_q_add_by_sequence(&q, &original_packet[1]);
	packet_q_add_by_sequence(&q, &original_packet[1]);
	i = 0;
	packet_q_reset_iter(&q);
	while(i < OKAY_TEST_COUNT){
		next = packet_q_next(&q);
		if(next == NULL){
			printf("packet_q: next is null before end of list. (5)");
			return 0;
		}
		if(original_packet[i].sequence != next->sequence){
			printf("packet_q: does not amthch. (5)");
			return 0;
		}
		i++;
	}
	next = packet_q_next(&q);
	if(next != NULL){
		printf("packet_q: next is NOT null at end of list. (5)");
		return 0;
	}
	/* test normal iteration method, and that current time is as expected */
	packet_q_reset_iter(&q);
	i = 0;
	int last_seq = -100;
	while((packet = packet_q_next(&q))){
		if(i==OKAY_TEST_COUNT && next->sequence != other_packet.sequence){
			printf("packet_q: enqueue failed, sequence not match: %i!=%i (6)",
					next->sequence, other_packet.sequence);
			return 0;
		}
		if(i++>OKAY_TEST_COUNT) {
			printf("packet_q: next failed to work normally. (6)");
			return 0;
		}
		//fprintf(stderr, "%i,", packet->sequence);
		/*
		if(last_seq> packet->sequence){
			printf("packet_q: current seqy went up!? %i>%i (6)", 
					last_seq, packet->sequence);
			return 0;
		}*/
		last_seq = packet->sequence;
	}

	return 1;
}

/*
int current_host(){
	char dot_dec[32];
	gethostname(dot_dec, 32);
	dot_dec[31] = 0;
	printf("This should print out the current host: '%s'\n", dot_dec);
}
*/

int main(){
	/*current_host();*/
	num_to_buffer_utest();
	packet_to_buffer_utest();
	if(packet_q_utest()) printf("\npacket_q: success\n");
	else printf("\npacket_q: FAIL!\n");
	return 0;
}







