// stdafx.cpp : source file that includes just the standard includes
//	filesys.pch will be the pre-compiled header
//	stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"
#include "EmptyWDX.h"
#include "cunicode.h"
#include "deelx.h"
#include <cstring>
#include <string>
#include <sstream>
#include <queue>
#include <map>
#include <functional> 

#include <shlwapi.h>
#pragma comment (lib, "shlwapi")
using namespace std;

// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

#define pluginrootlen 1
#define _detectstring ""
#define HNULL  INVALID_HANDLE_VALUE
#define A_DIR  FILE_ATTRIBUTE_DIRECTORY

wstring _filename = L"";
wstring ininame = L"";
int totalcount = 2;

typedef struct _field
{
	char *name;
	int type;
	char *unit;
} FIELD;

#define fieldcount (sizeof(fields)/sizeof(FIELD))

FIELD fields[]={
      {"IsEmpty(BOOL)",  ft_boolean,""},//basic
      {"IsEmpty(String)",  ft_string,""},//basic
	  {"Contains(BOOL)",  ft_boolean,""},//will be changed to dynamic one
	  {"Contains(String)",  ft_string,""},
	  //{"Test",  ft_string,""},
};

BOOL GetValueAborted=false;

wstring ctow(const char* src){ 
	wstring wstr(src, src + strlen(src));
	return wstr;
}
struct cmp_str
{
	bool operator()(char const *a, char const *b)
	{
		return std::strcmp(a, b) < 0;
	}
};
static std::map<char*, int,cmp_str> intKeys;

int ReadIniInt(char* szSection, char* szKey, int szDefaultValue, char* m_szFileName)
{
	int tmp = szDefaultValue;
	if (intKeys.find(szKey) != intKeys.end()){
		tmp = intKeys[szKey];
	}
	else {
	  tmp = GetPrivateProfileInt(szSection, szKey, szDefaultValue, m_szFileName);
	  intKeys[szKey] = tmp;
    }
	return tmp;
}

static std::map<char*, wstring,cmp_str> stringKeys;

wstring ReadIniString(char* szSection, char* szKey, const char* szDefaultValue, char* m_szFileName)
{
	if (stringKeys.find(szKey) != stringKeys.end()){
		return stringKeys[szKey];
	}
	char* szResult = new char[1024];
	memset(szResult, 0x00, 1024);
	GetPrivateProfileString(szSection,  szKey, szDefaultValue, szResult, 1024, m_szFileName); 
	wstring tmp = ctow(szResult);
	stringKeys[szKey] = tmp;
	return tmp;
}

HMODULE GetCurrentModule()
{
  MEMORY_BASIC_INFORMATION mbi;
  VirtualQuery( GetCurrentModule, &mbi, sizeof(mbi) );

  return (HINSTANCE)mbi.AllocationBase;
}

void GetIniName(){

    TCHAR szAppPath[MAX_PATH];
    DWORD nr = GetModuleFileName(GetCurrentModule(), szAppPath, sizeof(szAppPath));    
	ininame.assign(&szAppPath[0], &szAppPath[nr]);
	int lenExt = ininame.length() - ininame.find_last_of(L".") ;
	ininame.erase(ininame.end() - lenExt, ininame.end());
	ininame.append(L".ini");	
}

string narrow( const wstring& str )
{
    ostringstream stm ;
    const ctype<char>& ctfacet = 
                         use_facet< ctype<char> >( stm.getloc() ) ;
    for( size_t i=0 ; i<str.size() ; ++i ) 
                  stm << ctfacet.narrow( str[i], 0 ) ;
    return stm.str() ;
}

wstring GetFilterValueForNumber(int number)
{
	wchar_t buf[8];		
	_itow(number, buf, 10);	

	string str = narrow(ininame);
	
	char *tmpstr = new char[str.size() + 1];
	strcpy ( tmpstr, str.c_str() );

	wstring myfilt = L"";
	myfilt.append(buf);
	myfilt.append(L"_Filter");
	
	string namekey = narrow(myfilt);
	char *tmpkey = new char[namekey.size() + 1];
	strcpy ( tmpkey, namekey.c_str() );

	wstring filtw = ReadIniString("WDXContains", tmpkey, "\\", tmpstr);

	return filtw;
}

int GetFiltersNumber()
{
	int fieldcnt = 0;
	string str = narrow(ininame);
	char *tmpstr = new char[str.size() + 1];
	strcpy ( tmpstr, str.c_str() );

	for (fieldcnt = 0; fieldcnt < 20; fieldcnt++)
	{	
		wstring tmpfilt = GetFilterValueForNumber(fieldcnt + 1);

		int result = wcscmp( tmpfilt.c_str(), L"\\" );

		if (result == 0)
		{
			break;
		}
	}

	return fieldcnt;
}



