#pragma once

#define RS_LIBRARIES_FILE_DIR   L"RS Libraries\\"
#define RS_PROJECTS_FILE_DIR    L"RS Configurations\\"
#define RS_RECORDINGS_FILE_DIR  L"RS Recordings\\"
#define RS_CONTEXTS_FILE_DIR    L"RS Configurations\\"
#define RS_SCENES_FILE_DIR      L"RS Scenes\\"
#define RS_PREFERENCES_FILE_DIR L"RS Preferences\\"
#define RS_CONFIGURATIONS_FILE_DIR L"RS Configurations\\"
#define RS_SCRIPTS_FILE_DIR     L"Scripts\\"


class CRsFileTools
{
public :
	static bool CheckFileName(CStringW &strName)
	{
		strName.TrimLeft();
		strName.TrimRight();

		CStringW result;
		bool wasSpace = false;

		for ( int i = 0; i < strName.GetLength(); i++)
		{
			wchar_t ch = strName[ i];

			bool isOK = ( ch >= 'a' && ch <= 'z') || ( ch >= 'A' && ch <= 'Z') || ( ch == '_') || ( ch == ' ') || ( ch >= '0' && ch <= '9');

			if ( !isOK)
			{
				if ( ch == '"')
					ch = '\'';
				else
					ch = ' ';
			}

			if ( ch == ' ')
			{
				if ( wasSpace)
					continue;

				wasSpace = true;
			}
			else
				wasSpace = false;

			result += ch;
		}

		strName = result;

		return true;
	}

	static bool CheckFileName(CComBSTR &strName)
	{
		CStringW strwName = strName;
		bool result = CRsFileTools::CheckFileName( strwName);
		strName = strwName;

		return result;
	}

    static void SplitPathA(const char *path, char *drive, size_t driveLen, char *dir, size_t dirLen, 
                           char *fname, size_t fnameLen, char *ext, size_t extLen)
    {
        #if (_MSC_VER>=1400)
            errno_t err = _splitpath_s(path, drive, driveLen, dir, dirLen, fname, fnameLen, ext, extLen);
            ATLASSERT(err == 0);
        #else
            _splitpath(path, drive, dir, fname, ext);
        #endif
    }

    static void SplitPathW(const wchar_t *path, wchar_t *drive, size_t driveLen, wchar_t *dir, size_t dirLen, 
                           wchar_t *fname, size_t fnameLen, wchar_t *ext, size_t extLen)
    {
        #if (_MSC_VER>=1400)
            errno_t err = _wsplitpath_s(path, drive, driveLen, dir, dirLen, fname, fnameLen, ext, extLen);
            ATLASSERT(err == 0);
        #else
            _wsplitpath(path, drive, dir, fname, ext);
        #endif
    }

    static void MakePathA(char *path, size_t pathLen, const char *drive, const char *dir, const char *fname, const char *ext)
    {
        #if (_MSC_VER>=1400)
            errno_t err = _makepath_s(path, pathLen, drive, dir, fname, ext);
            ATLASSERT(err == 0);
        #else
            _makepath(path, drive, dir, fname, ext);
        #endif
    }

    static void MakePathW(wchar_t *path, size_t pathLen, const wchar_t *drive, const wchar_t *dir, const wchar_t *fname, const wchar_t *ext)
    {
        #if (_MSC_VER>=1400)
            errno_t err = _wmakepath_s(path, pathLen, drive, dir, fname, ext);
            ATLASSERT(err == 0);
        #else
            _wmakepath(path, drive, dir, fname, ext);
        #endif
    }

    static void MakeLokalPathDependend( CStringW &path)
	{
		if ( path.GetLength() > 1)
		{
			if ( ( path[ 0] == '\\') && ( path[ 1] != '\\'))
			{
				CComBSTR bsAppPath;
				GetAppPath( bsAppPath);

				path = bsAppPath + path.Mid( 1);
			}
			else if ( path[ 0] != '\\')
			{
				wchar_t pFileDrive[ MAX_PATH+1];
                SplitPathW(path, pFileDrive, MAX_PATH+1, NULL, 0, NULL, 0, NULL, 0);
				CString szDrive(pFileDrive);

				if ( szDrive.GetLength() == 0)
				{
					CComBSTR bsAppPath;
					GetAppPath( bsAppPath);

					path = bsAppPath + path;
				}
			}
		}
	}

