#ifndef _FILE_H
#define _FILE_H

#include <windows.h>
#include <shlwapi.h>
#include <fstream>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <cstdlib>
#include <sys/stat.h>
#include <sys/types.h>
#include "str.h"
#include "str_tok.h"
//#include <io.h>

namespace io_tools {

using namespace std;
using namespace string_tools;

class File {
public:
   typedef vector< File > dir_vector_type;
private:
   string _name;
public:
   File(const string & name) : _name(name) { 
   }
   File(const File & f) : _name(f._name) { 
   }
   File() {
   }
   ~File() {
   }
   off_t get_last_mod() {
      struct _stat st;
      _stat(_name.c_str(), &st);
      return st.st_mtime;
   }
   File & operator=(const File & right) {
      _name = right._name;
      return *this;
   }
   string name() { return get_name(); }
   string get_name() { 
      string temp = get_full_path();
      string::size_type ptr = temp.rfind('\\');
      if(ptr != string::npos && temp.size()) {
         temp = temp.substr(ptr+1);
      }
      else {
         temp = _name;
      }
      return temp; 
   }
   string get_full_path() const {
      string ret;
      TCHAR dir_part[MAX_PATH];
      TCHAR ** file_part = {0};
      memset(dir_part, 0, MAX_PATH);
      int retval = GetFullPathName(_name.c_str(), MAX_PATH-1, dir_part, file_part);
      ret = dir_part;
      if(file_part != 0) {
         if(*file_part != 0) {
            ret += *file_part;
         }
      }
      return ret;
   }
   bool exists() {
      return PathFileExists(_name.c_str()) != FALSE;
   }
   void hide() {         
      SetFileAttributes(_name.c_str(), FILE_ATTRIBUTE_HIDDEN);
   }
   void unhide() {
      SetFileAttributes(_name.c_str(), GetFileAttributes(_name.c_str()) & ~FILE_ATTRIBUTE_HIDDEN);
   }
   void set_read_only() {
      SetFileAttributes(_name.c_str(), FILE_ATTRIBUTE_READONLY);
   }
   void set_writable() {
      SetFileAttributes(_name.c_str(), GetFileAttributes(_name.c_str()) & ~FILE_ATTRIBUTE_READONLY);
   }
   bool is_hidden() {
      return GetFileAttributes(_name.c_str()) & FILE_ATTRIBUTE_HIDDEN;
   }
   bool is_read_only() {
      return GetFileAttributes(_name.c_str()) & FILE_ATTRIBUTE_READONLY;
   }
   bool is_directory() {
      bool ret = false;
      ret = GetFileAttributes(_name.c_str()) & FILE_ATTRIBUTE_DIRECTORY;
      if(exists() == false)
         ret = false;
      return ret;
   }
   bool is_file() {
      bool ret = false;
      ret = !(GetFileAttributes(_name.c_str()) & FILE_ATTRIBUTE_DIRECTORY);
      return ret;
   }
   long length() {
      HANDLE hFile = CreateFile(_name.c_str(), 
                                GENERIC_READ | GENERIC_WRITE, 0, NULL,
                                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
      long ret = 0L;
      if(hFile != 0) {
         ret = GetFileSize(hFile, 0);
         CloseHandle(hFile);
      }
      return ret;
   }
   bool dir(dir_vector_type & dir_vec) {
      dir_vec.clear();
      bool ret = false;
      if(is_directory()) {
         string first_file = _name + "\\*";
         if(_name.size()) {
            if(_name[_name.size()-1] == '\\') {
               first_file = _name + "*";
            }
         }
         WIN32_FIND_DATA ffd;
         HANDLE hFind = FindFirstFile(first_file.c_str(), &ffd);
         //dir_vec.clear();
         if(hFind != INVALID_HANDLE_VALUE) {
            while(hFind != INVALID_HANDLE_VALUE) {
               dir_vec.push_back(File(ffd.cFileName));
               ret = true;
               if(!FindNextFile(hFind, &ffd)) {
                  break;
               }
            }
            FindClose(hFind);
         } 
      }
      return ret;
   }
   bool mkdir() {
      return CreateDirectory(_name.c_str(), NULL) != FALSE;
   }
   bool remove() {
      return DeleteFile(_name.c_str()) != FALSE;
   }
   bool rmdir() {
      return RemoveDirectory(_name.c_str()) != FALSE;
   }
   bool copy(const File & file) {
      if(is_file()) {
         return CopyFile(get_full_path().c_str(), file.get_full_path().c_str(), FALSE) != FALSE;
      }
      return false;
   }
   bool rename(const string & nam) {
      bool ret = false;
      ret = MoveFile(_name.c_str(), nam.c_str());
      if(ret) {
         _name = nam;
      }
      return ret;
   }
   bool operator<(const File & right) {
      bool ret = false;
      if(strcasecmp(_name.c_str(), right._name.c_str()) < 0) {
         ret = true;
      }
      return ret;
   }
};

inline
string to_string(const vector< string > & vec)
{
   stringstream ss;
   for(size_t i = 0; i < vec.size(); i++) {
      ss << "\\";
      ss << vec[i];
   }
   return ss.str();
}

inline
void list_all(vector< string > & regex, 
              list< string > & collect,
              vector< string > & temp, 
              string dir,
              size_t I)
{
   if(temp.size() == regex.size()) {
      string ff = to_string(temp);
      if(find(collect.begin(), collect.end(), ff) == collect.end()) {
         collect.push_back(File(ff).get_full_path());
      }
   }
   else {
      File::dir_vector_type dir_vec;
      File d(dir);
      if(d.is_directory() || d.is_file()) {
         if(d.dir(dir_vec)) {
            if(I < regex.size()) {
              for(size_t j = 0; j < dir_vec.size(); j++) {
                  if(dir_vec[j].get_name() == "." || dir_vec[j].get_name() == "..") {
                     continue;
                  }
                  temp.push_back(dir_vec[j].get_name());
                  if(strcasecmp(dir_vec[j].get_name().c_str(), regex[I].c_str()) == 0) {            
                     list_all(regex, collect, temp, to_string(temp), I+1);
                  }
                  else if(wild_card_match(dir_vec[j].get_name(), regex[I])) {            
                     list_all(regex, collect, temp, to_string(temp), I+1);
                  }
                  temp.pop_back();
               }
            }
         }
      }
   }
}

inline
bool list_all(string dir, list< string > & collect) 
{
   bool ret = false;
   if(dir[0] != '\\' && dir.find(":\\") == string::npos) {
      File ff(".");
      dir = ff.get_full_path() + "\\" + dir;
   }
cout << "dir0: " << dir << endl;
   string_tokenizer st(dir, "\\");
   vector< string > regex,temp;
   string file;
   while(st.has_next()) {
      file = st.get_next();
      regex.push_back(file);
   }
   collect.clear();
   list_all(regex,collect,temp,"\\",0);
   if(collect.size()) {
      ret = true;
   }
   return ret;
}

}

#endif /* _FILE_H */