wstring GetFilterNameForNumber(int number)
{
	wchar_t buf[8];		
	_itow(number, buf, 10);	

	string str = narrow(ininame);
	
	char *tmpstr = new char[str.size() + 1];
	strcpy ( tmpstr, str.c_str() );

	wstring myfilt = L"";
	myfilt.append(buf);
	myfilt.append(L"_Name");
	
	string namekey = narrow(myfilt);
	char *tmpkey = new char[namekey.size() + 1];
	strcpy ( tmpkey, namekey.c_str() );

	wstring tmpdefname = L"";
	tmpdefname.append(L"Filter ");
	tmpdefname.append(buf);
	string tmpdef = narrow(tmpdefname);
	char *tmpdefn = new char[tmpdef.size() + 1];
	strcpy ( tmpdefn, tmpdef.c_str() );

	wstring filtw = ReadIniString("WDXContains", tmpkey, tmpdefn, tmpstr);

	return filtw;
}

int GetTypeValueForNumber(int number)
{
	wchar_t buf[8];		
	_itow(number, buf, 10);	

	string str = narrow(ininame);
	
	char *tmpstr = new char[str.size() + 1];
	strcpy ( tmpstr, str.c_str() );

	wstring myfilt = L"";
	myfilt.append(buf);
	myfilt.append(L"_Type");
	
	string namekey = narrow(myfilt);
	char *tmpkey = new char[namekey.size() + 1];
	strcpy ( tmpkey, namekey.c_str() );

	int tmptype = ReadIniInt("WDXContains", tmpkey, 0, tmpstr);

	return tmptype;
}

int GetMaxValueForNumber(int number)
{
	wchar_t buf[8];
	_itow(number, buf, 10);

	string str = narrow(ininame);

	char *tmpstr = new char[str.size() + 1];
	strcpy(tmpstr, str.c_str());

	wstring myfilt = L"";
	myfilt.append(buf);
	myfilt.append(L"_Max");

	string namekey = narrow(myfilt);
	char *tmpkey = new char[namekey.size() + 1];
	strcpy(tmpkey, namekey.c_str());

	int tmptype = ReadIniInt("WDXContains", tmpkey, 1, tmpstr);

	return tmptype;
}

