/*****************************************************************************
Open Source MIT License

Copyright (c) 2017 Matthew Ahmed

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*****************************************************************************/

// Util.h
// Make available to WinMain etc., i.e. outside ZWin class

#pragma once

// CSettings has conditional code for portability
#if !defined UTIL_H_
#define UTIL_H_
#endif

#define UTIL_MAX_ERROR 2047

#define UTL_NML_MIN   1
#define UTL_NML_MED   2
#define UTL_NML_MAX   3

// By do rand() twice, get max ((RAND_MAX << 15) + RAND_MAX) 30 bits  0x3FFFFFFF
#define RAND_MAX_BIG  1073741823

// To util.cpp so not globally visible: TCHAR sgUtilError[UTIL_MAX_ERROR + 1];
BOOL UtilSetError(LPCTSTR sMsg1, LPCTSTR sMsg2 = NULL, LPCTSTR sMsg3 = NULL,
					        LPCTSTR sMsg4 = NULL, LPCTSTR sMsg5 = NULL, LPCTSTR sMsg6 = NULL);
BOOL UtilSetSysError(LPCTSTR sMsg1, LPCTSTR sMsg2 = NULL, LPCTSTR sMsg3 = NULL,
					           LPCTSTR sMsg4 = NULL, LPCTSTR sMsg5 = NULL);
BOOL UtilGetError(LPTSTR sTarget, BOOL bClear = TRUE);
LPCTSTR UtilError(BOOL bClearError = TRUE);
void UtilClearError();

BOOL NewStrRaw(LPTSTR & spNew, BOOL bAddRN, INT nExtra, INT nStr, ...);

inline BOOL NewStr(LPTSTR & spNew, BOOL bAddRN, LPCTSTR s1, LPCTSTR s2 = NULL,
											LPCTSTR s3 = NULL, LPCTSTR s4 = NULL, LPCTSTR s5 = NULL,
												LPCTSTR s6 = NULL, LPCTSTR s7 = NULL,
													LPCTSTR s8 = NULL, LPCTSTR s9 = NULL)
{	
	return NewStrRaw(spNew, bAddRN, 0, 9, s1, s2, s3, s4, s5, s6, s7, s8, s9);
}

inline BOOL NewStr(LPTSTR & spNew, LPCTSTR s1, LPCTSTR s2 = NULL,
											LPCTSTR s3 = NULL, LPCTSTR s4 = NULL, LPCTSTR s5 = NULL,
												LPCTSTR s6 = NULL, LPCTSTR s7 = NULL,
													LPCTSTR s8 = NULL, LPCTSTR s9 = NULL)
{	
	return NewStrRaw(spNew, FALSE, 0, 9, s1, s2, s3, s4, s5, s6, s7, s8, s9);
}

INT MsgB(HWND hWnd, LPCTSTR sCapt, LPCTSTR sMsg1, LPCTSTR sMsg2 = NULL,
					LPCTSTR sMsg3 = NULL, LPCTSTR sMsg4 = NULL, LPCTSTR sMsg5 = NULL,
						LPCTSTR sMsg6 = NULL, UINT uType = MB_OK);

inline INT MsgB(LPCTSTR sMsg1, LPCTSTR sMsg2 = NULL, LPCTSTR sMsg3 = NULL,
					     LPCTSTR sMsg4 = NULL, LPCTSTR sMsg5 = NULL,
						    LPCTSTR sMsg6 = NULL, UINT uType = MB_OK)
{
  return MsgB(NULL, NULL, sMsg1, sMsg1, sMsg1, sMsg1, sMsg1, sMsg1, MB_OK);
}

BOOL fnError(HWND hWnd, LPCTSTR sCapt, LPCTSTR sErr1, LPCTSTR sErr2 = NULL,
							LPCTSTR sErr3 = NULL, LPCTSTR sErr4 = NULL,
								LPCTSTR sErr5 = NULL, LPCTSTR sErr6 = NULL);

