#include <iostream>
#include <string.h>
#include <windows.h>
#include <fstream>
#include <io.h>   // For access().
#include <sys/types.h>  // For stat().
#include <sys/stat.h>   // For stat().


#include "fileIO.h"

// Global Constants
bool DEBUG_MODE = false;

// Global Variables

double returnFileSize(FILE *fileHandle) {
	double start_location = ftell(fileHandle);
	if (fileHandle == NULL)
		return -1;
	fseek(fileHandle, 0, SEEK_END);
	double file_size = ftell(fileHandle);
	fseek(fileHandle, start_location, SEEK_SET);
	return file_size;
}

/*-------------------------------------*/
// Write Binary File Function          //
/*-------------------------------------*/
bool writeBinaryFile(string filePath, string fileContents) {
	double fileSize = fileContents.length();
	char *outputBuffer;
	
	// Open File For Writing and Get File Handle
	FILE * fileHandle;
	fileHandle = fopen(filePath.c_str(), "wb");
	
	// If File Opened Properly Write Data To It
	if (fileHandle != NULL) {
		// Create The Output Buffer
		outputBuffer = new char [int(fileSize)];
		
		// Copy File Contents To Output Buffer
		for (int counter = 0; counter < fileSize; counter++) {
			outputBuffer[counter] = fileContents[counter];
		}
		
		// Write Output Buffer To File
		fwrite(outputBuffer, 1, fileSize, fileHandle);
		
		// Delete The Output Buffer From Memory
		delete[] outputBuffer;
	} else {
		fclose(fileHandle);
		return false;
	}
	
	// Close File
	fclose(fileHandle);
	
	return true;
}


/*-------------------------------------*/
// Read Binary File Function           //
/*-------------------------------------*/
bool readBinaryFile(string filePath, string &fileContents) {
	double fileSize;
	char *inputBuffer;
	fileContents = "";
	
	FILE *fileHandle;
	fileHandle = fopen(filePath.c_str(), "rb");
	if (fileHandle != NULL) {
		// Get The File Size
		fileSize = returnFileSize(fileHandle);
		inputBuffer = new char [int(fileSize)];
		
		fread(inputBuffer, 1, fileSize, fileHandle);
		
		// Copy Buffer To String
		for (int counter = 0; counter < fileSize; counter++) {
			fileContents += inputBuffer[counter];
		}
		
		delete[] inputBuffer;
		
	} else {
		fclose(fileHandle);
		return false;
	}
	fclose(fileHandle);
	return true;
	
}

bool writeTextFile(string filePath, string fileContents) {
	// This Function Takes A Text String and Writes It To A File
	
	ofstream fileHandle(filePath.c_str());
	if (fileHandle.is_open() == true) {
		fileHandle << fileContents.c_str();
		fileHandle.close();
	} else {
		return false;
	}
	return true;

}

bool readTextFile(string filePath, string &fileContents) {
	// This Function Reads A File Into A String
	string line = "";
	fileContents = "";

	ifstream fileHandle(filePath.c_str());
	if (fileHandle.is_open() == true) {
		while (!fileHandle.eof()) {
			getline(fileHandle, line);
			fileContents += line;
			if (!fileHandle.eof())
				fileContents += "\n";
		}
		fileHandle.close();
	} else {
		fileContents = "";
		return false;
	}
	
	return true;
}

bool isDirectory(string path) {
    if ( access(path.c_str(), 0 ) == 0 ) {
        struct stat status;
        stat( path.c_str(), &status );

        if ( status.st_mode & S_IFDIR ) {
        	if (DEBUG_MODE)
        		cout << "DEBUG STATEMENT: The directory exists." << endl;
            return true;
        } else {
        	if (DEBUG_MODE)
        		cout << "DEBUG STATEMENT: The path you entered is a file." << endl;
        }
    } else {
    	if (DEBUG_MODE)
    		cout << "DEBUG STATEMENT: Path doesn't exist." << endl;
    }

    return false;
}

// Mode is set to 0 for folders only and 1 for files too
bool mapFilesAndFolders(string path, int mode, string &dataContents) {
	dataContents += "<ul>";
	// Variable Declarations
		WIN32_FIND_DATA FindFileData;
		HANDLE hFind = INVALID_HANDLE_VALUE;
		
		int numFiles = 0;
		int numFolders = 0;
		
		char directoryPath[MAX_PATH];
		
		for(int counter = 0; counter < path.length(); counter++) {
			directoryPath[counter] = path.c_str()[counter];
		}
		directoryPath[path.length()] = '\0'; // Null Terminator After File Path
		
		if (DEBUG_MODE)
			cout << "DEBUG STATEMENT: The Current Path Is: " << directoryPath << endl;
		
		WCHAR wdPath[MAX_PATH];
		strncat(directoryPath, "\\*", 3);
		
		for (int counter = 0; counter < MAX_PATH; counter++) {
			wdPath[counter] = directoryPath[counter];
		}
		
		hFind = FindFirstFile(wdPath, &FindFileData);
		
		if (hFind == INVALID_HANDLE_VALUE)
			cout << "ERROR: Invalid File Path In Count Num Files and Folders Function" << endl;
		
		string tempPath = "";
		while (FindNextFile(hFind, &FindFileData) != 0) {
			tempPath = "";
			for (int counter = 0; counter < MAX_PATH; counter++) {
				if (char(FindFileData.cFileName[counter]) == '\0')
					break;
				else {
					//cout << char(FindFileData.cFileName[counter]);
					tempPath += char(FindFileData.cFileName[counter]);
				}
			}
			//cout << endl;
			string tempPath2 = tempPath;
			tempPath = path + "\\" + tempPath;
			
			// Verify The File Is Not Telling Itself To Go Up 1 Level
			if (isDirectory(tempPath) == true && tempPath2 != "..") {
				numFolders++;
				int tempNumA = 0;
				int tempNumB = 0;
				dataContents += "<b><li>" + tempPath2 + "</li></b>";
				mapFilesAndFolders(tempPath, mode, dataContents);
				numFiles += tempNumA;
				numFolders += tempNumB;
				
			} else {
				if (tempPath2 != "..") {
					
					if (mode == 1) {
						dataContents += "<li>" + tempPath2 + "</li>";
					}
					numFiles++;
				}
			}
		}
		
		FindClose(hFind);
		if (DEBUG_MODE == true) {
			cout << "The Current Number Of Files Is: " << numFiles << endl;
			cout << "The Current Number Of Folders Is: " << numFolders << endl;
		}
		
		dataContents += "</ul>";
		
		return true;
	
	
}


