#include "stdafx.h"

// доступно только для Qt (Linux|Win)
#ifndef _MFC_VER

#include "aplAggr.h"



#ifdef __linux__
    #include <dirent.h>
    #include <sys/types.h>
    #include <sys/stat.h>
#elif __GNUC__

    typedef void *HWND; // определение нужно, чтобы  не включать wtypes.h

    #include <fileapi.h>
    #include <winbase.h>

#else
	// Qt+MSVC
    #include <direct.h>
    #define PATH_MAX _MAX_PATH
#endif

CaplFileFind::CaplFileFind() :
	m_sPath(_T("")),
	m_current_file(-1)
//  , m_path_id(-1)
{
	m_files.Clear();
}

CaplFileFind::~CaplFileFind()
{
	m_files.Clear();
}

#ifdef __linux__
// под виндой функция FindFirstFile сама разбирается с масками файлов; поэтому сравнивать врукопашную нужно только под линуксом
// В функции используются CapplStringW, потому что CaplStringA с русскими буквами UTF8 некорректно работает поиск
bool CompareWithMask(LPCTSTR _tested, LPCTSTR _mask)
{
	CString tested = _tested;
	CString mask = _mask;
	int iPos, iPosNext;

	if(mask == _T("*"))
		return true; // подходит любое имя

	tested.MakeLower();
	mask.MakeLower();

	iPos = mask.Find(_T("*"));
	if(iPos == -1)
	{
		// звездочек нет, должно быть полное совпадение
		return tested == mask;
	}

	if(iPos > 0)
	{
		// Левее звездочки есть что-то. Имя файла должно начинаться с этой подстроки
		CString left = mask.Left(iPos);
		if(tested.Find(left) != 0)
			return false;
	}

	int iMid, iMidPrev=0;
	while((iPosNext = mask.Find(_T("*"), iPos+1)) > 0)
	{
		// звездочек может быть несколько. То что между ними тоже должно быть в имени
		CString mid = mask.Mid(iPos+1, iPosNext-iPos-1);
		iMid = tested.Find(mid, iMidPrev);
		if(iMid == -1)
			return false;
		// Следующая искомая подстрока должна располагаться правее текущей
		iMidPrev = iMid + mid.GetLength();
		// и дальше текущей звездочки
		iPos = iPosNext;
	}

	//и еще сравниваем то, что правее правой звездочки
	iPos = mask.ReverseFind('*');
	if(iPos < mask.GetLength()-1)
	{
		// Правее звездочки есть что-то. Имя файла должно заканчиваться на эту подстроку
		CString right = mask.Mid(iPos+1);
		int lenPattern = right.GetLength();
		if(tested.Find(right) != tested.GetLength()-lenPattern)
			return false;
	}

	return true;
}
#endif

//********************************************************
int compare_SaplFindFileInfo( const void *arg1, const void *arg2 )
{
	return (*(CaplFileFind::SaplFindFileInfo **)arg1)->name.CompareNoCase((*(CaplFileFind::SaplFindFileInfo **)arg2)->name);
}



