#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#define false 0
#define true 1
#define MAX_LINE_SIZE 2040
#define PWD_BUFFER_SIZE 515
#define MAX_ARGS 257

void
print_error(){
	char error_message[30] = "An error has occurred\n";
	write(STDERR_FILENO, error_message, strlen(error_message));
}

int
printPrompt(){
	char prompt[9] = "myshell> ";	
	write(STDOUT_FILENO, prompt, strlen(prompt));
	return 0;
}

int
count(char *string, char delim){
	
	int count = 0;
	int i = 0;
	for(; i < strlen(string); i++){
		if(*(string + i) == delim){
			count++;
		}
	}

	return count;
}

int
toExit(char *word){

	
	if(strlen(word) != 4){
		return false;
	}
	
	char exiting[] = "exit";


	if(strcmp(word,exiting) != 0){
		return false;
	}
	else{
		return true;
	}
	
}
int split(char *string, char *delim, char **tokens)
{
    char *saveptr_string;
    char *token = string;  
    int iter = 0;
	        
    while (token != NULL) {
        if (iter == 1){
        	string = NULL;
	}

		
	token = strtok_r(string, delim, &saveptr_string);
//	printf("token is: \"%s\"\n", token);
	if (token != NULL)
	    tokens[iter] = token;
	else
	    tokens[iter] = NULL;		       
	
	iter++;
    }
    return iter - 1;
}

int emptyLine(char *line){
	if(line == NULL){
		return -1;
	}

	int i = 0;
	for(; i < strlen(line); i++){
		if(!isspace(line[i])){
			return 0;
		}
	}
	return 1;
}

char *trim(char *string)
{
	if (string == NULL){
		return string;
	}	

	size_t size = strlen(string);
	char *end;

	if (size == 0){
		return string;
	}

	end = string + size - 1;
	while (end >= string && isspace(*end)){
		end--;
	}	

	*(end + 1) = '\0';

	while (*string && isspace(*string)){
		string++;
	}	

	return string;
}

int
run_cd(char *path){
	if(path == NULL){
		if(chdir(getenv("HOME")) == -1){
			print_error();		
		}
		return 0;
	}
	else{
		if(chdir(path) == -1){
			print_error();
			return 0;
		}
	}
//	printf("current working dir: %s\n", run_pwd());
	return 0;
}

int run_pwd(int redirection, char *redirArguments, int argCount){
	char currentDir[PWD_BUFFER_SIZE];
	char newline[1];
	newline[0] = '\n';


	if(redirection && argCount != 2){
		print_error();
		return -1;
	}
	else if(!redirection && argCount != ){
		print_error();
		return -1;
	}

	if(getcwd(currentDir,(size_t) PWD_BUFFER_SIZE) != NULL){
		if(redirection){
			
			if(write(open(trim(redirArguments[1]), O_WRONLY | O_CREAT, S_IRWXU), currentDir, strlen(currentDir)) == -1){
				print_error(); 
			}
			write(open(trim(redirArguments[1]),O_WRONLY | O_APPEND, S_IRWXU), newline, 1); 
			return 0;
		}
		else {
			if(write(STDOUT_FILENO, currentDir, strlen(currentDir)) == -1){
				print_error();
			//	printf("Error while trying to print out the current directory");
				return -1;
			}
		}
	}
	write(STDOUT_FILENO, newline, 1);
	return 0;
}

