/* 
 * Book Share System Layer
 * 
 * system.c
 *
 * Implementation of the book share system layer. Manages the internal 
 * organization of books and reserves.
 * Implements the interface functions specified in system.h.
 *
 */


#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <strdup.h>
#include "system.h"
#include "auth.h"
#include "storage.h"
#include "neighbours.h"
#include "timeout.h"


/*
 * File system interface functions
 */


int system_init(char* users_filename, char* titles_filename) {
	printf("\33[1m[system_init]\33[0m system initialized.\n");
	
	return (auth_new(users_filename) == 0
		&& storage_new(titles_filename) == 0
		&& neighbours_new(DEFAULT_NEIGHBOURS, DEFAULT_SERVERS) == 0) ? 0 : -1;
}


void system_give(char* titles) {
	char booktitle[MAX_BOOKNAME_SIZE];
	
	printf("\33[1m[system_give]\33[0m handling a 'Give' request.\n");
	
	for(int i = 0; i < strlen(titles) && sscanf(titles + i, "%s ", booktitle) != EOF; 
		i += MAX_BOOKNAME_SIZE) {
		
		printf("\33[1m[system_give]\33[0m gave '%s' to the counter.\n", booktitle);
		storage_give(booktitle);
	}	
}


void system_search(char* username, char* password, char* titles, int weight,
	sys_list_t** results) {
	int search_status;
	char booktitle[MAX_BOOKNAME_SIZE];
	char counter[MAX_COUNTER_SIZE];
	char neighbours[MAX_NEIGHBOURS_SIZE];
	
	*results = sys_lst_new();
	
	printf("\33[1m[system_search]\33[0m handling a 'Search' request.\n");
	
	for(int i = 0; i < strlen(titles) && sscanf(titles + i, "%s ", booktitle) != EOF; 
		i += MAX_BOOKNAME_SIZE) {
		
		sprintf(neighbours, "%s", "");
		sprintf(counter, "%s %s %d", SERVER_ID, SERVER_HOST, SERVER_PORT);
		
		search_status = storage_search(username, password, booktitle);
		sys_lst_iitem_t* result = (sys_lst_iitem_t*) malloc(sizeof(sys_lst_iitem_t));
		
		result->type = SEARCH;
		strncpy(result->system_response.search.title, booktitle, MAX_BOOKNAME_SIZE);
		strncpy(result->system_response.search.counter, counter, MAX_COUNTER_SIZE);
		result->system_response.search.weight = weight;
		strncpy(result->system_response.search.neighbours, neighbours, MAX_NEIGHBOURS_SIZE);
		strncpy(result->system_response.search.reason, NOSTR, MAX_REASON_SIZE);
		
		switch (search_status) {
			// Finds book
			case 0:
				result->status = OK;
				
				sys_lst_insert(*results, result);
				printf("\33[1m[system_search]\33[0m '%s' reserved.\n", booktitle);
				
				timeout_reserve_start(strdup(username), strdup(booktitle));
				
				break;
			// Authentication error
			case -1:
				result->status = ERROR;
				strncpy(result->system_response.search.reason, "Authentication Failed",
					MAX_REASON_SIZE);
				
				sys_lst_insert(*results, result);
				printf("\33[1m[system_search]\33[0m '%s''s authentication failed.\n", username);
				break;
			// Doesn't find book
			case -2:
				neighbours_get(neighbours);
				result->status = OK;
				strncpy(result->system_response.search.counter, "0", MAX_COUNTER_SIZE);
				strncpy(result->system_response.search.neighbours, neighbours, MAX_NEIGHBOURS_SIZE);
				
				sys_lst_insert(*results, result);
				printf("\33[1m[system_search]\33[0m '%s' doesn't exist.\n", booktitle);
				break;
			default:
				printf("\33[1m[system_search]\33[0m unknown error!\n");
				exit(-1);
		}
	}
}