	static void MakeLokalPathDependend(CComBSTR &strName)
	{
		CStringW strwName = strName;
		CRsFileTools::MakeLokalPathDependend( strwName);
		strName = strwName;
	}

	static void MakeLokalPathIndependend( CStringW &path)
	{
		CComBSTR bsAppPath;
		GetAppPath( bsAppPath);

		CStringW strAppPath = bsAppPath;
        int iAppPathLen = strAppPath.GetLength();
		if ((iAppPathLen > 0) && (_wcsnicmp(path.Left(iAppPathLen), strAppPath, iAppPathLen) == 0))
		{
			path = path.Mid( strAppPath.GetLength() - 1);

			if ( path.GetLength() > 0)
			{
				if ( path[ 0] != '\\')
					path = L"\\" + path;
			}
		}
	}

	static void MakeLokalPathIndependend(CComBSTR &strName)
	{
		CStringW strwName = strName;
		CRsFileTools::MakeLokalPathIndependend( strwName);
		strName = strwName;
	}

	static bool GetAppPath(CStringW &strLibrariesPath)
	{
		const DWORD dwFileNLength = MAX_PATH+1;
		wchar_t pFileName[ dwFileNLength];
		GetModuleFileNameW( NULL, pFileName, dwFileNLength);

		wchar_t pFileDrive[ dwFileNLength];
		wchar_t pFileDir[ dwFileNLength + 20];
        SplitPathW(pFileName, pFileDrive, dwFileNLength, pFileDir, dwFileNLength + 20, NULL, 0, NULL, 0);

		MakePathW(pFileName, dwFileNLength, pFileDrive, pFileDir, NULL, NULL);

		strLibrariesPath = pFileName;
		return true; 
	}

	static bool GetAppPath(CComBSTR &strName)
	{
		CStringW strwName = strName;
		bool result = CRsFileTools::GetAppPath( strwName);
		strName = strwName;

		return result;
	}

	static bool GetNewFile( CStringW pathName, CStringW fileName, CStringW extension, CStringW &result)
	{
		fileName.TrimLeft();
		fileName.TrimRight();

		if ( fileName.GetLength() <= 0)
			pathName = L"no name";

		if ( pathName.Right( 1) != L"\\")
			pathName += L"\\";

		if ( !PathFileExistsW( pathName + fileName + extension))
		{
			result = pathName + fileName + extension;

			return true;
		}

		for ( int i = 2; i < 1000; i++)
		{
			CStringW newFile;

			newFile.Format( fileName +L"(%d)", i);
			if ( !PathFileExistsW( pathName + newFile + extension))
			{
				result = pathName + newFile + extension;

				return true;
			}
		}

		return false;
	}

	static bool GetNewFile( CComBSTR pathName, CComBSTR fileName, CComBSTR extension, CComBSTR &result)
	{
		CStringW _pathName = pathName;
		CStringW _fileName = fileName;
		CStringW _extension = extension;
		CStringW _result;

		bool res = GetNewFile( _pathName, _fileName, _extension, _result);

		result = _result;

		return res;
	}

    static bool GetScriptsPath(CStringW &strScriptsPath)
    {
        GetAppPath(strScriptsPath);
        strScriptsPath += RS_SCRIPTS_FILE_DIR;
        return true; 
    }

    static bool GetLayoutsPath(CStringW &strLibrariesPath)
	{
        GetAppPath(strLibrariesPath);
        strLibrariesPath += RS_CONFIGURATIONS_FILE_DIR;
		return true; 
	}

	static bool GetPreferencesPath(CStringW &strLibrariesPath)
	{
        GetAppPath(strLibrariesPath);
        strLibrariesPath += RS_PREFERENCES_FILE_DIR;
		return true; 
	}

	static bool GetScenesPath(CStringW &strLibrariesPath)
	{
        GetAppPath(strLibrariesPath);
        strLibrariesPath += RS_SCENES_FILE_DIR;
		return true; 
	}

	static bool GetContextsPath(CStringW &strLibrariesPath)
	{
        GetAppPath(strLibrariesPath);
        strLibrariesPath += RS_CONTEXTS_FILE_DIR;
		return true; 
	}

	static bool GetRecordingsPath(CStringW &strLibrariesPath)
	{
        GetAppPath(strLibrariesPath);
        strLibrariesPath += RS_RECORDINGS_FILE_DIR;
		return true; 
	}

	static bool GetProjectsPath(CStringW &strLibrariesPath)
	{
        GetAppPath(strLibrariesPath);
        strLibrariesPath += RS_PROJECTS_FILE_DIR;
		return true; 
	}