int
main(int argc, char *argv[]){

	char line[MAX_LINE_SIZE];
	char *arguments[MAX_ARGS];
	FILE *input;
	int cont = true;
	char *cmd_line;
	int redirection;
	char *redirArguments[3];
	int rc;
	int otherChild;
	int argCount;
	int batch;
	int gzip;
	

	
	if(argc == 1){
		batch = false;
		input = stdin;
	}
	else if(argc == 2){
		batch = true;
		input = fopen(argv[1], "r");
		
		if(input == NULL){
			print_error();
			//printf("Error while trying to open batch file\n");
			exit(1);
		}
	}
	else{
		print_error();
		printf("Too many command line arguments");
		exit(0);
	}


	while(true){
		
		if(!batch){
			printPrompt();
		}
		cmd_line = fgets(line, MAX_LINE_SIZE, input);
	
	
		if(batch){

			if(cmd_line == NULL){
				break;
			}
			else if(emptyLine(cmd_line)){
				write(STDOUT_FILENO, cmd_line, strlen(cmd_line));
				continue;
			}
			else{
				write(STDOUT_FILENO, cmd_line, strlen(cmd_line));
			}	
		}

		argCount = split(cmd_line, "\n;", arguments);


		char *command[argCount];
		command[1] = NULL;	

		//int i = 0;
		//for(; i < argCount; i++){
		//	printf("%s\n", arguments[i]);								
		//}
		
		//printf("argCount = %d\n", argCount);	

		int k = 0;
		for(; k <  argCount; k++){
	
			redirection = false;
			gzip = false;	

		//	printf("arguments[%d] before manipulation: %s\n", k, arguments[k]);
		//	printf("count of redirection: %d\n", count(arguments[k], '>'));

			if(count(arguments[k], '>') > 0){
				redirection = true;
			}
			if(count(arguments[k], ':') > 0){
				gzip = true;
			}

			if(redirection == true && gzip == true){
				print_error();
				printf("Can't redir and gzip in same command.\n");
				break;
			}

			if(redirection){
				split(arguments[k], ">", redirArguments);
			}

			else if(gzip){
				split(arguments[k], ":", redirArguments);
			}

			else {
				split(trim(arguments[k]), " ", redirArguments);
			}
						
		//	printf("arguments[%d]: %s\n", k, arguments[k]);
		//	printf("count of redirection: %d\n", count(arguments[k], '>'));
		//	printf("redirection = %d\n", redirection);
		//	printf("gzip = %d\n", gzip);

			
			if(strcmp(trim(redirArguments[0]), "cd") == 0){			
				run_cd(redirArguments[1]);
			}
			else if(strcmp(trim(redirArguments[0]), "pwd") == 0){
				run_pwd(redirection, redirArguments);
			}
			else{
				if(redirection){
					rc = fork();
					if(rc == 0){
						command[0] = trim(redirArguments[0]);
									
						close(STDOUT_FILENO);
						if(open(trim(redirArguments[1]), O_WRONLY | O_CREAT, S_IRWXU) == -1){
							print_error();
							printf("Print the error message NOW!");					
						}
						//printf("should see this in the new file");
						//printf("command: \"%s\"\n", command[0]);
						execvp(command[0], command);
						print_error();
						printf("Error: child process returned.");
						exit(1);
						}
						else{
							wait(NULL);
						}
				}
				else if(gzip){
					
					int firstChild;
					firstChild = fork();
					if(firstChild == 0){
						int fd[2];
						if(pipe(fd) < 0){
							print_error();
						}
																
						otherChild = fork();
						if(otherChild == 0){
							close(fd[1]);
							close(STDOUT_FILENO);
							if(open(trim(redirArguments[1]), O_WRONLY | O_CREAT, S_IRWXU) == -1){
								print_error();
							}
							if(fd[0] != STDIN_FILENO){
								
								if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO){
									printf("fd[0] didnt get set to STDIN");
								}
								close(fd[0]);
							}
							else{
								printf("fd[0] originally == STDIN");
							}

							char *gzipArgs[2];
							gzipArgs[0] = "gzip";
							gzipArgs[1] = NULL;
						
							execvp(gzipArgs[0], gzipArgs);
							print_error();
							exit(1);
							
						}
						else{
							close(fd[0]);

							if(dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO){
							//	printf("fd[1] is not set to STDOUT");
								print_error();
							}

							command[0] = trim(redirArguments[0]);
							execvp(command[0], command);
							print_error();
							exit(1);
						}
					}
					else{
						wait(NULL);
					}
				}
				else{
					command[0] = trim(redirArguments[0]);
			
					if(strcmp(command[0], "exit") == 0){
						exit(0);
					}
					if(strcmp(command[0], "cat") == 0){
						command[1] = redirArguments[1];
						command[2] = NULL;
						rc = fork();										
					}

				
					//printf("Current argument: \"%s\"\n", command[0]);
					rc = fork();
					if(rc == 0){
						
						execvp(command[0], command);
						print_error();
						printf("Error: child process returned\n");
					}
					else{
						int wc = wait(NULL);
					}
					
				}
			}			
		}


	}

	return 0;
}