void system_cancel(char* username, char* titles) {
	int cancel_status;
	char booktitle[MAX_BOOKNAME_SIZE];

	printf("\33[1m[system_cancel]\33[0m handling a 'Cancel' request.\n");
	
	for(int i = 0; i < strlen(titles) && sscanf(titles + i, "%s ", booktitle) != EOF;
		i += MAX_BOOKNAME_SIZE) {
		timeout_reserve_cancel(username, booktitle);
		
		cancel_status = storage_cancel(username, booktitle);
		
		switch (cancel_status) {
			case 0:
				printf("\33[1m[system_cancel]\33[0m '%s''s reserve canceled.\n", booktitle);
				break;
			case -1:
				printf("\33[1m[system_cancel]\33[0m user '%s' doesn't exist.\n", username);
				break;
			case -2:
				printf("\33[1m[system_cancel]\33[0m '%s' wasn't reserved.\n", booktitle);
				break;
			default:
				printf("\33[1m[system_cancel]\33[0m unknown error!\n");
				exit(-1);
		}
	}
}


void system_take(char* username, char* titlecs, sys_list_t** results) {
	int take_status, cover, size;
	char titlec[MAX_BOOKNAME_SIZE + 2];
	char booktitle[MAX_BOOKNAME_SIZE];
	char counter[MAX_COUNTER_SIZE];
	
	*results = sys_lst_new();
	
	printf("\33[1m[system_take]\33[0m handling a 'Take' request.\n");
	
	for(int i = 0; i < strlen(titlecs) && sscanf(titlecs + i, "%s ", titlec) != EOF;
		i += MAX_BOOKNAME_SIZE + 2) {
		sprintf(counter, "%s %s %d", SERVER_ID, SERVER_HOST, SERVER_PORT);
				
		size = 0;
		strncpy(booktitle, titlec, MAX_BOOKNAME_SIZE - 1);
		booktitle[MAX_BOOKNAME_SIZE - 1] = '\0';
		cover = strtol(titlec + MAX_BOOKNAME_SIZE, NULL, 10);
		
		printf("\33[1m[system_take]\33[0m take '%s'%s", booktitle, cover ?
			" and download cover.\n" : ".\n");
		
		timeout_reserve_cancel(username, booktitle);
		
		take_status = storage_take(username, booktitle);
		sys_lst_iitem_t* result = (sys_lst_iitem_t*) malloc(sizeof(sys_lst_iitem_t));
				
		result->type = TAKE;
		strncpy(result->system_response.take.title, booktitle, MAX_BOOKNAME_SIZE);
		strncpy(result->system_response.take.counter, counter, MAX_COUNTER_SIZE);
		result->system_response.take.size = size;
		result->system_response.take.cover = NULL;
		strncpy(result->system_response.take.reason, NOSTR, MAX_REASON_SIZE);
		
		switch (take_status) {
			// Takes book
			case 0:
				result->status = OK;
				
				if (cover == 1) {
					FILE *fp;
					char cover_filename[MAX_FILENAME_SIZE];
					
					strcpy(cover_filename, COVER_PATH);
					strcpy(cover_filename + strlen(cover_filename), booktitle);
					strcpy(cover_filename + strlen(cover_filename), COVER_EXTENSION);
					
					printf("\33[1m[system_take]\33[0m loading cover: '%s'\n", cover_filename);
					
					fp = fopen(cover_filename, "r");
					
					if (fp != NULL) {
						fseek(fp, 0L, SEEK_END);
						size = ftell(fp);
						fseek(fp, 0L, SEEK_SET);
						
						printf("\33[1m[system_take]\33[0m cover size: %d\n", size);
						
						result->system_response.take.size = size;
						result->system_response.take.cover = (char*) malloc(sizeof(char) * size);
						fread(result->system_response.take.cover, sizeof(char), size, fp);
						
						fclose(fp);
					} else {
						printf("\33[1m[system_take]\33[0m unknown cover!\n");
						sprintf(result->system_response.take.reason, "Book %s reserved but cover does not exist.", 
							result->system_response.take.title);
					}
				}
				
				sys_lst_insert(*results, result);
				printf("\33[1m[system_take]\33[0m '%s' taken.\n", booktitle);
				break;
			// User does not exist
			case -1:
				result->status = ERROR;
				
				strncpy(result->system_response.take.reason, "Username does not exist",
					MAX_REASON_SIZE);
				
				sys_lst_insert(*results, result);
				printf("\33[1m[system_take]\33[0m '%s''s authentication failed.\n", username);
				break;
			// Book does not exist or not reserved
			case -2:
				result->status = ERROR;
				
				sprintf(result->system_response.take.reason, "Book %s does not exist or not reserved.", 
						result->system_response.take.title);
			
				sys_lst_insert(*results, result);
				printf("\33[1m[system_take]\33[0m '%s' does not exist or not reserved.\n",
					booktitle);
				break;
			default:
				printf("\33[1m[system_take]\33[0m unknown error!\n");
				exit(-1);
		}
	}
}


