#include <stdio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>
#include "projcommon.h"
#define HEADER_SIZE (9) 
#define PACKET_TYPE_SIZE (1) 
#define PACKET_SEQUENCE_SIZE (4)
#define PACKET_LENGTH_SIZE (4)
#define LSB_MASK 0x0000FF
#define MAXLEN 15209

void error(char *message){
	fprintf(stderr, "A fatal error has occured: %s",  message);
	fflush(stderr);
	exit(1);
}


int Socket(int domain, int type, int protocol){
	int sockfd;
	if((sockfd = socket(domain, type, protocol)) == -1)
		error("socket didn't set up correctly\n");
	return sockfd;
}

int Bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen){
	int rc;
	if((rc = bind(sockfd, my_addr, addrlen)) == -1){
		perror("Bind:");
		error("bind  didn't work \n");
	}
	return rc; 
}


int Sendto(int s, void *buf, size_t len, int flags, 
		struct sockaddr *to, socklen_t tolen){
	int rc;
	if((rc = sendto(s, buf, len, flags, to, tolen))== -1){
		perror("Error in sendto");
		error("sendto didn't work \n");
	}
	return rc; 
}

ssize_t Write(int fd, const void *buf, size_t count){
	int ret;
	if((ret = write(fd, buf, count)) == -1 || ret != count){
		error("Write failed!\n");
	}
	return ret;
}


ssize_t Recvfrom(int s, void *buf, size_t len, int flags,
		struct sockaddr *from, socklen_t *fromlen){
	int rc;
	if((rc = recvfrom(s,buf,len,flags,from,fromlen)) == -1)
		error("recvfrom is broken.");
	return rc;
}

FILE *Fopen(char *path, char *mode){
	FILE *f;
	if((f=fopen(path, mode))==NULL)
		error("fopen failed, file not found");
	return f;
}

/* simple gettimeofday() wrapper,  returns time as a double */
double get_time(){
	struct timeval tv;
	if(gettimeofday(&tv, NULL)!=0) error("gettimeofday failed.");
	return (double) ((double)tv.tv_sec + ((double)tv.tv_usec / 1e6));
}

char* get_current_time(){
	time_t result;
	struct tm *tp;
	struct timeval tv;
	char* cur_time;
	gettimeofday(&tv, NULL);
	result = time(NULL);
	tp = localtime(&result);
	sprintf(cur_time, "%i:%i:%i", tp->tm_hour, tp->tm_min, tp->tm_sec); //, ((double)(tv.tv_usec / 1e10)));
	return cur_time;
}

struct packet* packet_receive(int sockfd){
	socklen_t len;
	uint8_t buffer[MAXLEN];
	struct sockaddr_in addr;
	struct packet *packet;
	Recvfrom(sockfd, buffer, MAXLEN, 0, (struct sockaddr*) &(addr), &len);
	/* Note: some error checking could feasibly be introduced here, by way 
	** of checking if len and the packet->length match up */
	packet = buffer_to_packet(buffer);
	packet->source = addr;
	return packet;
}

void packet_send(int sockfd, struct packet* packet){
	unsigned int buffer_size;
	uint8_t *buffer;
	buffer = packet_to_buffer(packet, &buffer_size);
	if(sendto(sockfd, buffer, buffer_size, 0, 
				(struct sockaddr*) &packet->destination, 
				sizeof(packet->destination)) == -1){
		error("Error in the sendto function!\n");
	}
}

void print_packet(struct packet* packet){
	fprintf(stderr, "\n-----Packet: %016lx -----\n", 
			(unsigned long) packet);
	fprintf(stderr, "type: %c\n", packet->type);
	fprintf(stderr, "Sequence: %08i\n", packet->sequence);
	fprintf(stderr, "Length: %08i\n", packet->length);
	fprintf(stderr, "Payload location: %016lx\n",
			(unsigned long) packet->payload);
	fflush(stderr);
}

void print_sockaddr(struct sockaddr_in* addr){
	fprintf(stderr, "\n-----sockaddr_in: %016lx -----\n",
			(unsigned long) addr);
	fprintf(stderr, "sin_family: %04i\n", addr->sin_family);
	fprintf(stderr, "sin_port: %04u\n", addr->sin_port);
	fprintf(stderr, "in_addr.s_addr: %016lu\n", 
			(unsigned long) addr->sin_addr.s_addr);
	fflush(stderr);
}
void alog_data(char *d, char* s){
	uint8_t i = 0;
	do {
		if(*(d+i)=='\n') {
			*(s++)='\\';
			*(s++)='n';
		}
		else *(s++)=*(d+i);
	} while(i++<4);
	*s='\0';
}
void alog_print_packet(struct packet* packet, char is_send){
	char data[9], tstr[20], nano_str[10];
	time_t t = time(NULL);
	(void) strftime(tstr, 20, "%H:%M:%S", localtime(&t));
	double nano = get_time();
	nano = nano - floor(nano);
	sprintf(nano_str, "%0.3f", nano);
	alog_data((char*)packet->payload, data);
	printf("Seq#: %i  ", packet->sequence);
	if(is_send) printf("To: ");
	else printf("Size: %i  From: ", packet->length);
	printf("%s  ", packet->dot_addr);
	printf("Time: %s%s  Data: %s\n", tstr, (nano_str+1), data);
/*size_t strftime(char *s, size_t max, const char *format,                const struct tm
		*tm);*/
	/*Seq#: 610  To: 128.105.112.19  Time: 17:18:50.234  Data: this*/
/*Seq#: 640  Size: 30  From: 128.105.112.32  Time: 17:20:14.251  Data:this*/

}
void num_to_buffer(uint8_t* b, uint32_t num){
	*(b+3) = num>>24; *(b+2) = num>>16; *(b+1) = num>>8; *b = num;
}

uint32_t buffer_to_num(uint8_t* b){
	return  ((uint32_t) b[3]<<24) |
		((uint32_t) b[2]<<16) |
		((uint32_t) b[1]<<8)  |
		((uint32_t) b[0]);
}

/* 
** FORMAT:
**    --------------------------------------------------------------
**   |    8 bit     |   32 bit    |    32 bit   |   variable length |
**   | packet type  |   sequence  |    length   |      payload      |
**    --------------------------------------------------------------
**
*/
uint8_t* packet_to_buffer(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_buffer(buffer, htonl(packet->sequence));
	buffer += PACKET_SEQUENCE_SIZE;
	/* Insert 32 bit length   */
	num_to_buffer(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(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_num(buffer));
	buffer += PACKET_SEQUENCE_SIZE;
	/* Extract 32 bit length   */
	packet->length   = ntohl(buffer_to_num(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;
}

