#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "str.h"
#include "st.h"
#include "file.h"
#include "str_tok.h"

#ifdef NO_MAIN
extern bool is_script_running();
#else
bool is_script_running()
{
   return true;
}
#endif

namespace GREP {

using namespace std;
using namespace string_tools;
using namespace io_tools;

const char GREP_VERSION[] = "1.0.0b";

class grep {
   bool show_new_line;
   string expr;

   bool read_line(string & line) {
      bool ret = false;
      line = "";
      char ch;
      ch = cin.get();
      while(ch != EOF) {
         ret = true;
         if(ch == '\n') break;
         line += ch;
         ch = cin.get();
      }
      if(ch == EOF && line.size() != 0) {
         cin.unget();
      }
      return ret;
   }
   bool read_line(fstream & fin, string & line) {
      bool ret = false;
      line = "";
      char ch;
      ch = fin.get();
      while(!fin.fail()) {
         ret = true;
         if(ch == '\n') break;
         line += ch;
         ch = fin.get();
      }
      return ret;
   }
   int file_search(const string & dir, const string & file0) {
      int rc = 0;
      File the_file(dir);
      File::dir_vector_type listing;
      string file = file0;
      file = replace(file, ".", "[\\.]");
      file = replace(file, "*", "(.*)");
      if(the_file.dir(listing)) {
         bool found = false;
         for(size_t index = 0; index < listing.size(); index++) {
            string::size_type n = 0;
            File & temp = listing[index];
            if(find_regex(file,temp.name(),n) != string::npos && is_script_running()) {
               found = true;
               fstream fin(temp.name().c_str(), ios::in);
               if(!fin.fail()) {
                  long lineno = 1L;
                  string line;
                  while(read_line(fin,line) && is_script_running()) {
                     if(find_regex(expr,line,n) != string::npos) {
                        if(show_new_line) {
                           cout << temp.name() << ":" << lineno << ":";
                        }
                        cout << line;
                        cout << endl;
                     }
                     lineno++;
                  }
                  fin.close();
               }
            }
         }
         if(!found) {
            rc = 1;
         }
      }
      else {
         rc = 1;
      }
      return rc;
   }
   int process_file(File & f, 
                    vector< string > & path_vec, 
                    string & path, 
                    const string & dir, 
                    const string & file, 
                    size_t i, 
                    bool & found, 
                    bool & found2) {
      int rc = 0;
      string::size_type n = 0;
      string temp = f.name();
      string temp2 = replace(dir, ".", "[\\.]");
      temp2 = replace(temp2, "*", "(.*)");
      if(find_regex(temp2,temp,n) != string::npos && n == temp.size()) {
         found = true;
         found2 = true;
         string t = path;
         path += temp + "\\";
         rc = dir_search(path_vec,i+1,path,file);
         path = t;
      }
      return rc;
   }
   int dir_search(vector< string > & path_vec, size_t index, string & path, const string & file) {
      int rc = 0;
      string::size_type n;
      string dir;
      bool found2 = false;
      if(index >= path_vec.size()) {
         rc = file_search(path, file);
         if(rc == 0) {
            found2 = true;
         }
      }
      else {
         for(size_t i = index; i < path_vec.size(); i++) {
            dir = path_vec[i];
            if(dir.find("*") == string::npos) {
               string t = path;
               path += dir + "\\";
               rc = dir_search(path_vec,i+1,path,file);
               path = t;
               if(rc != 0) {
                  break;
               }
               else {
                  found2 = true;
               }
            }
            else {
               File the_file(path);
               File::dir_vector_type listing;
               string temp,temp2;
               if(the_file.dir(listing)) {
                  bool found = false;
                  for(size_t j = 0; j < listing.size(); j++) {
                     File & f = listing[j];
                     rc = process_file(f,path_vec,path,dir,file,i,found,found2);
                  }
                  if(rc != 0 || !found) {
                     break;
                  }
               }
            }
            if(rc != 0) {
               break;
            }
         }
      }
      if(!found2) {
         rc = 1;
      }
      return rc;
   }
   void tokenize_path(vector< string > & path_vec, const string & path, string & dir) {
      if(path.size()) {
         dir = path;
         string_tokenizer stok(path, "\\");
         string temp;
         while(stok.has_next()) {
            temp = stok.get_next();
            path_vec.push_back(temp);
         }
      }
   }
   int process_file(const string & file0) {
      int rc = 0;
      string path;
      string file = file0;
      string::size_type ptr = file.rfind("\\");
      if(ptr != string::npos) {
         path = file.substr(0,ptr);
         file = file.substr(ptr+1);
      }
      string dir = ".";
      vector< string > path_vec;
      tokenize_path(path_vec,path,dir);
      if(path_vec.size() == 0) {
         path_vec.push_back(dir);
      }
      string the_path = path;
      if(path.find("*") == string::npos) {
         rc = file_search(dir,file);
      }
      else {
         if(the_path.size()) {
            if(the_path[0] == '\\') {
               path = "\\";
            }
            else {
               path = "";
            }
         }
         else {
            path = "";
         }
         rc = dir_search(path_vec,0,path,file);
      }
      return rc;
   }
   void usage() {
      cout << "Matt Coan GREP command..." << endl;
      cout << "Email: matthewcoan1976@hotmail.com" << endl;
      cout << "Build date: " << __DATE__ << " " << __TIME__ << endl;
      cout << "Version: " << GREP_VERSION << endl << endl;
      cout << "Use this program to search a text file..." << endl << endl;
      cout << "usage: grep [ -n | -h ] <regex> <file_list>" << endl << endl;
   }
public:
   int run(int argc, char ** argv) {  
      int rc = 0;
      show_new_line = false;
      vector< string > file_vec;
      size_t i;
      for(i = 1; i < argc; i++) {
         if(string(argv[i]) == "-n") {
            show_new_line = true;
         }
         else if(string(argv[i]) == "-h") {
            usage();
         }
         else if(expr.size() == 0) {
            expr = argv[i];
         }
         else {
            file_vec.push_back(argv[i]);
         }
      }
      for(i = 0; i < file_vec.size(); i++) {
         rc = process_file(file_vec[i]);
         if(rc != 0) {
            cerr << "file not found..." << endl;
            break;
         }
      }
      if(file_vec.size() == 0) {
         string line;
         string::size_type n = 0;
         while(read_line(line) && is_script_running()) {
            if(find_regex(expr,line,n) != string::npos) {
               cout << line << endl;
            }
         }
      }
      return rc;
   }
};

}

#ifdef NO_MAIN
extern "C" {
extern int grep_main(int argc, char ** argv);
}
int grep_main(int argc, char ** argv)
#else
int main(int argc, char ** argv)
#endif
{
   GREP::grep my_grep;
   int rc = my_grep.run(argc,argv);
   return rc;
}
