#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <time.h>
#include "command.h"
#define MAX_CONN 5

void communicate(int, char*, char*);

void echo(char* prompt, char* string);

int main(int argc, char* argv[]) {
	if(argc < 2){
		printf("usage %s port\n", argv[0]);
       exit(0);
	}
	int sock, newsocketfd, clientlen, pid = 1, connCount = 0;
	struct sockaddr_in serv_addr, cli_addr;
	char clientLocation[BUFF_SIZE] = "./\0";
	char prompt[16];
	srand(time(NULL));

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

	bzero((char*) &serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = INADDR_ANY;
	serv_addr.sin_port = htons(atoi(argv[1]));//5001);

	// bind socket
	if (bind(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
		perror("bind");
		exit(1);
	}

	// listen for clients
	listen(sock, MAX_CONN);
	clientlen = sizeof(cli_addr);
	while (1) {
		if ((newsocketfd = accept(sock, (struct sockaddr*) &cli_addr,
				&clientlen)) < 0) {
			perror("accept");
		} else {
			connCount++;
			pid = fork();
			if (pid == 0) {		// child process
				sprintf(prompt, "[CLIENT %d] ", connCount);
				echo(prompt, "connection established\n");
				fflush(stdout);
				close(sock);
				communicate(newsocketfd, clientLocation, prompt);
				return 0;
				fflush(stdout);
			} else {			// parent process
				close(newsocketfd);
				signal(SIGCHLD, SIG_IGN);
			}
		}
	}
	close(sock);

	return EXIT_SUCCESS;
}

void communicate(int sockfd, char* location, char* prompt) {
	int n, cmd, exists = 0;
	char buff[BUFF_SIZE];
	char filename[BUFF_SIZE];
	char fullFilePath[BUFF_SIZE*2];
	// send starting location to client
	if ((n = write(sockfd, location, BUFF_SIZE)) <= 0) {
		perror("communicate : write location");
	}
	while (1) {
		bzero(buff, BUFF_SIZE);
		// read client command
		if ((n = read(sockfd, buff, BUFF_SIZE)) < 1) {
			perror("read");
			return;
		}

		cmd = strtol(buff, NULL, 10);
		fflush(stdout);

		switch (cmd) {
		case CMD_INVALID:
			break;
		case CMD_LS:
			// send directory tree file to client
			echo(prompt, "command : ls\n");
			cmd_call_ls(location);
			sendFile(sockfd, LS_FILENAME, buff, 1, 1, 0);
			break;
		case CMD_CD:
			echo(prompt, "command : cd\n");
			// get requested location from client
			bzero(buff, BUFF_SIZE);
			if ((n = read(sockfd, buff, BUFF_SIZE)) <= 0) {
				perror("read location");
				break;
			}
			cmd_call_cd(location, buff);
			// write new location to client
			if ((n = write(sockfd, location, BUFF_SIZE)) < 1) {
				perror("write location");
				return;
			}
			break;
		case CMD_GET:
		case CMD_GET_ENCRYPT:
			echo(prompt, "command : get\n");
			// get file name from client
			bzero(buff, BUFF_SIZE);
			if ((n = read(sockfd, buff, BUFF_SIZE)) <= 0) {
				perror("read location");
				break;
			}
			bzero(filename, BUFF_SIZE);
			strcpy(filename, buff);
			// check if file exists and confirm or deny send
			exists = doesFileExist(location, filename);
			bzero(buff, BUFF_SIZE);
			sprintf(buff, "%d", (exists == 1) ? RES_PROCEED : RES_CEASE);
			if ((n = write(sockfd, buff, BUFF_SIZE)) <= 0)
				perror("communicate : write response");
			// send the requested file
			if(exists == 1){
				printf("file: %s%s\n", location, filename);
				bzero(fullFilePath, BUFF_SIZE*2);
				sprintf(fullFilePath, "%s%s", location, filename);
				sendFile(sockfd, fullFilePath, buff, (cmd == CMD_GET) ? 0 : 1, 1, 1);
				fflush(stdout);
			}
			break;
		case CMD_PUT:
		case CMD_PUT_ENCRYPT:
			echo(prompt, "command : put\n");
			// get file name from client
			bzero(buff, BUFF_SIZE);
			if ((n = read(sockfd, buff, BUFF_SIZE)) <= 0) {
				perror("read location");
				break;
			}
			bzero(fullFilePath, BUFF_SIZE*2);
			sprintf(fullFilePath, "%s%s", location, buff);
			//receive confirmation
			bzero(buff, BUFF_SIZE);
			if ((n = read(sockfd, buff, BUFF_SIZE)) <= 0)
				perror("main : receive confirmation");
			//receive file
			if (strtol(buff, NULL, 10) == RES_PROCEED)
				receiveFile(sockfd, fullFilePath, buff, (cmd == CMD_GET) ? 0 : 1, 1, 1);
			else
				printf("File transmission was denied.\n");
			break;
		case CMD_QUIT:
			echo(prompt, "command : quit\n");
			if (close(sockfd) < 0)
				perror("closing socket");
			else
				echo(prompt, "Connection closed\n");
			return;
		}
	}
}

void echo(char* prompt, char* string) {
	printf("%s%s", prompt, string);
}