	static bool GetLibrariesPath(CStringW &strLibrariesPath)
	{
        GetAppPath(strLibrariesPath);
        strLibrariesPath += RS_LIBRARIES_FILE_DIR;
		return true; 
	}

	static bool GetLayoutsPath(CComBSTR &strName)
	{
		CStringW strwName = strName;
		bool result = CRsFileTools::GetLayoutsPath( strwName);
		strName = strwName;

		return result;
	}

	static bool GetScenesPath(CComBSTR &strName)
	{
		CStringW strwName = strName;
		bool result = CRsFileTools::GetScenesPath( strwName);
		strName = strwName;

		return result;
	}

	static bool GetContextsPath(CComBSTR &strName)
	{
		CStringW strwName = strName;
		bool result = CRsFileTools::GetContextsPath( strwName);
		strName = strwName;

		return result;
	}

	static bool GetProjectsPath(CComBSTR &strName)
	{
		CStringW strwName = strName;
		bool result = CRsFileTools::GetProjectsPath( strwName);
		strName = strwName;

		return result;
	}

	static bool GetRecordingsPath(CComBSTR &strName)
	{
		CStringW strwName = strName;
		bool result = CRsFileTools::GetRecordingsPath( strwName);
		strName = strwName;

		return result;
	}

	static bool GetLibrariesPath(CComBSTR &strName)
	{
		CStringW strwName = strName;
		bool result = CRsFileTools::GetLibrariesPath( strwName);
		strName = strwName;

		return result;
	}

	static bool GetFilePath(CStringW file, CStringW &strPath)
	{
		const DWORD dwFileNLength = MAX_PATH+1;
		wchar_t pFileDrive[ dwFileNLength];
		wchar_t pFileDir[ dwFileNLength + 20];
		wchar_t pFileName[ dwFileNLength + 20];
        SplitPathW(file, pFileDrive, dwFileNLength, pFileDir, dwFileNLength + 20, NULL, 0, NULL, 0);

		MakePathW(pFileName, dwFileNLength + 20, pFileDrive, pFileDir, NULL, NULL);

		strPath = pFileName;

		return true; 
	}

    static bool GetFileName(CStringW strFullPath, CStringW &strFileName)
    {
        wchar_t pszFileName[_MAX_PATH] = L"";
        wchar_t pszExt[_MAX_PATH] = L"";
        SplitPathW(strFullPath, NULL, 0, NULL, 0, pszFileName, _MAX_PATH, pszExt, _MAX_PATH);

        strFileName = pszFileName;
        strFileName += pszExt;

        return true; 
    }


    static bool ReconstructIndependentPath(CStringW strFullPath, CStringW &strRelPath)
    {
        int pos = -1;

        if (((pos = strFullPath.Find(RS_LIBRARIES_FILE_DIR, 0)) == -1) &&
            ((pos = strFullPath.Find(RS_PROJECTS_FILE_DIR, 0)) == -1) &&
            ((pos = strFullPath.Find(RS_RECORDINGS_FILE_DIR, 0)) == -1) &&
            ((pos = strFullPath.Find(RS_CONTEXTS_FILE_DIR, 0)) == -1) &&
            ((pos = strFullPath.Find(RS_SCENES_FILE_DIR, 0)) == -1) &&
            ((pos = strFullPath.Find(RS_PREFERENCES_FILE_DIR, 0)) == -1) &&
            ((pos = strFullPath.Find(RS_CONFIGURATIONS_FILE_DIR, 0)) == -1) &&
            ((pos = strFullPath.Find(RS_SCRIPTS_FILE_DIR, 0)) == -1))
        {
            strRelPath = strFullPath;
            return false;
        }

        strRelPath = L"\\" + strFullPath.Mid(pos);

        return true;
    }



};

//#define RS_ROSETTA_DIALOG

#include <CommDlg.h>

class CRsBaseFileDialog
{	
	class CRsFileDialgData
	{
	public:
		CRsBaseFileDialog* GetOwnerObj(void)
		{
			return m_pRecObj;
		}

		CRsFileDialgData(CRsBaseFileDialog* pRecObj)
		{
			m_pRecObj = pRecObj;
		}

	private:
		CRsBaseFileDialog* m_pRecObj;
	};
	
public:
	
