
/*
    _______    ________    _____      ________    __         _______    __     __
   |__   __|  |  ______|  |  _   \   |  ______|  |  |       |  _____|   \  \  /  /
      | |     | |___      | |  \  |  | |___      |  |       | |__        \  \/  /
      | |     | ____|     | |   | |  | ____|     |  |       | ___|       /  /\  \
      | |     | |______   | |_ /  |  | |         |  |____   | |_____    /  /  \  \
      |_|     |________|  |_____ /   |_|         |_______|  |_______|  /__/    \__\

*/

/* TedFlex - A File Explorer, File Viewer, and Text Editor.

A combination of two of my programs: Ted (Text EDitor) and Flex (FiLe EXplorer). 

This is an ncurses tool allows you to browse your files and view/edit them in the same window.

*/


#include "texted.h"
#include "fileman.h"

#include <locale.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define MAX_LINE_LENGTH 142857 		//This is the maximum length of a line that we can read from a file.
#define START_WIN 5 			//This is the position on the terminal window from where we will start drawing our ncurses window.



int main(int argc,char** argv) {
	int xLen,yLen,mainX,mainY;	//values for the x-length and y-length of stdscr (i.e. the terminal window), and the 'main window' inside it.
	int fileCountNum;    		//Number of files in a given directory
	int highlighted=0;    		//Index of the highlighted file
	int choice;    			//stores the user's key press
	int fileRow;			//Line number that the user is on when they choose to open a file
	int fileCol;    		//ISN'T USED ELSEWHERE IN THE PROGRAM - SHOULD BE REMOVED
	int numNewLines;		//Number of new-line characters read in a file
	int rowCounter;    		//Number of rows to skip reading in a file, when the user presses Page Down
	bool eofFound;    		//Self-explanatory; is EOF found?
	int  colorScheme;    		//stores the value of the color scheme - changes when user hits Ctrl-D
//	int colCounter;    		//ISN'T USED ELSEWHERE IN THE PROGRAM - SHOULD BE REMOVED
	char tempChar;			//Holds the 'current' character while reading from a file.
	int numCharsToPrint;		//Weird naming - should be renamed to 'numLinesToPrint' - Number of lines to print in the file explorer.
	int numDowns=0;			//Stores the number of times the user has pressed the Down Arrow / Page Down
	bool hasExited;			//Has the user exited from TextView mode?
	bool hasChosen;			//Has the user chosen to edit or view a file?
	char editOrView;		//Did the user choose to edit or view a file?
	char line[MAX_LINE_LENGTH];	//Holds a line in a text file when the user chooses to view a file
	FILE* ptrFile;    		//Used to open a file to read from


	bool hasCopied;
	FILE* tempFileCopy;


	char* curDir;

	char* fileCopied;

	setlocale(LC_CTYPE,""); 

	initscr();    			//NCURSES - Initializes screen to begin doing stuff in ncurses
	getmaxyx(stdscr,yLen,xLen);	/*NCURSES - Gets the maximum x- and y-length of stdscr, which represents the terminal/TTY window 
	that is running the program. Used to make calculations for the 'main' window based on the size. */

	curs_set(0);    		//NCURSES - Makes the cursor (the white rectangle) invisible.
	noecho();    			//NCURSES - prevents echoing of user choices.
	cbreak();			//NCURSES - Similar to Raw Mode, key presses are registered immediately after they are typed.
	if (has_colors() == TRUE) {
		start_color();		//NCURSES - Initialize color - TODO - Disable colors if color support is not found.
	}

/*	NCURSES - The next block of code creates colors and color pairs for our window. */

	init_color(COLOR_CYAN,500,500,500);
	init_color(COLOR_MAGENTA,150,150,150);
	init_color(COLOR_YELLOW,650,650,650);
	init_color(COLOR_GREEN,40,291,177);

	init_pair(0,COLOR_WHITE,COLOR_BLACK);
	init_pair(1,COLOR_BLACK,COLOR_CYAN);
	init_pair(2,COLOR_WHITE,COLOR_MAGENTA);
	init_pair(4,COLOR_GREEN,COLOR_CYAN);
	init_pair(6,COLOR_WHITE,COLOR_BLUE);
	init_pair(8,COLOR_BLACK,COLOR_CYAN);
	init_pair(10,COLOR_BLACK,COLOR_BLACK);
	init_pair(11,COLOR_BLACK,COLOR_YELLOW);
	init_pair(12,COLOR_WHITE,COLOR_BLACK);

/*	BLOCK ENDS HERE */

	colorScheme=1;		// Default value for the color scheme.

	rowCounter = 1;		// Set this to zero, because we don't want to skip anything
//	colCounter = 1;

	WINDOW* mainwin = newwin(yLen-7,xLen-9,START_WIN,START_WIN);		/*NCURSES - This creates our 'main' window, that will be 
	used to display ~99% of the actual content.*/
	
	wbkgd(stdscr,COLOR_PAIR(6));	//NCURSES - Sets the default foreground and background color for stdscr.
	wbkgd(mainwin,COLOR_PAIR(8));	//NCURSES - Sets the default foreground and background color for mainwin.
	keypad(mainwin,true);		//NCURSES - Allows the use of 'keypad' values: Arrow Keys, Function Keys, etc.
	getmaxyx(mainwin,mainY,mainX);	//NCURSES - Gets the maximum x- and y-values of the mainwin, and stores them in the appropriate variables.

	wattron(stdscr,COLOR_PAIR(10));	//NCURSES - Sets the color for the 'shadows' in the following block of code.

/*	NCURSES - The following block of code adds 'shadows' to the mainwin. */

	for (int i=START_WIN+1;i<mainY+START_WIN;i++) {
		mvwaddch(stdscr,i,mainX+START_WIN,32);	
		mvwaddch(stdscr,i,mainX+START_WIN+1,32);
	}
	mvwaddch(stdscr,mainY+START_WIN,mainX+START_WIN+1,32); //Fills in an empty 'shadow' spot that was missed by the previous for loop.

	for (int i=START_WIN+1;i<=mainX+START_WIN;i++) {
		mvwaddch(stdscr,mainY+START_WIN,i,32);
	}
	wattroff(stdscr,COLOR_PAIR(10));

/*	BLOCK ENDS HERE */

	char* dir = malloc(sizeof(char) * FILE_ARR_SIZE);	//Char array for storing the name of the current file/directory
	char* tempDir = malloc(sizeof(char) * FILE_ARR_SIZE);		//Char array for temporarily storing the name of the highlighted file/directory


	if (argv[1] == NULL) {		//If no argument is provided for file path...
		dir[0]='/';		//Use root as a default file path
		dir[1]='\0';
	} else {			//If there is a default path provided...
		dir = argv[1];		//Use that as the default path
		if (dir[0] == '.' || dir[0] != '/') {


//			DONE - ACCOUNT FOR THE POSSIBILITY THAT USER DIDN'T USE ./ BEFORE FILE NAME


			curDir = malloc(sizeof(char) * FILE_ARR_SIZE);
			getcwd(curDir,FILE_ARR_SIZE);
			int i=strlen(curDir);
			int a;
			if (dir[0] != '.') {
				a = 0;
				*(curDir+i)  = '/';
				i++;
			} else {
				a = 1;
			}
			while (*(dir+a) != '\0') {
//				strcat(curDir,*(dir+i));
				*(curDir+i) = *(dir+a);
				i++;
				a++;
			}
			for (int i=0;i<FILE_ARR_SIZE;i++) {
				*(dir+i) = '\0';
			}
			strcpy(dir,curDir);
		}
			

		if (!(isFile(dir))) {
			addSlash(dir);		//If it doesn't have a forward slash at the end, add one
		}
	}

	fileCountNum = findFileCount(dir);	//Set the number of files in the directory using a function

	char** fileList = calloc(FILE_ARR_SIZE,1000);	//Allocating memory for the fileList: I used calloc because the list is a double-pointer
		for (int i=0;i<FILE_ARR_SIZE;i++) {	//Initializing the values of the list
			fileList[i] = NULL;
		}

	/* 
	Adds <mainY+5> files in the directory 'dir' to the fileList. The reason behind the limit
	in the number of files added is that, with large directories (e.g. /lib), I encountered a 
	weird bug where, after a certain number of files, the list corrupts itself. My solution 
	was to add a limited number of files at a given time, and change those files depending on 
	where the user is in the file list.
	*/

	
	if (!(isFile(dir))) {
		addFiles(dir,fileList,0,mainY+5);
	}

	int firstItemIndex = 0;		//Initializes the index of the first item in the list

	/* This block prints some helpful information regarding the controls to navigte within the program */

	wmove(stdscr,1,0);
	wclrtoeol(stdscr);
	mvwaddstr(stdscr,1,17,"<Up Arrow> - Move Up | <Down Arrow> - Move Down | <Left Arrow> - Go Back | <Right Arrow>/Enter - Enter Directory/Open File\n");
	wmove(stdscr,2,0);
	wclrtoeol(stdscr);
	mvwaddstr(stdscr,2,40,"<Page Up> - Scroll Up | <Page Down> - Scroll Down\n");
	
	/* BlOCK ENDS HERE */

	while (true) {


//		getmaxyx(stdscr,yLen,xLen);
//		getmaxyx(mainwin,mainY,mainX);

		wmove(stdscr,3,0);
		wclrtoeol(stdscr);

		if (highlighted == firstItemIndex+mainY) {
			firstItemIndex++;
			refresh();
			wrefresh(mainwin);
		}
		


		if (isFile(dir)) {
			if (hasChosen == false) {
				mvwaddstr(mainwin,5,5,"Do you want to (e)dit or (v)iew this file? ");
				editOrView='$';
				if (!(fileExists(dir))) {
	                                while (editOrView != 'e') {
        	                                editOrView = wgetch(mainwin);
                	                }
				}
				else {
					while (editOrView != 'e' && editOrView != 'v') {
						editOrView = wgetch(mainwin);
					}
				}
				if (editOrView == 'e') {
					curs_set(2);
					run(dir);
					curs_set(0);				
					wmove(stdscr,1,0);
					wclrtoeol(stdscr);
                                        wmove(stdscr,2,0);
                                        wclrtoeol(stdscr);					
					hasExited = true;
				}
			}	
			if (editOrView == 'v' /*&& hasChosen == false*/) {
				wclear(mainwin);
				refresh();
				wrefresh(mainwin);
				mvwprintw(stdscr,3,0,"Line %d",rowCounter);
	
				ptrFile = fopen(dir,"r");
				rewind(ptrFile);
				fileRow=1;
				for (int i=0;i<(rowCounter-1);i++) {
					fgets(line,MAX_LINE_LENGTH,ptrFile);
				}
	
				while(fgets(line, MAX_LINE_LENGTH, ptrFile)) {
					mvwprintw(mainwin,fileRow,1,"%s",line);
					fileRow++;
					}
					fclose(ptrFile);
					refresh();
					wrefresh(mainwin);
					hasChosen=true;		
			}
		} else {

			if (mainY < fileCountNum) {
				if (mainY < fileCountNum-firstItemIndex){
					numCharsToPrint = mainY;
				} else {
					numCharsToPrint = mainY-1;
				}
			} else {
				numCharsToPrint = fileCountNum;
			}
			
			
			


			for (int i=0,a=0;i<numCharsToPrint;i++,a++) {

					for (int i=0;i<FILE_ARR_SIZE;i++){
						*(tempDir+i) = '\0';
					}


				if (i == highlighted) {
					wattron(mainwin,A_REVERSE);
				}
				strcpy(tempDir,dir);
				strcat(tempDir, (char *)(*(fileList+i)));

				strcat(tempDir,"\0");

				if (isFile(tempDir)) {
					wattron(mainwin,COLOR_PAIR(4));
				}

				

				mvwprintw(mainwin,a+1,5,"%s",*(i+fileList));
				refresh();
				wrefresh(mainwin);
				wattroff(mainwin,COLOR_PAIR(4));
	 			wattroff(mainwin,A_REVERSE);
	 			
			}
			refresh();
			wrefresh(mainwin);
		}

		mvwprintw(stdscr,0,1,"%s\n",dir);
		refresh();
		wrefresh(mainwin);
		//break;
		
		if (hasExited) {
				removeDirFromPath(dir);
				highlighted=0;
                                move(0,0);
				wclrtoeol(stdscr);
				refresh();
				wrefresh(mainwin);
                                mvwprintw(stdscr,0,1,"%s",dir);
                                fileCountNum = findFileCount(dir);
                                addFiles(dir,(char **)fileList,0,mainY+5);
				firstItemIndex=0;
                                highlighted=0;
                                wclear(mainwin);
                                refresh();
                                wrefresh(mainwin);
                                
                                hasChosen=false;

				rowCounter=1;
				hasExited=false;

				wmove(stdscr,1,0);
				wclrtoeol(stdscr);
				mvwaddstr(stdscr,1,17,"<Up Arrow> - Move Up | <Down Arrow> - Move Down | <Left Arrow> - Go Back | <Right Arrow>/Enter - Enter Directory/Open File\n");
				wmove(stdscr,2,0);
				wclrtoeol(stdscr);
				mvwaddstr(stdscr,2,40,"<Page Up> - Scroll Up | <Page Down> - Scroll Down");


		} else {
			choice=wgetch(mainwin);
			switch (choice) {

				case (('d' & 0x1f)):
					
					if (colorScheme == 0) {
						wbkgd(stdscr,COLOR_PAIR(6));
						init_color(COLOR_GREEN,40,291,177);
						init_pair(4,COLOR_GREEN,COLOR_CYAN);
						wbkgd(mainwin,COLOR_PAIR(8));
						colorScheme++;
						refresh();
						wrefresh(mainwin);
						break;
					} else if (colorScheme == 1){
                                                wbkgd(stdscr,COLOR_PAIR(2));
						init_pair(4,COLOR_GREEN,COLOR_BLACK);
                                                wbkgd(mainwin,COLOR_PAIR(0));
                                                colorScheme++; 
                                                refresh();
                                                wrefresh(mainwin);
						break;
					} else if (colorScheme == 2) {
                                                wbkgd(stdscr,COLOR_PAIR(12));
						init_color(COLOR_GREEN,40,438,7);
						init_pair(4,COLOR_GREEN,COLOR_YELLOW);
                                                wbkgd(mainwin,COLOR_PAIR(11));
                                                colorScheme=0;
                                                refresh();
                                                wrefresh(mainwin);
                                                break;


					}
					break;


					
				case KEY_UP:
	
	                                if (isFile(dir)) {
						 if (rowCounter <= 1) {
							rowCounter = 1;
	                	                        break;
	                        	        }
	
	                                        rowCounter--;
	                                        wclear(mainwin);
	                                        refresh();
	                                        wrefresh(mainwin);
	                                        break;
	                                }




					highlighted--;
					if (highlighted == -1) {
						highlighted=0;
					}
					if (highlighted < 1) {
						if (firstItemIndex > 0) {
	                                        	firstItemIndex--;
	                                        	highlighted++;
	                                        	addFiles(dir,(char **)fileList,firstItemIndex,mainY+5);
	                                        	numDowns--;


	        	                                if (highlighted > mainY-3)
	                	                                highlighted=mainY-3;
	                        	                wclear(mainwin);
	                                        	refresh();
		                                        wrefresh(mainwin);
	        	                        }
					}
					break;
				case KEY_DOWN:
	
					if (isFile(dir)) {
						numNewLines = 0;
						eofFound = false;
						ptrFile = fopen(dir,"r");
			                        for (int i=0;i<(rowCounter-1);i++) {
			                                fgets(line,MAX_LINE_LENGTH,ptrFile);
			                        }
	
						while (true) {
							tempChar = fgetc(ptrFile);
							if (tempChar == '\n') {
								numNewLines++;
							}
							if (tempChar == EOF) {
								eofFound = true;
								break;
							}
							if (numNewLines >= (mainY-5)) {
								break;
							}
						}
						if (eofFound) {
							break;
						}
						fclose(ptrFile);
						rowCounter++;
	                                        wclear(mainwin);
	                                        refresh();
	                                        wrefresh(mainwin);
						break;
					}
					

					if (highlighted > mainY-3) {
						break;
					}
	
					if (fileCountNum < mainY-1 && highlighted+numDowns+1 >= fileCountNum) {	
						break;
					} 					


					highlighted++;
					/*
					if (highlighted+numDowns >= fileCountNum) {
						if (numCharsToPrint-1 < mainY-2) {
							highlighted = numCharsToPrint-1;
						} else {
							highlighted = mainY-2;
						}
						break;
					}
					*/



					/*

					if (highlighted > mainY-2) {
		                                if (firstItemIndex < fileCountNum-1) {
	        	                                firstItemIndex++;
							highlighted--;
							numDowns++;
	        	                                addFiles(dir,(char **)fileList,firstItemIndex,mainY+5);  
	        	         
	                                	        wclear(mainwin);
		                                        refresh();
		                                        wrefresh(mainwin);
	        	                        }
					}




					*/
					break;
	
				case KEY_NPAGE:
					if (isFile(dir)) {
						numNewLines = 0;
						eofFound = false;
						ptrFile = fopen(dir,"r");
			                        for (int i=0;i<(rowCounter-1);i++) {
			                                fgets(line,MAX_LINE_LENGTH,ptrFile);
			                        }
	
						while (true) {
							tempChar = fgetc(ptrFile);
							if (tempChar == '\n') {
								numNewLines++;
							}
							if (tempChar == EOF) {
								eofFound = true;
								break;
							}
							if (numNewLines >= (mainY-5)) {
								break;
							}
						}
						if (eofFound) {
							break;
						}
						fclose(ptrFile);
						rowCounter += mainY-5;
	                                        wclear(mainwin);
	                                        refresh();
	                                        wrefresh(mainwin);
						break;
					}





					if (strcmp("linux",getenv("TERM")) == 0) {
						mainY+=1;
					}

					if (fileCountNum - firstItemIndex > mainY-3) {
						firstItemIndex++;
						numDowns++;
						addFiles(dir,(char **)fileList,firstItemIndex,mainY+5);
						wclear(mainwin);
						refresh();
						wrefresh(mainwin);
					}

					if (strcmp("linux",getenv("TERM")) == 0) {
						mainY-=1;
					}

					break;
	
				case KEY_PPAGE:
	
	
	                                if (isFile(dir)) {
						 if (rowCounter-mainY+5 <= 1) {
							rowCounter = 1;
	                	                        break;
	                        	        }
	
	                                        rowCounter -= mainY+5;
	                                        wclear(mainwin);
	                                        refresh();
	                                        wrefresh(mainwin);
	                                        break;
	                                }
	
	
			                if (firstItemIndex > 0) {
						firstItemIndex--;

						addFiles(dir,(char **)fileList,firstItemIndex,mainY+5);

						if (highlighted > firstItemIndex+mainY-3)
							highlighted=firstItemIndex+mainY-3;
				                wclear(mainwin);
			                	refresh();
				                wrefresh(mainwin);
					}
					break;

/* *************************************************************************************** */
//	STUFF TO IMPLEMENT
//
//	1. Show the content of files in another ncurses window
////////////	2. Use Arrow Keys to scroll in file (line-by-line)
////////////	3. Define symbolic constants for commonly used values
/* *************************************************************************************** */



				case KEY_RIGHT:
				case 10:
					if (!(isFile(dir))) {
						strcat(dir,*(fileList+highlighted));
						if (!(isFile(dir))) {
							strcat(dir,"/");
						}
						move(0,1);
						printw("%s",dir);
						fileCountNum = findFileCount(dir);
						firstItemIndex=0;
	                       		         addFiles(dir,(char **)fileList,firstItemIndex,mainY+5);
	                       		         numDowns=0;
	
						highlighted=0;
						wclear(mainwin);
						refresh();
						wrefresh(mainwin);
					}
					break;
	
				case KEY_LEFT:
					removeDirFromPath(dir);
					highlighted=0;
	                                move(0,0);
					wclrtoeol(stdscr);
					refresh();
					wrefresh(mainwin);
	                                mvwprintw(stdscr,0,1,"%s",dir);
	                                fileCountNum = findFileCount(dir);
                           	     addFiles(dir,(char **)fileList,0,mainY+5);
					numDowns=0;
					firstItemIndex=0;
	                                highlighted=0;
	                                wclear(mainwin);
	                                refresh();
	                                wrefresh(mainwin);
	                                
	                                hasChosen=false;
	
					rowCounter=1;
	                                break;
	
				case ('n' & 0x1f):
//					WINDOW* newFileWin = newwin(6,50,mainY/2-3,mainX/2-25);
//					mvwaddstr(newFileWin,1,1,"Enter the name of the file you wish to create");
					char* newFileLoc = getSaveLoc(mainX,mainY,"$$$");
					char* newSaveLoc = malloc(sizeof(char) * (strlen(dir) + strlen(newFileLoc)) + 1);
					strcpy(newSaveLoc,dir);
//					strcat(newSaveLoc,"/");
					strcat(newSaveLoc,newFileLoc);
					curs_set(2);
					run(newSaveLoc);
					curs_set(0);
					refresh();
					wmove(mainwin,2,0);
					wclrtoeol(mainwin);
					wrefresh(mainwin);
					break;

				case ('p' & 0x1f):
					char* tempFileDir = malloc(sizeof(char) * MAX_LINE_LENGTH);
					strcpy(tempFileDir,dir);
//					strcat(tempFileDir,"/");
					strcat(tempFileDir,fileList[highlighted]);
					fileCopied = malloc(sizeof(char) * strlen(fileList[highlighted]) * 2);
					strcpy(fileCopied,fileList[highlighted]);
					ptrFile = fopen(tempFileDir,"r");
					tempFileCopy = fopen("/tmp/tedflexTempFile","w");
					char tempChar = 'a';
					while(!feof(ptrFile)) {
						fscanf(ptrFile,"%c",&tempChar);
						if (!feof(ptrFile)) {
							fprintf(tempFileCopy,"%c",tempChar);
						}
					}
					hasCopied = true;
					fclose(tempFileCopy);
					fclose(ptrFile);
//					rewind(ptrFile);
        	                        addFiles(dir,(char **)fileList,0,mainY+5);
        	                        wclear(mainwin);
	                                refresh();
                	                wrefresh(mainwin);

					break;

				case ('v' & 0x1f):
					if (hasCopied) {
	                                        char* tempFileDir = malloc(sizeof(char) * MAX_LINE_LENGTH);
	                                        strcpy(tempFileDir,dir);
						tempFileCopy = fopen("/tmp/tedflexTempFile","r");
						char* tempFileCopied = malloc((strlen(tempFileDir)+strlen(fileCopied))*2);
						strcpy(tempFileCopied,tempFileDir);
						strcat(tempFileCopied,fileCopied);
						int fd = open(tempFileCopied,O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
						if (errno != EEXIST) {
							strcat(tempFileDir,fileCopied);
							ptrFile = fopen(tempFileDir,"w");
							char tempChar = 'a';
        	                	                while(!feof(tempFileCopy)) {
	                	                                fscanf(tempFileCopy,"%c",&tempChar);
								if (!feof(tempFileCopy)) {
	                        	                        	fprintf(ptrFile,"%c",tempChar);
								}
							}
							hasCopied=0;
							free(fileCopied);
						}


                                        }
	                                fileCountNum = findFileCount(dir);
        	                        addFiles(dir,(char **)fileList,0,mainY+5);
        	                        wclear(mainwin);
	                                refresh();
                	                wrefresh(mainwin);

				
				default:
					break;
	
			}
		}
		
		if (choice == 113){
			endwin();
			break;
		}
	}


	return 0;
}