bool getNumFilesAndFolders(string path, int &numFilesReturned, int &numFoldersReturned) {
	// Variable Declarations
	WIN32_FIND_DATA FindFileData;
	HANDLE hFind = INVALID_HANDLE_VALUE;
	
	int numFiles = 0;
	int numFolders = 0;
	
	char directoryPath[MAX_PATH];
	
	for(int counter = 0; counter < path.length(); counter++) {
		directoryPath[counter] = path.c_str()[counter];
	}
	directoryPath[path.length()] = '\0'; // Null Terminator After File Path
	
	if (DEBUG_MODE)
		cout << "DEBUG STATEMENT: The Current Path Is: " << directoryPath << endl;
	
	WCHAR wdPath[MAX_PATH];
	strncat(directoryPath, "\\*", 3);
	
	for (int counter = 0; counter < MAX_PATH; counter++) {
		wdPath[counter] = directoryPath[counter];
	}
	
	hFind = FindFirstFile(wdPath, &FindFileData);
	
	if (hFind == INVALID_HANDLE_VALUE)
		cout << "ERROR: Invalid File Path In Count Num Files and Folders Function" << endl;
	
	string tempPath = "";
	while (FindNextFile(hFind, &FindFileData) != 0) {
		tempPath = "";
		for (int counter = 0; counter < MAX_PATH; counter++) {
			if (char(FindFileData.cFileName[counter]) == '\0')
				break;
			else {
				//cout << char(FindFileData.cFileName[counter]);
				tempPath += char(FindFileData.cFileName[counter]);
			}
		}
		//cout << endl;
		string tempPath2 = tempPath;
		tempPath = path + "\\" + tempPath;
		
		// Verify The File Is Not Telling Itself To Go Up 1 Level
		if (isDirectory(tempPath) == true && tempPath2 != "..") {
			numFolders++;
			int tempNumA = 0;
			int tempNumB = 0;
			getNumFilesAndFolders(tempPath, tempNumA, tempNumB);
			numFiles += tempNumA;
			numFolders += tempNumB;
		} else {
			if (tempPath2 != "..") {
				numFiles++;
			}
		}
	}
	
	FindClose(hFind);
	if (DEBUG_MODE == true) {
		cout << "The Current Number Of Files Is: " << numFiles << endl;
		cout << "The Current Number Of Folders Is: " << numFolders << endl;
	}
	numFilesReturned = numFiles;
	numFoldersReturned = numFolders;
	
	return true;
	
}

int countNumFiles(string path) {
	// Variable Declarations
	WIN32_FIND_DATA FindFileData;
	HANDLE hFind = INVALID_HANDLE_VALUE;
	int numFiles = 0;
	int lastLoc = 0;
	
	char directoryPath[MAX_PATH];
	
	for (int counter = 0; counter < path.length(); counter++) {
		directoryPath[counter] = path.c_str()[counter];
		lastLoc = counter;
	}
	lastLoc++;
	directoryPath[lastLoc] = '\0';
	
	if (DEBUG_MODE)
		cout << "DEBUG STATEMENT: The Current Path Is: " << directoryPath << endl;
	WCHAR wdPath[MAX_PATH];
	
	strncat(directoryPath, "\\*", 3);
	
	for (int counter = 0; counter < MAX_PATH; counter++) {
		wdPath[counter] = directoryPath[counter];
	}
	
	hFind = FindFirstFile(wdPath, &FindFileData);
	
	if (hFind == INVALID_HANDLE_VALUE) {
		cout << "Error: Invalid File Path" << endl;
	}
	
	//cout << FindFileData.cFileName << endl;
	string tempPath = "";
	while (FindNextFile(hFind, &FindFileData) != 0) {
		tempPath = "";
		for (int counter = 0; counter < MAX_PATH; counter++) {
			if (char(FindFileData.cFileName[counter]) == '\0')
				break;
			else {
				//cout << char(FindFileData.cFileName[counter]);
				tempPath += char(FindFileData.cFileName[counter]);
			}
		}
		//cout << endl;
		string tempPath2 = tempPath;
		tempPath = path + "\\" + tempPath;
		
		// Verify The File Is Not Telling Itself To Go Up 1 Level
		if (isDirectory(tempPath) == true && tempPath2 != "..") {
			numFiles += countNumFiles(tempPath);
		} else {
			if (tempPath2 != "..") {
				numFiles++;
			}
		}
	}
	
	FindClose(hFind);

	return numFiles;
}