BOOL CaplFileFind::FindFile(LPCTSTR pstrName)
{
	m_current_file = -1; // сбрасываем индекс текущего файла
	m_files.Clear();

	// Определяем текущий каталог
	char const_wd[PATH_MAX];
    getcwd(const_wd, PATH_MAX);
	CaplStringAdapter ad(const_wd);
	m_sPath = (LPCTSTR)ad;
	if(m_sPath.Right(1) != aplDirRazd)
		m_sPath += aplDirRazd;

	// Определяем путь поиска и маску файла
	CString sFindPath = _T("");
	CString sFullMask = pstrName;
	sFullMask.Replace(aplMissedDirRazd, aplDirRazd);

#ifdef __linux__
	CString sFileMask = _T("");
	int iFind;
	if(sFullMask==_T(""))
	{
		// строка пустая, ищем все в текущем каталоге
		sFileMask = _T("*.*");
	}
	else
	{
		// Строка не пустая, определяем есть ли путь в строке поиска
		iFind = sFullMask.ReverseFind(aplDirRazd);

		if (iFind != 0)
		{
			// путь есть
			sFindPath = sFullMask.Left(iFind);
			sFileMask = sFullMask.Mid(iFind+1);
		}
		else
		{
			// задана только маска файла
			sFileMask = sFullMask;
		}
	}

	// для линукса выделяем отдельно маску имени и маску расширения
	CString sMaskBase = _T(""), sMaskExt = _T("");
	iFind = sFileMask.ReverseFind(_T('.'));
    if (iFind<0)
    {
		sMaskBase = sFileMask;
		sMaskExt = "*";
    }
    else
    {
		sMaskBase = sFileMask.Left(iFind);
		sMaskExt = sFileMask.Mid(iFind+1);
    }

	CStringA listedDirA;
	if(sFindPath == _T(""))
	{
		// ищем в текущем каталоге
		listedDirA = const_wd;
	}
	else if(sFindPath.Left(1)==aplDirRazd)
	{
		// для поиска задан абсолютный путь
		ad = sFindPath;
		listedDirA = (LPCSTR)ad;
	}
	else
	{
		// для поиска задан относительный путь.
		listedDirA = const_wd;
		listedDirA += aplDirRazd;
		ad = sFindPath;
		listedDirA += (LPCSTR)ad;
	}
	listedDirA += aplDirRazd;


	SaplFindFileInfo* fi;
	DIR *d;
	struct dirent *dir;
	d = opendir(listedDirA + ".");
	if (d)
	{
		struct stat sb;
		CStringA nameA, pathnameA;
		CString name, nameBase, nameExt;
		uint dwFileAttributes;

		while ((dir = readdir(d)) != NULL)
		{
			nameA = dir->d_name;
			pathnameA = listedDirA + dir->d_name;

			stat(pathnameA, &sb);
			dwFileAttributes = (sb.st_mode & S_IFMT);

			// нас интересуют только файлы и каталоги; сокеты, хардлинки и т.п. игнорируем
			if (dwFileAttributes != S_IFREG &&  dwFileAttributes != __S_IFDIR)
				continue;

			// если какая-либо маска не звездочка, то надо фильтровать
			if(sMaskBase != _T("*") || sMaskExt!= _T("*") )
			{
				// используются CaplStringW, потому что в CaplStringA с русскими UTF8 символами некорректно работает поиск
				name = nameA;
				iFind = name.ReverseFind(_T('.'));
				if (iFind<0)
				{
					nameBase = name;
					nameExt = "";
				}
				else
				{
					nameBase = name.Left(iFind);
					nameExt = name.Mid(iFind+1);
				}

				// если не подходит или имя, или расширение - игнорируем файл
				if(!CompareWithMask(nameBase, sMaskBase) || !CompareWithMask(nameExt, sMaskExt))
					continue;
			}

			fi = new SaplFindFileInfo();
			//ad = pathname;
			fi->name = nameA;
			fi->pathname = pathnameA;
			fi->dwFileAttributes = (sb.st_mode & S_IFMT);
			m_files.Add(fi);
		}
		closedir(d);

		qsort(m_files.Data, m_files.GetSize(), sizeof(SaplFindFileInfo*), compare_SaplFindFileInfo);

		return TRUE;
	}
	return FALSE;
#else

	if(sFullMask==_T(""))
	{
		// строка пустая, ищем все в текущем каталоге
		sFullMask = _T("*.*");
	}
	else
	{
		// Строка не пустая, определяем есть ли путь в строке поиска
		int iFind = sFullMask.ReverseFind(aplDirRazd);

		if(sFullMask.Find(_T(":\\"))==1)
		{
			sFindPath = sFullMask.Left(iFind+1);
		}
		else
		{
			// путь оносительный
			sFindPath = m_sPath;
			if(iFind != -1)
				sFindPath += sFullMask.Left(iFind+1);
		}
	}

	WIN32_FIND_DATA FindFileData;
	HANDLE hf;
	hf=FindFirstFile(sFullMask, &FindFileData);
	SaplFindFileInfo* fi;

	if (hf!=INVALID_HANDLE_VALUE)
	{
		CString str;
		do
		{
			fi = new SaplFindFileInfo();
			//ad = pathname;
			fi->name = FindFileData.cFileName;
			fi->pathname = sFindPath + FindFileData.cFileName;
			fi->dwFileAttributes = FindFileData.dwFileAttributes;
			m_files.Add(fi);

		}
		while (::FindNextFile(hf,&FindFileData)!=0);
		FindClose(hf);

		qsort(m_files.Data, m_files.GetSize(), sizeof(SaplFindFileInfo*), compare_SaplFindFileInfo);

		return TRUE;
	}

	return FALSE;
#endif

}

// в win нельзя назвать функцию FindNextFile - эта строчка подменяется макросом, определенном в fileapi.h
BOOL CaplFileFind::FindNextFileEx()
{
    if (m_files.GetSize()==0)
        return FALSE;

    ++m_current_file;
    if (m_current_file>=m_files.GetSize())
	{
        return FALSE;
	}

	return TRUE;
}

 // Является ли файл каталогом текущего или верхнего уровня
bool CaplFileFind::IsDots()
{
	if(m_current_file<0 || m_current_file >= m_files.GetSize()) return false;
	CString sName=m_files[m_current_file]->name;
	return (sName== _T(".") || sName== _T(".."));
}

 // Является ли найденный файл подкаталогом
bool CaplFileFind::IsDirectory()
{
	if(m_current_file<0 || m_current_file >= m_files.GetSize()) return false;
	UINT dwAttribute = m_files[m_current_file]->dwFileAttributes;
#ifdef __linux__
	return dwAttribute == __S_IFDIR;
#else
	return dwAttribute & FILE_ATTRIBUTE_DIRECTORY;
#endif
}


CString CaplFileFind::GetFilePath() const
{
	if(m_current_file<0 || m_current_file >= m_files.GetSize())
		return _T("");
	return m_files[m_current_file]->pathname;
}

CString CaplFileFind::GetFileName() const
{
	if(m_current_file<0 || m_current_file >= m_files.GetSize())
		return _T("");

	return m_files[m_current_file]->name;
}

// в win нельзя назвать функцию GetFileTitle - эта строчка подменяется макросом, определенном в fileapi.h
CString CaplFileFind::GetFileTitleEx() const
{
	if(m_current_file<0) return _T("");

	CString sname=GetFileName();

	int i=sname.ReverseFind(_T('.'));
	if(i<0) return sname;
	if (i>0) return sname.Left(i);
	return _T("");
}


#endif // #ifndef _MFC_VER