	virtual BOOL OnFileDialogTask(LPOPENFILENAME lpofn) = 0;
	BOOL GetFileName(LPOPENFILENAME lpofn);


private:
	HRESULT FileDialogFunc(CRsFileDialgData *pThreadParmsObj);
	
    /// file dialog synchronization event:
	HANDLE m_hFileDialogEvent;

    /// dialog structure passed to shell function:
	LPOPENFILENAME m_pOfn;

    /// return value:
	BOOL		   m_retValue;

	
		
	void ProcessFileDialogTask(void)
	{
		m_retValue = OnFileDialogTask(m_pOfn);

		// wake up streaming write thread to finish tasks:
		SetEvent(m_hFileDialogEvent);
	}
	
    /// entry point for dialog thread:
	static DWORD WINAPI RsFileDialogThreadFunction(void* pThreadParms)
	{
		// get thread data:
		CRsFileDialgData* pThreadParmsObj = (CRsFileDialgData*) pThreadParms;
		CRsBaseFileDialog* pDialogObj = pThreadParmsObj->GetOwnerObj();
				
		pDialogObj->ProcessFileDialogTask();
		delete pThreadParmsObj;
	
		return 0;
	}
};

inline HRESULT CRsBaseFileDialog::FileDialogFunc(CRsFileDialgData * /*pThreadParmsObj*/)
{
#ifdef RS_ROSETTA_DIALOG
    /// handle indicates that in buffer is ready contains events:
	m_hFileDialogEvent = CreateEvent(
		NULL,						// no security attributes.
		TRUE,						// manual-reset event.
		FALSE,						// initial state is non-signaled.
		"Rs::FileDialog"		// object name.
		); 

	if (m_hFileDialogEvent == NULL) 
	{ 
		// error exits:
		return E_FAIL;
	}

	// create new thread for streaming events:
	DWORD IDThread;
	HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) RsFileDialogThreadFunction, pThreadParmsObj, 0, &IDThread);

	if ( !hThread)
		return E_FAIL;

	// reset event, it will cause write event thread to wait for new events:
	ResetEvent(m_hFileDialogEvent);	


	// Wait for any message sent or posted to this queue, 
	// or for one of the passed handles being set to signaled.

	RtUlong cObjects = 1;
    MSG msg ;
    RtUlong result;

	while(true)
	{

		// Read all of the messages in this next loop, 
        // removing each message as we read it.
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
        { 
            // If it is a quit message, exit loop.
            if (msg.message == WM_QUIT)  
                return S_OK; 
            // Otherwise, dispatch the message.
            DispatchMessage(&msg); 
        } // End of PeekMessage while loop.


		result = MsgWaitForMultipleObjects(cObjects, &m_hFileDialogEvent, 
			FALSE, INFINITE, QS_ALLINPUT /*QS_ALLPOSTMESSAGE| QS_PAINT*/); 
		// The result tells us the type of event we have.
		if (result == (WAIT_OBJECT_0 + cObjects))
		{
			// New messages have arrived. 
			// Continue to the top of the always while loop to 
			// dispatch them and resume waiting.
			continue;
		}
		else
		{
			break;
		}
	}

	// close handle:
	CloseHandle(m_hFileDialogEvent);
	m_hFileDialogEvent = NULL;

	return S_OK;

#else

	m_retValue = OnFileDialogTask(m_pOfn);
	return S_OK;

#endif 
}


inline BOOL CRsBaseFileDialog::GetFileName(LPOPENFILENAME lpofn)
{
	CRsFileDialgData*  pThreadParmsObj = new CRsFileDialgData(this);
	
	// initialize dialog structure:
	m_pOfn = lpofn;

	// call function:
	FileDialogFunc(pThreadParmsObj);
	
	return m_retValue;
}

class CRsFileSaveDialog: public CRsBaseFileDialog
{
public:
	virtual BOOL OnFileDialogTask(LPOPENFILENAME lpofn)
	{
		return ::GetSaveFileName(lpofn);
	};
};


class CRsFileOpenDialog: public CRsBaseFileDialog
{
public:

	virtual BOOL OnFileDialogTask(LPOPENFILENAME lpofn)
	{
		return ::GetOpenFileName(lpofn);
	}
};



// for library ( remove later):
class CRsBaseFileDialogW
{	
	class CRsFileDialgData
	{
	public:
		CRsBaseFileDialogW* GetOwnerObj(void)
		{
			return m_pRecObj;
		}

		CRsFileDialgData(CRsBaseFileDialogW* pRecObj)
		{
			m_pRecObj = pRecObj;
		}

