#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;
}

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
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;
}


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,int gzip, char *redirArguments, int argCount){
	char currentDir[PWD_BUFFER_SIZE];
	char newline[1];
	newline[0] = '\n';

	//printf("argCount = %d\n", argCount);
	//printf("redirection = %d\n", redirection);

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

	if(getcwd(currentDir,(size_t) PWD_BUFFER_SIZE) != NULL){
		//printf("got the path");
		if(redirection){
			//printf("In the redirection pwd");	
			if(write(open(redirArguments[1], O_WRONLY | O_CREAT, S_IRWXU), currentDir, strlen(currentDir)) == -1){
				print_error(); 
			}
			write(open(redirArguments[1],O_WRONLY | O_APPEND, S_IRWXU), newline, 1); 
			return 0;
		}
		else if(gzip){
			
			int firstChild;
			firstChild = fork();
			if(firstChild == 0){
				int fd[2];
				if(pipe(fd) < 0){
					print_error();
				}

			int otherChild = fork();
				if(otherChild == 0){
					close(fd[1]);
					close(STDOUT_FILENO);
					if(open(redirArguments[1], O_WRONLY | O_CREAT, S_IRWXU) == -1){
						print_error();
					}
					if(fd[0] != STDIN_FILENO){
						if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO){
						}
						close(fd[0]);
					}

					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){
						print_error();
					}
					if(write(STDOUT_FILENO, currentDir, strlen(currentDir)) == -1){
						print_error();
					}
				}
			}
			else{
				wait(NULL);
			}
				
		}
		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;
	int numArgs;		

	
	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 if(strlen(cmd_line) > 512){
				write(STDOUT_FILENO, cmd_line, strlen(cmd_line));
				print_error();
				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){
				numArgs = split(arguments[k], ">", redirArguments);
			}

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

			else {
				numArgs = split(trim(arguments[k]), " ", redirArguments);
			}

			int k = 0;
			for(; k < numArgs; k++){
				redirArguments[k] = trim(redirArguments[k]);
			}

	//		k = 0;
	//		for(; k < numArgs+1; k++){
	//			printf("redirArguments[%d] = %s\n",k, redirArguments[k]); 
	//		}
						
		//	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(redirArguments[0], "cd") == 0){			
				run_cd(redirArguments[1]);
			}
			else if(strcmp(redirArguments[0], "pwd") == 0){
				run_pwd(redirection,gzip, redirArguments, numArgs);
			}
			else{
				if(redirection){
					char *multiArg[512];	
				 	if(count(redirArguments[0], ' ') > 0){
						split(redirArguments[0], " ", multiArg);
					}
					else{
						multiArg[0] = redirArguments[0];
						multiArg[1] = NULL;
					}
					
					//int r = 0;
					//for(;multiArg[r] != NULL ; r++){
					//	printf("multiArg[%d] = %s\n",r, multiArg[r]);
					//}
					//printf("multiArg[%d] = %s\n", r, multiArg[r]);

					if(numArgs == 2){
						
						if(count(redirArguments[1], ' ') > 0){
							print_error();
							break;
						}

					//	if(multiArg[0] == NULL){
					//		print_error();
					//		break;
					//	}
	
						rc = fork();
						if(rc == 0){
							command[0] = redirArguments[0];
							command[1] = NULL;

							if(multiArg[0] == NULL){
								print_error();
								break;
							}

					
							close(STDOUT_FILENO);
							if(open(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(multiArg[0], multiArg);
							print_error();
							printf("Error: child process returned.");
							exit(1);
							}
							else{
								wait(NULL);
							}
					}
					else{
						print_error();						
					}
				}
				else if(gzip){
					char *multiArg[512];
					if(count(redirArguments[0], ' ') > 0){
						split(redirArguments[0], " ", multiArg);
					}
					else{
						multiArg[0] = redirArguments[0];
						multiArg[1] = NULL;
					}
					

					if(numArgs == 2){

						if(count(redirArguments[1], ' ') > 0){
							print_error();
							break;
						}

						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(multiArg[0], multiArg);
								print_error();
								exit(1);
							}
						}
						else{
							wait(NULL);
						}
					}
					else{
						print_error();
					}
								
				}
				else{
				
					
					//k = 0;
					//for(; k < numArgs+1; k++){
				//		printf("redirArguments[%d]: %s\n", k, redirArguments[k]);
				//	}
						


					if(strcmp(redirArguments[0], "exit") == 0){
						
						exit(0);
					}

					if(strcmp(redirArguments[0], "cat") == 0 || strcmp(redirArguments[0], "echo") == 0){
					//	printf("no segment fault");		
						rc = fork();
						if(rc == 0){
							execvp(redirArguments[0], redirArguments);
							print_error();
							printf("Child process returned");
						}
						else{
							wait(NULL);
						}
																		
					}

					else{	
						//printf("No seg fault yet\n");
						rc = fork();
						if(rc == 0){
						
							execvp(redirArguments[0], redirArguments);
							print_error();
							printf("Error: child process returned\n");
						}
						else{
							wait(NULL);
						}
					}
				}
			}			
		}


	}

	return 0;
}
