

#include "libcgc.h"
#include "cgc_libc.h"
#include "cgc_operation.h"
list_t *cgc_haiku_list = NULL;
uint32_t next_haiku_id = INIT_HAIKU_ID;
uint32_t cgc_recv_uint32() {
    uint32_t num[1] = {0};
    RECV(num, sizeof(uint32_t));
	return num[0];
}
uint32_t cgc_recv_uint16() {
    uint16_t num[1] = {0};
    RECV(num, sizeof(uint16_t));
	return num[0];
}
uint8_t cgc_haiku_list_exists() {
	if (NULL == cgc_haiku_list) {
		return FALSE;
	} else {
		return TRUE;
	}
}
uint8_t cgc_is_haiku_list_empty() {
	if (0 == cgc_haiku_list->count) {
		return TRUE;
	} else {
		return FALSE;
	}
}
void cgc_init_haiku_list() {
	if (FALSE == cgc_haiku_list_exists()) {
		cgc_haiku_list = cgc_list_create();
	}
}
int cgc_get_count() {
	if (TRUE == cgc_haiku_list_exists()) {
		return cgc_haiku_list->count;
	} else {
		return ERR_LIST_NOT_EXIST;
	}
}
uint32_t cgc_get_id_from_haiku(node_t *haiku) {
	if (NULL != haiku->data) {
		struct haiku *h = (struct haiku *) haiku->data;
		return h->id;
	} else {
		return ERR_EMPTY_NODE;
	}
}
uint32_t cgc_get_next_haiku_id() {
	next_haiku_id++;
	return next_haiku_id - 1;
}
int cgc_get_random_idx(uint32_t *idx) {
	uint32_t random_idx = 0;
	int ret = 0;
	ret = cgc_rand((char *)&random_idx, 4);
	int32_t count = cgc_get_count();
	if (ERR_LIST_NOT_EXIST == count) {
		return count;
	} else {if (0 == count) {
		return ERR_LIST_EMPTY;
	} else {if (0 != ret) {
		return ERR_RAND_FAILED;
	} else {
		*idx = random_idx % (uint32_t)count;
		return SUCCESS;
	}}}
}
int cgc_populate_array_with_haiku_ids(uint32_t id_arr[], uint32_t count) {
	node_t *haiku_ptr = NULL;
	struct haiku *h = NULL;
	uint32_t id = 0;
	if (TRUE == cgc_haiku_list_exists()) {
		haiku_ptr = cgc_haiku_list->tail;
	} else {
		return ERR_LIST_NOT_EXIST;
	}
	if ((0 == count) || (NULL == haiku_ptr)) {
		return ERR_LIST_EMPTY;
	}
	while ((0 < count) && (NULL != haiku_ptr)) {
		h = (struct haiku *)haiku_ptr->data;
		id_arr[count - 1] = h->id;
		count--;
		haiku_ptr = haiku_ptr->prev;
	}
	return SUCCESS;
}
int cgc_find_haiku_with_id(struct haiku **h, uint32_t id) {
	node_t *haiku_ptr = NULL;
	struct haiku *tmp = NULL;
	int count = 0;
	bool_t found = FALSE;
	count = cgc_get_count();
	if (0 < count) {
		haiku_ptr = cgc_haiku_list->head;
	} else {if (0 == count) {
		return ERR_LIST_EMPTY;
	} else {
		return ERR_LIST_NOT_EXIST;
	}}
	while (NULL != haiku_ptr) {
		tmp = (struct haiku *)haiku_ptr->data;
		if (id == tmp->id) {
			*h = tmp;
			found = TRUE;
			break;
		}
		haiku_ptr = haiku_ptr->next;
	}
	if (TRUE == found) {
		return SUCCESS;
	} else {
		return ERR_INVALID_ID;
	}
}
void cgc_send_haiku(struct haiku *h) {
	uint32_t id = h->id;
	char *content = h->content;
	uint16_t length = h->length;
	cgc_send((char *)&id, sizeof(uint32_t));
	cgc_send((char *)&length, sizeof(uint16_t));
	cgc_send(content, length);
}
void cgc_send_easter_egg_haiku() {
	uint32_t id = EGG_ID;
	char *content = EGG_HAIKU;
	uint16_t length = cgc_strlen(content);
	cgc_send((char *)&id, sizeof(uint32_t));
	cgc_send((char *)&length, sizeof(uint16_t));
	cgc_send(content, length);
}
void cgc_send_haiku_id(uint32_t id) {
	SENDUI(id);
}
int cgc_add_haiku_to_list(struct haiku *h) {
	node_t *nd = cgc_node_create((void *)h);
	return cgc_list_push(cgc_haiku_list, nd);
}
int cgc_recv_haiku_line(char *haiku_content_ptr, int16_t bytes_remaining) {
	char temp_line_buf[MAX_HAIKU_LINE_LEN] = {0};
	int16_t bytes_received = 0;
	int16_t bytes_written = 0;
	// bytes_received includes line termination char, 
	//  but line term char is not written to temp_line_buf, a null is in its place.
	bytes_received = cgc_recvline(STDIN, temp_line_buf, MAX_HAIKU_LINE_LEN);
	//  this will make the creation of POV input harder to create 
	//   since it will need line termination chars at set intervals.
	if (0 > bytes_received) {
		return ERR_INVALID_HAIKU;
	}
	// VULN HERE
	// - if bytes_remaining goes neg, MAX_HAIKU_LINE_LEN chars can be written
	//  to haiku_content_ptr for each line of input.
	// +1 for trailing null
	bytes_written = cgc_snprintf(haiku_content_ptr, bytes_remaining + 1, "~c\x07", temp_line_buf);
	return bytes_written;
}
struct haiku *cgc_recv_haiku(uint16_t total_bytes) {
	struct haiku *h = NULL;
	char *haiku_content_ptr = NULL;
	int16_t bytes_remaining = 0;
	int16_t bytes_written = 0;
	bytes_remaining = (int16_t)total_bytes;
	ALLOC(sizeof(struct haiku) + total_bytes + 1, &h); // +1 for trailing null
	haiku_content_ptr = h->content;
	// VULN HERE - if something causes bytes_remaining to go negative, this loop will not stop.
	while (0 != bytes_remaining) {
		bytes_written = cgc_recv_haiku_line(haiku_content_ptr, bytes_remaining);
		if (0 > bytes_written) { // ERR_INVALID_HAIKU
			return NULL;
		}
		haiku_content_ptr += bytes_written;
		bytes_remaining -= bytes_written;
	}
	h->id = cgc_get_next_haiku_id();
	h->length = total_bytes;
	return h;
}
uint16_t cgc_recv_haiku_size() {
	return cgc_recv_uint16(); 
}
// Operation functions
int cgc_add_haiku() {
	int ret = SUCCESS;
	uint16_t total_bytes = 0; // number of bytes in haiku, including line termination chars.
	struct haiku *h = NULL;
	if (FALSE == cgc_haiku_list_exists()) {
		cgc_init_haiku_list();
	}
	total_bytes	= cgc_recv_haiku_size();
	if ((0 < total_bytes) && (MAX_HAIKU_LEN > total_bytes)) {
		h = cgc_recv_haiku(total_bytes);
		if (NULL == h) {
			ret = ERR_INVALID_HAIKU;
		} else {
			ret = cgc_add_haiku_to_list(h);
			cgc_send_haiku_id(h->id);			
		}
	} else {
		ret = ERR_INVALID_HAIKU_LEN;
	}
	return ret;
}
int cgc_get_haiku_by_id() {
	struct haiku *h = NULL;
	uint32_t id = 0;
	int res = 0;
	if (TRUE == cgc_haiku_list_exists()) {
		id = cgc_recv_uint32();
		if (EGG_ID == id) {
			cgc_send_easter_egg_haiku();
		} else {
			res = cgc_find_haiku_with_id(&h, id);
			if (SUCCESS == res) {
				cgc_send_haiku(h);
			}
		}
	} else {
		res = ERR_LIST_NOT_EXIST;
	}
	return res;
}
int cgc_get_haiku_cgc_random() {
	uint32_t random_idx = 0;
	uint32_t count = 0;
	uint32_t *id_arr = NULL;
	struct haiku *rand_haiku = NULL;
	int res = SUCCESS;
	if (TRUE == cgc_haiku_list_exists()) {
		count = cgc_get_count();
		if (0 == count) {
			return ERR_LIST_EMPTY;
		}
		ALLOC(count * sizeof(uint32_t), &id_arr);
		// these functions should not return error due to haiku_list existance or count <= 0.
		res = cgc_populate_array_with_haiku_ids(id_arr, count);
		res = cgc_get_random_idx(&random_idx);
		if (ERR_RAND_FAILED != res) {
			res = cgc_find_haiku_with_id(&rand_haiku, id_arr[random_idx]);
		}
		DEALLOC(id_arr, count * sizeof(uint32_t));
		if (SUCCESS == res) {
			cgc_send_haiku(rand_haiku);
		}
	} else {
		res = ERR_LIST_NOT_EXIST;
	}
	return res;
}
int cgc_get_haiku_count() {
	uint32_t count = 0;
	if (TRUE == cgc_haiku_list_exists()) {
		count = cgc_get_count();
		SENDUI(count);
		return SUCCESS;
	} else {
		return ERR_LIST_NOT_EXIST;
	}
}
int cgc_get_haiku_ids() {
	if (TRUE == cgc_haiku_list_exists()) {
		uint32_t count = (uint32_t)cgc_get_count();
		SENDUI(count);
		node_t *hl_ptr = cgc_haiku_list->head;
		while(NULL != hl_ptr) {
			uint32_t id = cgc_get_id_from_haiku(hl_ptr);
			SENDUI(id);
			hl_ptr = hl_ptr->next;
		}
	} else {
		return ERR_LIST_NOT_EXIST;
	}
	return SUCCESS;
}