bool GetRegexpStateByNumber(int number)
{
	wchar_t buf[8];		
	_itow(number, buf, 10);	

	string str = narrow(ininame);
	
	char *tmpstr = new char[str.size() + 1];
	strcpy ( tmpstr, str.c_str() );

	wstring myfilt = L"";
	myfilt.append(buf);
	myfilt.append(L"_IsRegex");
	
	string namekey = narrow(myfilt);
	char *tmpkey = new char[namekey.size() + 1];
	strcpy ( tmpkey, namekey.c_str() );

	int tmptype = ReadIniInt("WDXContains", tmpkey, 0, tmpstr);

	if (tmptype > 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

int __stdcall ContentGetDetectString(char* DetectString,int maxlen)
{
	strncpy(DetectString,_detectstring,maxlen);
	return 0;
}

int __stdcall ContentGetSupportedField(int FieldIndex,char* FieldName,char* Units,int maxlen)
{
	GetIniName();

	//calculate field count

	int fieldscnt = GetFiltersNumber();
	totalcount = 2 + fieldscnt;

	if (FieldIndex<0 || FieldIndex>= totalcount)
		return ft_nomorefields;

	if (FieldIndex == 0 || FieldIndex == 1)
	{
		strncpy(FieldName,fields[FieldIndex].name,maxlen-1);
		strncpy(Units,fields[FieldIndex].unit,maxlen-1);
		return fields[FieldIndex].type;
	}

	//read from INI

	if (FieldIndex >= 2 && FieldIndex < totalcount)
	{
		strncpy(Units,"",maxlen-1);		
		
		wstring tmpname = GetFilterNameForNumber(FieldIndex - 1);		

		string tmp2 = narrow(tmpname);
		char *tmp3 = new char[tmp2.size() + 1];
		strcpy ( tmp3, tmp2.c_str() );
		strncpy(FieldName,tmp3,maxlen-1);

		//get type		
		int tmptype = GetTypeValueForNumber(FieldIndex -1);

		switch (tmptype)
		{
			case 0:
				return ft_boolean;
				break;
			case 1:
				return ft_string;
				break;
			default:
				return ft_nomorefields;
				break;
		}
	}

	return ft_nomorefields;
}

bool IsFieldEmpty(string field)
{
     int i = field.length();

     if (i == 0)
     {
        return true;
     }
     
     return false;
}

char* UnicodeToAnsi(LPCWSTR s)
{
	if (s==NULL) return NULL;
	int cw=lstrlenW(s);
	if (cw==0) {CHAR *psz=new CHAR[1];*psz='\0';return psz;}
	int cc=WideCharToMultiByte(CP_ACP,0,s,cw,NULL,0,NULL,NULL);
	if (cc==0) return NULL;
	CHAR *psz=new CHAR[cc+1];
	cc=WideCharToMultiByte(CP_ACP,0,s,cw,psz,cc,NULL,NULL);
	if (cc==0) {delete[] psz;return NULL;}
	psz[cc]='\0';
	return psz;
}

/*bool IsEmpty(WCHAR* FileName)
{
	return false;
}*/

bool IsPathEmpty(WCHAR* _path)
{	
	return PathIsDirectoryEmptyW((LPCWSTR) _path);
}


bool ContainsFile(WCHAR* _path, wstring filtw)
{
	HANDLE fh2;
	WIN32_FIND_DATAW fd2;	
	bool cont = false;

	//split filtw
	wchar_t* token;
	token = wcstok((WCHAR*) filtw.c_str(), L"|");

	while( token != NULL )
	{		 
		wstring _filename = L"";
			
		_filename = (wstring) _path;
		_filename = _filename.append(L"\\");
		_filename = _filename.append(token);

		fh2 = FindFirstFileT((WCHAR*) _filename.c_str(), &fd2);

		if (fh2 == INVALID_HANDLE_VALUE)
		{		
			cont = false;
		}
		else
		{
			int result1 = wcscmp( fd2.cFileName, L"." );
			int result2 = wcscmp( fd2.cFileName, L".." );
			bool more = true;		

			while (more)
			{
				cont = false;			

				if (more)
				{
					result1 = wcscmp( fd2.cFileName, L"." );
					result2 = wcscmp( fd2.cFileName, L".." );

					if ((result1 != 0 ) && (result2 != 0))
					{				
						cont = true;
						break;
					}

					more = FindNextFileT(fh2, &fd2);
				}			
			}
		}

		FindClose(fh2);

		if (cont)
		{
			break;
		}
		token = wcstok( NULL, L"|" );
	 }

	return cont;
}
bool ContainsFileRegexp(WCHAR* _path, wstring filtw)
{
	HANDLE fh2;
	WIN32_FIND_DATAW fd2;
	bool cont = false;

	//split filtw
	wchar_t* token;
	token = wcstok((WCHAR*)filtw.c_str(), L"|");

	while (token != NULL)
	{
		wstring _filename = L"";

		_filename = (wstring)_path;
		_filename = _filename.append(L"\\*.*");

		fh2 = FindFirstFileT((WCHAR*)_filename.c_str(), &fd2);

		if (fh2 == INVALID_HANDLE_VALUE)
		{
			cont = false;
		}
		else
		{
			int result1 = wcscmp(fd2.cFileName, L".");
			int result2 = wcscmp(fd2.cFileName, L"..");
			bool more = true;

			CRegexpT <WCHAR> regexp(filtw.c_str());

			while (more)
			{
				cont = false;

				if (more)
				{
					result1 = wcscmp(fd2.cFileName, L".");
					result2 = wcscmp(fd2.cFileName, L"..");

					if ((result1 != 0) && (result2 != 0))
					{
						MatchResult result = regexp.Match(fd2.cFileName);

						if (result.IsMatched())
						{
							cont = true;
							break;
						}
					}

					more = FindNextFileT(fh2, &fd2);
				}
			}
		}

		FindClose(fh2);

		if (cont)
		{
			break;
		}
		token = wcstok(NULL, L"|");
	}

	return cont;
}

bool RecurseContainFile(WCHAR* _path, wstring filtw,int nbMAX,bool isRegex){
	wstring wpath = wstring(_path);
	HANDLE finder;          // for FindFirstFile
	WIN32_FIND_DATAW file;   // data about current file.

	priority_queue< wstring, std::vector<wstring>, greater<wstring> > dirs;
	int nbFolder = 1;

	dirs.push(wpath); // start with passed directory 

	do {
		wstring path = dirs.top();// retrieve directory to search
		WCHAR*  __path;
		dirs.pop();


		if ( ! isRegex && ContainsFile((WCHAR*)path.c_str(), filtw)){
			return true;
		}
		else if ( isRegex && ContainsFileRegexp((WCHAR*)path.c_str(), filtw)){
		}



		// Then search for subdirectories:
		path.append(L"\\");
		if (nbMAX >= nbFolder && HNULL == (finder = FindFirstFileT((WCHAR*)(path + L"*").c_str(), &file)))
			continue;
		do {
			if ((file.dwFileAttributes & A_DIR) && (file.cFileName[0] != '.') && nbFolder < nbMAX){
				dirs.push(path + file.cFileName);
				nbFolder++;
			}
		} while (FindNextFileT(finder, &file) && nbFolder < nbMAX );
		FindClose(finder);
	} while (!dirs.empty());
	return false;
}


char* strlcpy(char* p,const char* p2,int maxlen)
{
    if ((int)strlen(p2)>=maxlen) {
        strncpy(p,p2,maxlen);
        p[maxlen]=0;
    } else
        strcpy(p,p2);
    return p;
}

wstring AppendFilter(WCHAR* FileName, wstring filter)
{
	if (_filename.compare(FileName) !=0)
	{			
		WCHAR FileNameW[wdirtypemax];
			
		_filename = (wstring) FileName;
		_filename = _filename.append(L"\\");
		_filename = _filename.append(filter);
	}

	return L"";
}

int __stdcall ContentGetValueT(bool unicode, WCHAR* FileName,int FieldIndex,int UnitIndex,void* FieldValue,int maxlen,int flags)
{
	WIN32_FIND_DATAW fd;
	HANDLE fh;
	__int64 filesize;


	if (ininame.length() == 0)
	{
		GetIniName();
	}

	if (wcslen(FileName+pluginrootlen)<=3)
		return ft_fileerror;


	int tmpMax = GetMaxValueForNumber(FieldIndex - 1);
	if (tmpMax > 5 && flags & CONTENT_DELAYIFSLOW) {
		return ft_ondemand;
	}
	if (tmpMax > 1 && flags & CONTENT_DELAYIFSLOW) {
		return ft_delayed;
	}
  	fh=FindFirstFolderT(FileName, &fd);
	

	if (fh!=INVALID_HANDLE_VALUE)
	{
		FindClose(fh);		

		string tmp = "";	
		int tmp2 = -1;		

		wstring filtw = L"";//ReadIniString("WDXContains", "Filter", "*.*", tmpstr);
		
		int tmptype = 0;

		bool tmpcontains = false;

		switch (FieldIndex) {
			case 0:  // Empty bool				
				tmp2 = fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
				
				if (tmp2 == 16)
				{
					//AppendFilter(FileName, L"\\*.*");
					AppendFilter(FileName, L"\\");

					if ( IsPathEmpty( (WCHAR*) _filename.c_str() ) )
					{
						*(int*)FieldValue = 1;
					}
					else
					{
						*(int*)FieldValue = 0;
					}
					return ft_boolean;
				}
				else
				{					
					return ft_fieldempty;
				}				
				break;
			case 1:  //Empty String				
				tmp2 = fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
				
				if (tmp2 == 16)
				{	
					//AppendFilter(FileName, L"\\*.*");
					AppendFilter(FileName, L"\\");
					if ( IsPathEmpty( (WCHAR*) _filename.c_str() ) )
					{
						strlcpy((char*)FieldValue,"X",maxlen-1);
						return ft_string;
					}
					else
					{
						return ft_fieldempty;
					}
				}
				else
				{					
					return ft_fieldempty;
				}				
				break;			
			default:
				if ((FieldIndex < totalcount) && (FieldIndex > 1))
				{
					//check

					tmp2 = fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;

					if (tmp2 == 16)
					{
						filtw = GetFilterValueForNumber(FieldIndex-1);

						if (GetRegexpStateByNumber(FieldIndex-1))
						{
							tmpcontains = RecurseContainFile(FileName, filtw, tmpMax, true);
						}
						else
						{
							tmpcontains = RecurseContainFile(FileName, filtw, tmpMax, false);
						}						
						
						tmptype = GetTypeValueForNumber(FieldIndex-1);

						switch (tmptype)
						{
							case 0:
								if (tmpcontains)
								{
									*(int*)FieldValue = 1;
								}
								else
								{
									*(int*)FieldValue = 0;
								}
								return ft_boolean;
								break;
							case 1:
								if (tmpcontains)
								{
									strlcpy((char*)FieldValue,"X",maxlen-1);
								}
								else
								{
									strlcpy((char*)FieldValue,"",maxlen-1);
								}
								return ft_string;
								break;
							default:
								return ft_fieldempty;
								break;
						}
					}
					else
					{
						return ft_fieldempty;
					}
				}
				else
				{
					return ft_nosuchfield;
				}
			}
	}
	else
	{
		return ft_fileerror;
	}

	return fields[FieldIndex].type;  // very important!
}

int __stdcall ContentGetValueW(WCHAR* FileName,int FieldIndex,int UnitIndex,void* FieldValue,int maxlen,int flags)
{
	return ContentGetValueT(true,FileName,FieldIndex,UnitIndex,FieldValue,maxlen,flags);
}

int __stdcall ContentGetValue(char* FileName,int FieldIndex,int UnitIndex,void* FieldValue,int maxlen,int flags)
{
	WCHAR FileNameW[wdirtypemax];
	return ContentGetValueT(false,awfilenamecopy(FileNameW,FileName),FieldIndex,UnitIndex,FieldValue,maxlen,flags);
}