#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <time.h>
#include "command.h"

void printFile(char* filename);
void getFileName(char* filename, char* fullFilePath);

int main(int argc, char* argv[]) {
	if (argc < 3){
	       printf("usage %s hostname port\n", argv[0]);
	       exit(0);
	 }

	struct sockaddr_in serv_addr;
	struct hostent *server;
	int sock, n, cmd, encryptionOn = 1, exists = 0;
	char buff[BUFF_SIZE];
	char myLocation[BUFF_SIZE];
	char cmdArg[BUFF_SIZE];
	char filename[BUFF_SIZE];
	srand(time(NULL));

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket create");
		exit(1);
	}

	server = gethostbyname(argv[1]); //servAddress);
	if (server == NULL) {
		printf("Server not found.\nExiting...\n");
		exit(1);
	}
	memset(&serv_addr, '0', sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(atoi(argv[2]));//5001);
	bcopy((char *) server->h_addr, (char*)&serv_addr.sin_addr.s_addr, server->h_length);

	printf("Connecting\n");
	if (connect(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
		printf("Failed to connect.\nExiting...\n");
		exit(1);
	}

	// get current location in server directory tree
	bzero(buff, BUFF_SIZE);
	if ((n = read(sock, buff, BUFF_SIZE)) <= 0) {
		perror("receive location");
		exit(1);
	}
	strcpy(myLocation, buff);
	while (1) {
		bzero(buff, BUFF_SIZE);
		bzero(cmdArg, BUFF_SIZE);
		printf("[user@server %s]$ ", myLocation);
		fflush(stdout);
		fgets(buff, BUFF_SIZE, stdin);

		// if command is enc, toggle encryption
		if (strncmp(buff, "enc", 3) == 0) {
			encryptionOn = (encryptionOn == 0) ? 1 : 0;
			printf("File transfer encryption turned %s\n",
					(encryptionOn == 0) ? "off" : "on");
			continue;
		}
		cmd = command_parse(buff, cmdArg, encryptionOn);
		if (cmd == CMD_EMPTY)
			continue;
		if (cmd == CMD_INVALID) {
			printf("Invalid command\n");
			continue;
		}

		// if valid command, write to server
		sprintf(buff, "%d", cmd);
		if ((n = write(sock, buff, BUFF_SIZE)) <= 0) {
			perror("send command");
		}

		// based on the command, perform a transaction
		switch (cmd) {
		case CMD_LS:
			receiveFile(sock, LS_FILENAME, buff, 1, 1, 0);
			printFile(LS_FILENAME);
			break;
		case CMD_CD:
			// write the location argument to the server
			bzero(buff, BUFF_SIZE);
			strcpy(buff, cmdArg);
			if ((n = write(sock, buff, BUFF_SIZE)) <= 0) {
				perror("write location");
				break;
			}
			// get the new location from the server
			if ((n = read(sock, buff, BUFF_SIZE)) <= 0) {
				perror("read location");
				break;
			}
			bzero(myLocation, BUFF_SIZE);
			strcpy(myLocation, buff);
			break;
		case CMD_GET:
		case CMD_GET_ENCRYPT:
			// send filename
			bzero(buff, BUFF_SIZE);
			strcpy(buff, cmdArg);
			if ((n = write(sock, buff, BUFF_SIZE)) <= 0)
				perror("main : send argument");
			//receive confirmation
			bzero(buff, BUFF_SIZE);
			if ((n = read(sock, buff, BUFF_SIZE)) <= 0)
				perror("main : receive confirmation");
			//receive file
			if (strtol(buff, NULL, 10) == RES_PROCEED)
				receiveFile(sock, cmdArg, buff, (cmd == CMD_GET) ? 0 : 1, 1, 1);
			else
				printf("File transmission was denied.\n");
			break;
		case CMD_PUT:
		case CMD_PUT_ENCRYPT:
			// send file name to server
			getFileName(filename, cmdArg);
			strcpy(buff, filename);
			printf("file: %s\n", buff);
			if((n = write(sock, buff, BUFF_SIZE)) <= 0){
				perror("main : put write file name");
			}
			//check existence of file
			exists = doesFileExist(cmdArg, "\0");
			bzero(buff, BUFF_SIZE);
			// tell the server whether the file exists and will be sent
			sprintf(buff, "%d", (exists == 1) ? RES_PROCEED : RES_CEASE);
			if ((n = write(sock, buff, BUFF_SIZE)) <= 0)
				perror("communicate : write response");
			// send the requested file
			if (exists == 1) {
				sendFile(sock, filename, buff, (cmd == CMD_GET) ? 0 : 1, 1, 1);
			}
			else
				printf("File not found\n");
			break;
		case CMD_QUIT:
			if (close(sock) < 0)
				perror("close socket");
			else
				printf("\nGoodbye\n");
			exit(0);
			break;
		}
	}

	if (n < 0) {
		printf("\n Read error\n");
	}

	return EXIT_SUCCESS;
}

// prints a file to stdout
void printFile(char* filename) {
	FILE* file;
	char c;
	if ((file = fopen(filename, "r")) == NULL) {
		perror("printFile : fopen");
		return;
	}
	while ((c = getc(file)) != EOF)
		putchar(c);
	fclose(file);
}

// parse filename out of fullFilePath
void getFileName(char* filename, char* fullFilePath) {
	char* head, *filenamePtr;
	char* fullFilePathCopy[BUFF_SIZE * 2];
	bzero(filename, BUFF_SIZE);
	strcpy(fullFilePathCopy, fullFilePath);
	head = strtok(fullFilePathCopy, "/\\");
	filenamePtr = head;
	if (head == NULL) {
		strncpy(filename, fullFilePathCopy, BUFF_SIZE - 1);
		return;
	}
	while(head != NULL){
		filenamePtr = head;
		head = strtok(NULL, "/\\");
	}
	strcpy(filename, filenamePtr);
}