	private:
		CRsBaseFileDialogW* m_pRecObj;
	};
	
public:

	virtual BOOL OnFileDialogTask(LPOPENFILENAMEW lpofn) = 0;
	BOOL GetFileName(LPOPENFILENAMEW lpofn);


private:
	HRESULT FileDialogFunc(CRsFileDialgData *pThreadParmsObj);
	
    /// file dialog synchronization event:
	HANDLE m_hFileDialogEvent;

    /// dialog structure passed to shell function:
	LPOPENFILENAMEW m_pOfn;

    /// return value:
	BOOL		   m_retValue;

	
		
	void ProcessFileDialogTask(void)
	{
		m_retValue = OnFileDialogTask(m_pOfn);

		// wake up streaming write thread to finish tasks:
		SetEvent(m_hFileDialogEvent);
	}
	
    /// entry point for dialog thread:
	static DWORD WINAPI RsFileDialogThreadFunction(void* pThreadParms)
	{
		// get thread data:
		CRsFileDialgData* pThreadParmsObj = (CRsFileDialgData*) pThreadParms;
		CRsBaseFileDialogW* pDialogObj = pThreadParmsObj->GetOwnerObj();
				
		pDialogObj->ProcessFileDialogTask();
		delete pThreadParmsObj;
	
		return 0;
	}
};

inline HRESULT CRsBaseFileDialogW::FileDialogFunc(CRsFileDialgData * /*pThreadParmsObj*/)
{
#ifdef RS_ROSETTA_DIALOG
    /// handle indicates that in buffer is ready contains events:
	m_hFileDialogEvent = CreateEvent(
		NULL,						// no security attributes.
		TRUE,						// manual-reset event.
		FALSE,						// initial state is non-signaled.
		"Rs::FileDialog"		// object name.
		); 

	if (m_hFileDialogEvent == NULL) 
	{ 
		// error exit:
		return E_FAIL;
	}

	// create new thread for streaming events:
	DWORD IDThread;
	HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) RsFileDialogThreadFunction, pThreadParmsObj, 0, &IDThread);

	if ( !hThread)
		return E_FAIL;

	// reset event, it will cause write event thread to wait for new events:
	ResetEvent(m_hFileDialogEvent);	


	// Wait for any message sent or posted to this queue,  
	// or for one of the passed handles being set to signaled.

	RtUlong cObjects = 1;
    MSG msg ;
    RtUlong result;

	while(true)
	{

		// Read all of the messages in this next loop, 
        // removing each message as we read it.
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
        { 
            // If it is a quit message, exit loop.
            if (msg.message == WM_QUIT)  
                return S_OK; 
            // Otherwise, dispatch the message.
            DispatchMessage(&msg); 
        } // End of PeekMessage while loop.


		result = MsgWaitForMultipleObjects(cObjects, &m_hFileDialogEvent, 
			FALSE, INFINITE, QS_ALLINPUT /*QS_ALLPOSTMESSAGE| QS_PAINT*/); 
		// The result tells us the type of event we have.
		if (result == (WAIT_OBJECT_0 + cObjects))
		{
			// New messages have arrived. 
			// Continue to the top of the always while loop, to 
			// dispatch them and resume waiting.
			continue;
		}
		else
		{
			break;
		}
	}

	// close handle:
	CloseHandle(m_hFileDialogEvent);
	m_hFileDialogEvent = NULL;

	return S_OK;

#else

	m_retValue = OnFileDialogTask(m_pOfn);
	return S_OK;

#endif 
}


inline BOOL CRsBaseFileDialogW::GetFileName(LPOPENFILENAMEW lpofn)
{
	CRsFileDialgData*  pThreadParmsObj = new CRsFileDialgData(this);
	
	// initialize dialog structure:
	m_pOfn = lpofn;

	// call function:
	FileDialogFunc(pThreadParmsObj);
	
	return m_retValue;
}

class CRsFileSaveDialogW: public CRsBaseFileDialogW
{
public:
	virtual BOOL OnFileDialogTask(LPOPENFILENAMEW lpofn)
	{
		return ::GetSaveFileNameW(lpofn);
	};
};


class CRsFileOpenDialogW: public CRsBaseFileDialogW
{
public:

	virtual BOOL OnFileDialogTask(LPOPENFILENAMEW lpofn)
	{
		return ::GetOpenFileNameW(lpofn);
	}
};

