//GREP.CPP
//COPYRIGHT EMILE J. KEITH 2014//
#include <iostream>
#include <regex>
#include <cstdlib>
#include <exception>
#include <fstream>
#include <iomanip>
#include <boost/filesystem.hpp>//filesystem header file from boost library, cross platform compatible

using namespace std; 
using namespace boost::filesystem;

int main(int argc, char *argv[])
{
	if(argc<3)
	{
		cout<<"Not enough Arguments provided\n";
	}
	else
	{
		regex rootExpression("([A-Za-z]:[\\\\/]){1,1}");//This expression states the first char in the word must be any capital letter followed by a : and then a back or forward slash
		regex expressionToMatch;
		string concatinatedPath, extension, fileToSearch, fileName, fileExtension, currentLine;
		bool iFlag=false, vFlag=false, lFlag=false, nFlag=false, cFlag=false, iterator=false, fullPathProvided=false, match=false;
		/*iFlag is ignore case
		  vFlag prints non matching
		  lFlag prints just file names instead of actual matching string
		  nFlag prints line number in input file
		  cFlag just print number of matches and not the matches*/
		unsigned short lineNumber, numberOfMatches=0;
		unsigned long currentPosition, endOfFile;
		path searchPath;//a class from the boost filesystem header
		if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='i')
			iFlag=true;
		else if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='v')//Only one flag can be provided as an argument, therefore else if works perfectly for this situation
			vFlag=true;
		else if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='l')
			lFlag=true;
		else if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='n')
			nFlag=true;
		else if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='c')
			cFlag=true;
		for(unsigned short count=argc-1;count>0;count--)
		{
			if(regex_search(string(argv[count]),rootExpression))//if the current argument matches the expression
			{
				fullPathProvided=true;
				for(unsigned short counter=count; counter<argc; counter++)
				{
					concatinatedPath+=argv[counter];//concatinate it onto the string
					if(concatinatedPath[concatinatedPath.length()-1]!='\\' && counter!=(argc-1))//And as long as the last char of the argument is not a \ and it is not the last argument
					{
						concatinatedPath+=" ";//add a space
					}
				}
				break;//break the for loop
			}
		}
		if(!fullPathProvided)//If a full search path was not provided, identified by the previous regex having no matches
			concatinatedPath=argv[argc-1];//Use the last provided argument as the concatinated path
		if((concatinatedPath.find_last_of('\\')+1==concatinatedPath.find_last_of('*'))||(concatinatedPath.find_last_of('/')+1==concatinatedPath.find_last_of('*')) || ((!fullPathProvided) && concatinatedPath.at(0)=='*' && concatinatedPath.at(1)=='.'))
		{//if the char in the path after the last \ or / is *
			iterator=true;
			extension=concatinatedPath.substr(concatinatedPath.find_last_of('.'),string::npos);//take a substring begining from the last . and assign it to the extension
		}
		else
		{
			fileToSearch=concatinatedPath.substr(concatinatedPath.find_last_of('\\')+1,string::npos);//else everything after the last \ or / specifies a specific file to search
		}
		if(iFlag)//if the -i flag is active
			expressionToMatch.assign(argv[2],regex::icase);//Assign the third argument to the regex object expressionToMatch and make it case insensitive
		else
			expressionToMatch.assign(argv[1]);//else assign the second argument to expressionToMatch
		if(fullPathProvided)
			searchPath.append(concatinatedPath);
		else
			searchPath.append(current_path().string()+"\\"+concatinatedPath);
		if(exists(searchPath) || (iterator && exists(searchPath.parent_path())))//If the path exists, or iteretor is true and the root path for the searPath variable exists
		{
			if(iterator)//if iteratin
			{
				directory_iterator end_iter;//directory_iterator class, when initialized without a path, it is default set to indicate the end of a directory flag
				if(searchPath.parent_path()==current_path())//If the searPath root is the same as current directoyr
				{
					for(directory_iterator dir_iter(searchPath.parent_path()); dir_iter!=end_iter; ++dir_iter)//this time the directory_iterator class instance was intitialized with a path vairiable, and is set to the beginning of the directory(LAYMAN'S TERMS)
					{
						path newPath=dir_iter->path();//new path currently holds the value of the path for the item dir_iter is currently positioned on
						fileName=newPath.filename().string();//extracts the filename from the path, casts to a string then returns it
						fileExtension=newPath.extension().string();
						if(is_regular_file(newPath))
						{
							if(fileExtension==extension)
							{
								ifstream file((fileName+fileExtension));
								try
								{
									if(file.bad())
										throw runtime_error("could not open "+fileName+fileExtension+" for reading!\n");
									else
									{
										file.seekg(0, ios::end);
										endOfFile=file.tellg();//find out the position of the last charachter in file
										file.seekg(0, ios::beg);//set reading pointer back to start of file
									}
								}
								catch(runtime_error &ex)
								{
									cerr<<ex.what()<<endl;
								}
								lineNumber=1;
								while(getline(file, currentLine))
								{    //This is where the error occurs
									system("PAUSE");
									//cout<<"I am reading line "<<lineNumber<<" which is "<<currentLine;
									currentPosition=file.tellg();
									if(currentPosition==endOfFile)
									{
										if(cFlag)
											cout<<numberOfMatches;
										numberOfMatches=0;
										lineNumber=1;
										cout<<endl;
										file.close();
										break;
									}
									else
									{
										cout<<"almost at match\n";
										if(regex_search(currentLine,expressionToMatch) || (!regex_search(currentLine, expressionToMatch) && vFlag))//The last condition says f there is no match and vFlag is true, execute this block of code. Since vFlag is print non-matching lines
										{
											cout<<"match\n";
											numberOfMatches++;
											if(!match && lFlag)//if no match has been made for this file, and lFlag active, which is to print file name
											{
												cout<<fileName<<endl;
												numberOfMatches=0;
												lineNumber=1;
												break;//We know already that the file contains a match so, we need to read no further so break
											}
											else
											{
												 if(nFlag)//If the nFlag is true which means to print line number
													cout<<lineNumber<<". ";
												 cout<<currentLine<<'\n';
												 lineNumber++;
											}
										}
									}
								}
							}
						}
					}
				}		
			}
		}
		else
			cout<<"That specified file could not be found"<<endl;
	}
	return 0;
}