void system_release(char* username, char* titles, int makeavailable, sys_list_t** results) {
	int release_status;
	char booktitle[MAX_BOOKNAME_SIZE];
	char counter[MAX_COUNTER_SIZE] = "A";
	
	*results = sys_lst_new();
	
	printf("\33[1m[system_release]\33[0m handling a 'Release' request.\n");
	
	for(int i = 0; i < strlen(titles) && sscanf(titles + i, "%s ", booktitle) != EOF;
		i += MAX_BOOKNAME_SIZE) {
		sprintf(counter, "%s %s %d", SERVER_ID, SERVER_HOST, SERVER_PORT);
		
		release_status = storage_release(username, booktitle, makeavailable);
		sys_lst_iitem_t* result = (sys_lst_iitem_t*) malloc(sizeof(sys_lst_iitem_t));
		
		result->type = RELEASE;
		strncpy(result->system_response.release.title, booktitle, MAX_BOOKNAME_SIZE);
		strncpy(result->system_response.release.counter, counter, MAX_COUNTER_SIZE);
		strncpy(result->system_response.release.reason, NOSTR, MAX_REASON_SIZE);
		
		switch (release_status) {
			// Book released
			case 0:
				result->status = OK;
				
				sys_lst_insert(*results, result);
				
				if (makeavailable) {
					printf("\33[1m[system_release]\33[0m '%s' released and returned.\n", booktitle);
				} else {
					printf("\33[1m[system_release]\33[0m '%s' released.\n", booktitle);
				}
				break;
			// Authentication error
			case -1:
				result->status = ERROR;
				strncpy(result->system_response.release.reason, "Authentication Failed",
					MAX_REASON_SIZE);
				
				sys_lst_insert(*results, result);
				printf("\33[1m[system_release]\33[0m '%s''s authentication failed.\n", username);
				break;
			// Book isn't taken
			case -2:
				result->status = ERROR;
				
				sprintf(result->system_response.release.reason, "Book %s isn't taken.", 
						result->system_response.release.title);				
				
				sys_lst_insert(*results, result);
				printf("\33[1m[system_release]\33[0m '%s' isn't taken.\n", booktitle);
				break;
			default:
				printf("\33[1m[system_release]\33[0m unknown error!\n");
				exit(-1);
		}
	}
}


void system_return(char* titles) {
	char booktitle[MAX_BOOKNAME_SIZE];
	
	printf("\33[1m[system_return]\33[0m handling a 'Return' request.\n");
	
	for(int i = 0; i < strlen(titles) && sscanf(titles + i, "%s ", booktitle) != EOF;
		i += MAX_BOOKNAME_SIZE) {
		
		printf("\33[1m[system_return]\33[0m returned '%s' to the preferred counter.\n", booktitle);
		storage_give(booktitle);
	}	
}