BOOL GetSysErrorMsg(LPTSTR sTarget, DWORD dwErrId = 0);

BOOL fnSysError(HWND hWnd, LPCTSTR sCapt, LPCTSTR sErr1, LPCTSTR sErr2 = NULL,
									LPCTSTR sErr3 = NULL, LPCTSTR sErr4 = NULL);
BOOL fnSysError(DWORD dwErr, HWND hWnd, LPCTSTR sCapt, LPCTSTR sErr1,
								LPCTSTR sErr2 = NULL,	LPCTSTR sErr3 = NULL, LPCTSTR sErr4 = NULL);

BOOL WriteStrToFile(LPCTSTR sMsg, LPCTSTR sPath, BOOL bAppend = TRUE,
                    BOOL bAddRN = TRUE, BOOL bFlush = FALSE);

BOOL EnsureDirExists(LPCTSTR sDirPath);
BOOL EnsureContainingDirExists(LPCTSTR sFilePath);

LPCTSTR TimeStamp();

// Compare function for qsort reorder of INT array, e.g.:
// qsort((void *) nArray, (size_t) nArrSize, sizeof(INT), QsortCompareInt);
inline int QsortCompareInt(const void *arg1, const void *arg2)
{
   return *(INT*) arg1 - *(INT*) arg2;
}

// Compare function for qsort reorder of  array of LPTSTR, e.g.:
// qsort((void *) spArray, (size_t) nArrSize, sizeof(LPTSTR), QsortCompareLptstr);
inline int QsortCompareLptstr(const void *arg1, const void *arg2)
{
   /* Compare all of both strings: */
   return _tcscmp(*(LPTSTR*) arg1, *(LPTSTR*) arg2 );
}


/*
template <class T> BOOL sdelete(T * & ptr)
{
	if (!ptr) return FALSE;

	delete [] ptr;
	ptr = NULL;
	return TRUE;
}
*/

//void DeleteStringArray(LPTSTR * & sppArr, INT nArrSize);
//BOOL AllocateStringArray(LPTSTR * & sppArr, INT nArrSize,
//                         INT nMemberLen, LPTSTR sError);

BOOL StrCpyWhatFits(LPTSTR sTarget, INT nMaxCharsTarget, LPCTSTR sSource);
BOOL StrCatWhatFits(LPTSTR sTarget, INT nMaxCharsTarget,
                    LPCTSTR sSource, BOOL bInsertLineBreak = TRUE);

INT StrCatArrToStr(LPTSTR sTarget, INT nMaxCharsTarget,
                    LPCTSTR * sSrcArr, INT nArrSize, LPCTSTR sSep = NULL);

UINT GetRandomUint(UINT uMaxResult, LPTSTR sRetMsg = NULL);

BOOL ParsePath(LPCTSTR sPath, LPTSTR sDirSlash, LPTSTR sNameNoExt,
               LPTSTR sDotExt, BOOL bRequireExt = TRUE);

BOOL ParsePath(LPCTSTR sPath, LPTSTR sDirSlash, LPTSTR sFullName,
               BOOL bRequireExt = TRUE);

// COMMENT OUT THESE TWO LATER
//BOOL GetOwnPathParsed(LPTSTR sDirSlash, LPTSTR sNameNoExt, LPTSTR sDotExt);
//BOOL GetOwnDirSlash(LPTSTR sDirSlash);

/// Replace both GetOwn... above with renamed to force errors to replace w appdata dir etc
BOOL GetProgPathParsed(LPTSTR sDirSlash, LPTSTR sNameNoExt, LPTSTR sDotExt);
//BOOL GetProgDirSlash(LPTSTR sDirSlash);
// Dirs in user profile to use instead of Prog Dir, to work with Win7 UAC
BOOL GetAppPathsInfo(LPTSTR sProgDirSlash, LPTSTR sDataDirSlash,
                     LPTSTR sDocsDirSlash, LPTSTR sExeNameNoDotExt,
                     LPCTSTR sSubfolderName = NULL, BOOL bProgForAllIfUser = TRUE);

// Cloned from ZWin
BOOL ReadReg(HKEY hOpen, LPCTSTR sKey, LPCTSTR sValue,
						 LPTSTR sTarget, DWORD dwSize = 260);

void StrCatMult(LPTSTR sTarget, LPCTSTR sCat1, LPCTSTR sCat2,
        LPCTSTR sCat3 = NULL, LPCTSTR sCat4 = NULL, LPCTSTR sCat5 = NULL);

BOOL RectanglesOverlap(RECT* pr1, RECT* pr2);

// These are OK when Char Set is UTF-8, not useful now with UTF-16
//char* Utf8toAnsi( const char * utf8, int len );
//void Utf8ToAnsi(LPCTSTR sSource, LPSTR sTargetAnsi); // Wrapper for above
//BOOL TcharToWchar(LPCTSTR sSourceT, LPWSTR sTargetW);

BOOL Utf16ToAnsi(LPCWSTR swSource, LPSTR sTarget);
BOOL Utf16ToUtf8(LPCWSTR swSource, LPSTR sTarget);

// These date arithmetic functions depend on time.h in Headers.h
INT IntYYYYMMDD_to_N_DaysAgo(INT nYYYYMMDD);
INT N_DaysAgo_to_IntYYYYMMDD(INT nDaysAgo);

////// BEGIN REQUIRE shlwapi.h AND ADD shlwapi.lib TO LINK COMMAND LINE /////
#if defined(_INC_SHLWAPI)

TCHAR LatinComponentForLower(const TCHAR c);
LPCTSTR Normalized(LPCTSTR sSource, LPTSTR sTarget, INT nHowMuch);
LPCTSTR NormalizedAlpha(LPCTSTR sSource, LPTSTR sTarget, BOOL bDecompose = TRUE);

// Even param strings that don't get changed can't be LPCTSTR:
BOOL GetStrBetween(LPTSTR sSource, LPCTSTR sBefore, LPCTSTR sAfter,
                   LPTSTR sTarget = NULL, BOOL bCaseSensitive = FALSE);

// Even param strings that don't get changed can't be LPCTSTR:
BOOL ReplaceStrBetween(LPTSTR sSource, LPCTSTR sBefore, LPCTSTR sAfter,
           LPTSTR sInsert, LPTSTR sTarget = NULL, BOOL bCaseSensitive = FALSE);

inline BOOL ReplaceStrBefore(LPTSTR sSource, LPCTSTR sTossBefore, LPTSTR sInsert,
                       LPTSTR sTarget = NULL, BOOL bCaseSensitive = FALSE)
{
  return ReplaceStrBetween(sSource, NULL, sTossBefore, sInsert,
                           sTarget, bCaseSensitive);
}

inline BOOL ReplaceStrAfter(LPTSTR sSource, LPCTSTR sTossAfter, LPTSTR sInsert,
                       LPTSTR sTarget = NULL, BOOL bCaseSensitive = FALSE)
{
  return ReplaceStrBetween(sSource, sTossAfter, NULL, sInsert,
                           sTarget, bCaseSensitive);
}

BOOL ReplaceSubStr(LPTSTR sSource, LPCTSTR sSubStr, LPCTSTR sReplacement,
                   LPTSTR sTarget = NULL, BOOL bReplFirstOnly = FALSE,
                   BOOL bCaseSensitive = FALSE);

// Same as above except returns TRUE if not found, unless error
BOOL ReplaceSubStrIfThere(LPTSTR sSource, LPCTSTR sSubStr, LPCTSTR sReplacement,
                          LPTSTR sTarget = NULL, BOOL bReplFirstOnly = FALSE,
                          BOOL bCaseSensitive = FALSE);

#endif // _INC_SHLWAPI
