// encoding: UTF-8
/******************************************************************************
*                                                                             *
*                                                                             *
* Notepad3                                                                    *
*                                                                             *
* Helpers.h                                                                   *
*   Definitions for general helper functions and macros                       *
*   Based on code from Notepad2, (c) Florian Balmer 1996-2011                 *
*                                                                             *
*                                                  (c) Rizonesoft 2008-2022   *
*                                                    https://rizonesoft.com   *
*                                                                             *
*                                                                             *
*******************************************************************************/
#pragma once
#ifndef _NP3_HELPERS_H_
#define _NP3_HELPERS_H_

#include "TypeDefs.h"

#include <heapapi.h>
#include <process.h>
#include <math.h>
#include <shlwapi.h>
#include <VersionHelpers.h>

#include "Scintilla.h"

// ============================================================================
// ---  Disable/Enable some CodeAnalysis Warnings  ---

#pragma warning ( disable: 26451 28159)
//#pragma warning ( enable : 6001 )

// ============================================================================

// ============================================================================

#ifndef _W
#define __CC(p,s) p ## s
#define _W(s)  __CC(L,s)
#endif

#ifndef _STRG
#define _STRINGIFY(s) #s
#define _STRG(s)  _STRINGIFY(s)
#endif

#define SIZEOF(ar) sizeof(ar)
//#define ARRAYSIZE(A) (assert(!(sizeof(A) % sizeof(*(A)))), (sizeof(A) / sizeof((A)[0])))
#define COUNTOF(ar) ARRAYSIZE(ar)
#define CONSTSTRGLEN(s)  (COUNTOF(s)-1)

#define NOOP ((void)0)

// ============================================================================

#if (defined(_DEBUG) || defined(DEBUG)) && !defined(NDEBUG)
#ifndef _DEBUG
#define _DEBUG 1
#endif
#ifndef DEBUG
#define DEBUG 1
#endif
inline void* reallocz(void* pMem, size_t numBytes) { void* pM = realloc(pMem, numBytes); if (pM) memset(pM, 0, numBytes); return pM; }
#define AllocMem(B, F) (((F)&HEAP_ZERO_MEMORY) ? calloc(1, B) : malloc(B))
#define ReAllocMem(M, B, F) ((M) ? (((F)&HEAP_ZERO_MEMORY) ? reallocz(M, B) : realloc(M, B)) : AllocMem(B, F))
#define ReAllocGrowMem(M, B, F) ((M) ? ((_msize(M) >= (B)) ? (((F)&HEAP_ZERO_MEMORY) ? (memset(M, 0, B), (M)) : (M)) : (((F)&HEAP_ZERO_MEMORY) ? reallocz(M, B) : realloc(M, B))) : AllocMem(B, F))
#define FreeMem(M) ((M ? (free(M)) : NOOP), true)
#define SizeOfMem(M) ((M) ? _msize(M) : 0)

#else  // RELASE VERSION

// direct heap allocation
#if (defined(_DEBUG) || defined(DEBUG)) && !defined(NDEBUG)
#define DEFAULT_ALLOC_FLAGS (HEAP_GENERATE_EXCEPTIONS | HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED | HEAP_CREATE_HARDENED)
#else
#define DEFAULT_ALLOC_FLAGS (HEAP_CREATE_HARDENED)
#endif

inline LPVOID AllocMem(size_t numBytes, DWORD dwFlags)
{
    return HeapAlloc(Globals.hndlProcessHeap, (dwFlags | DEFAULT_ALLOC_FLAGS), numBytes);
}

inline LPVOID ReAllocMem(LPVOID lpMem, size_t numBytes, DWORD dwFlags)
{
    if (lpMem) {
        return HeapReAlloc(Globals.hndlProcessHeap, (dwFlags | DEFAULT_ALLOC_FLAGS), lpMem, numBytes);
    }
    return HeapAlloc(Globals.hndlProcessHeap, (dwFlags | DEFAULT_ALLOC_FLAGS), numBytes);
}

inline LPVOID ReAllocGrowMem(LPVOID lpMem, size_t numBytes, DWORD dwFlags)
{
    if (lpMem) {
        size_t const memSize = HeapSize(Globals.hndlProcessHeap, 0, lpMem);
        if (memSize >= numBytes) {
            if (dwFlags & HEAP_ZERO_MEMORY) {
                SecureZeroMemory(lpMem, memSize);
            }
            return lpMem;
        }
        return HeapReAlloc(Globals.hndlProcessHeap, (dwFlags | DEFAULT_ALLOC_FLAGS), lpMem, numBytes);
    }
    return HeapAlloc(Globals.hndlProcessHeap, (dwFlags | DEFAULT_ALLOC_FLAGS), numBytes);
}

inline bool FreeMem(LPVOID lpMem)
{
    return (lpMem ? HeapFree(Globals.hndlProcessHeap, 0, lpMem) : true);
}

inline size_t SizeOfMem(LPCVOID lpMem)
{
    return (lpMem ? HeapSize(Globals.hndlProcessHeap, 0, lpMem) : 0);
}

#endif

// ============================================================================

#if (defined(_DEBUG) || defined(DEBUG)) && !defined(NDEBUG)
void DbgLog(const char *fmt, ...);
#else
#define DbgLog(fmt, ...) NOOP
#endif

// ============================================================================

// min/max
#define _min_(x,y) (((x) > (y)) ? (y) : (x))
#define _RETCMPMIN_  { return (x > y) ? y : x; }
__forceinline int min_i(const int x, const int y) _RETCMPMIN_
__forceinline unsigned int min_u(const unsigned int x, const unsigned int y) _RETCMPMIN_
__forceinline long min_l(const long x, const long y) _RETCMPMIN_
__forceinline long long min_ll(const long long x, const long long y) _RETCMPMIN_
__forceinline long min_dw(const DWORD x, const DWORD y) _RETCMPMIN_
__forceinline size_t min_s(const size_t x, const size_t y) _RETCMPMIN_
__forceinline DocPos min_p(const DocPos x, const DocPos y) _RETCMPMIN_
__forceinline DocLn min_ln(const DocLn x, const DocLn y) _RETCMPMIN_
__forceinline DocPosCR min_cr(const DocPosCR x, const DocPosCR y) _RETCMPMIN_
__forceinline float min_f(float x, float y) _RETCMPMIN_

#define _max_(x,y) (((x) < (y)) ? (y) : (x))
#define _RETCMPMAX_  { return (x < y) ? y : x; }
__forceinline int max_i(int x, int y) _RETCMPMAX_
__forceinline unsigned int max_u(unsigned int x, unsigned int y) _RETCMPMAX_
__forceinline long max_l(const long x, const long y) _RETCMPMAX_
__forceinline long long max_ll(const long long x, const long long y) _RETCMPMAX_
__forceinline long max_dw(const DWORD x, const DWORD y) _RETCMPMAX_
__forceinline size_t max_s(const size_t x, const size_t y) _RETCMPMAX_
__forceinline DocPos max_p(const DocPos x, const DocPos y) _RETCMPMAX_
__forceinline DocLn max_ln(const DocLn x, const DocLn y) _RETCMPMAX_
__forceinline DocPosCR max_cr(const DocPosCR x, const DocPosCR y) _RETCMPMAX_
__forceinline float max_f(float x, float y) _RETCMPMAX_

__forceinline DocPos abs_p(const DocPos x) {
    return (x >= 0LL) ? x : (0LL - x);
}

// swap
__forceinline void swapi(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}
__forceinline void swapos(DocPos *a, DocPos *b) {
    DocPos t = *a;
    *a = *b;
    *b = t;
}

// clamp
__forceinline int clampi(int x, int lower, int upper) {
    return (x < lower) ? lower : ((x > upper) ? upper : x);
}

__forceinline unsigned clampu(unsigned x, unsigned lower, unsigned upper) {
    return (x < lower) ? lower : ((x > upper) ? upper : x);
}

__forceinline unsigned clampul(unsigned long x, unsigned long lower, unsigned long upper) {
    return (x < lower) ? lower : ((x > upper) ? upper : x);
}

__forceinline DocPos clampp(DocPos x, DocPos lower, DocPos upper) {
    return (x < lower) ? lower : ((x > upper) ? upper : x);
}

__forceinline DocPosU clamppu(DocPosU x, DocPosU lower, DocPosU upper) {
    return (x < lower) ? lower : ((x > upper) ? upper : x);
}

__forceinline int float2int(const float f) {
    return (int)lroundf(f);
}
__forceinline float Round10th(const float f) {
    return (float)float2int(f * 10.0f) / 10;
}
__forceinline bool HasNonZeroFraction(const float f) {
    return ((float2int(f * 10.0f) % 10) != 0);
}

__forceinline bool IsKeyDown(int key) {
    return (((GetKeyState(key) >> 8) & 0xff) != 0);
}
__forceinline bool IsAsyncKeyDown(int key) {
    return (((GetAsyncKeyState(key) >> 8) & 0xff) != 0);
}

// ----------------------------------------------------------------------------

inline DWORD GetNumberOfProcessors()
{
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    return sysinfo.dwNumberOfProcessors;
}

// ----------------------------------------------------------------------------

inline bool Char2Int(LPCWSTR str, int *value) {
    LPWSTR end;
    *value = (int)wcstol(str, &end, 10);
    return (str != end);
}
bool StrToFloat(WCHAR *wnumber, float *fresult);
void FloatToStr(float fValue, LPWSTR lpszStrg, int cchSize);

// ----------------------------------------------------------------------------

#define RGB_SUB(X, Y) (((X) > (Y)) ? ((X) - (Y)) : ((Y) - (X)))

__forceinline COLORREF CalcContrastColor(COLORREF rgb, int alpha) {

    bool const mask = RGB_SUB(MulDiv(rgb >> 0, alpha, SC_ALPHA_OPAQUE) & SC_ALPHA_OPAQUE, 0x80) <= 0x20 &&
                      RGB_SUB(MulDiv(rgb >> 8, alpha, SC_ALPHA_OPAQUE) & SC_ALPHA_OPAQUE, 0x80) <= 0x20 &&
                      RGB_SUB(MulDiv(rgb >> 16, alpha, SC_ALPHA_OPAQUE) & SC_ALPHA_OPAQUE, 0x80) <= 0x20;

    return mask ? ((0x7F7F7F + rgb)) & 0xFFFFFF : (rgb ^ 0xFFFFFF);
}


__forceinline COLORREF AdjustColor(COLORREF rgb, int amount) {

    return RGB(clampu(GetRValue(rgb) + amount, 0, 0xFF),
               clampu(GetGValue(rgb) + amount, 0, 0xFF),
               clampu(GetBValue(rgb) + amount, 0, 0xFF));
}


__forceinline void ColorToHtmlCode(COLORREF rgb, LPWSTR strg, size_t count) {
    StringCchPrintf(strg, count, L"#%02X%02X%02X", (int)GetRValue(rgb), (int)GetGValue(rgb), (int)GetBValue(rgb));
}

// ----------------------------------------------------------------------------

#define SendWMCommandEx(hwnd, id, hi)  SendMessage((hwnd), WM_COMMAND, MAKEWPARAM((id), (hi)), 0)
#define SendWMCommand(hwnd, id)        SendWMCommandEx((hwnd), (id), 1)
#define PostWMCommandEx(hwnd, id, hi)  PostMessage((hwnd), WM_COMMAND, MAKEWPARAM((id), (hi)), 0)
#define PostWMCommand(hwnd, id)        PostWMCommandEx((hwnd), (id), 1)

#define SetWindowStyle(hwnd, style)			  SetWindowLong((hwnd), GWL_STYLE, (style))
#define SetWindowExStyle(hwnd, style)     SetWindowLong((hwnd), GWL_EXSTYLE, (style))

//==== StrIs(Not)Empty() =============================================

__forceinline bool StrIsEmptyA(LPCSTR s) {
    return (!s || (*s == '\0'));
}
__forceinline bool StrIsEmptyW(LPCWSTR s) {
    return (!s || (*s == L'\0'));
}

#if defined(UNICODE) || defined(_UNICODE)
#define StrIsEmpty(s)     StrIsEmptyW(s)
#define StrIsNotEmpty(s)  (!StrIsEmptyW(s))
#else
#define StrIsEmpty(s)     StrIsEmptyA(s)
#define StrIsNotEmpty(s)  (!StrIsEmptyA(s))
#endif

// ----------------------------------------------------------------------------

//inline COLORREF GetBackgroundColor(HWND hwnd) { return GetBkColor(GetDC(hwnd)); }


inline int SetModeBkColor(const HDC hdc, const bool bDarkMode)
{
#ifdef D_NP3_WIN10_DARK_MODE
    return SetBkColor(hdc, bDarkMode ? Settings2.DarkModeBkgColor : GetSysColor(COLOR_WINDOW));
#else
    UNREFERENCED_PARAMETER(bDarkMode);
    return SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
#endif
}

inline int SetModeBtnFaceColor(const HDC hdc, const bool bDarkMode)
{
#ifdef D_NP3_WIN10_DARK_MODE
    return SetBkColor(hdc, bDarkMode ? Settings2.DarkModeBtnFaceColor : GetSysColor(COLOR_BTNFACE));
#else
    UNREFERENCED_PARAMETER(bDarkMode);
    return SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
#endif
}

inline COLORREF GetModeBkColor(const bool bDarkMode)
{
#ifdef D_NP3_WIN10_DARK_MODE
    return bDarkMode ? Settings2.DarkModeBkgColor : (COLORREF)GetSysColor(COLOR_WINDOW);
#else
    UNREFERENCED_PARAMETER(bDarkMode);
    return (COLORREF)GetSysColor(COLOR_WINDOW);
#endif
}

inline COLORREF GetModeBtnfaceColor(const bool bDarkMode)
{
#ifdef D_NP3_WIN10_DARK_MODE
    return bDarkMode ? Settings2.DarkModeBtnFaceColor : (COLORREF)GetSysColor(COLOR_BTNFACE);
#else
    UNREFERENCED_PARAMETER(bDarkMode);
    return (COLORREF)GetSysColor(COLOR_BTNFACE);
#endif
}


inline int SetModeTextColor(const HDC hdc, const bool bDarkMode)
{
#ifdef D_NP3_WIN10_DARK_MODE
    return SetTextColor(hdc, bDarkMode ? Settings2.DarkModeTxtColor : GetSysColor(COLOR_WINDOWTEXT));
#else
    UNREFERENCED_PARAMETER(bDarkMode);
    return SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
#endif
}

inline COLORREF GetModeTextColor(const bool bDarkMode)
{
#ifdef D_NP3_WIN10_DARK_MODE
    return bDarkMode ? Settings2.DarkModeTxtColor : (COLORREF)GetSysColor(COLOR_WINDOWTEXT);
#else
    UNREFERENCED_PARAMETER(bDarkMode);
    return (COLORREF)GetSysColor(COLOR_WINDOWTEXT);
#endif
}


#ifdef D_NP3_WIN10_DARK_MODE

inline INT_PTR SetDarkModeCtlColors(const HDC hdc, const bool bDarkMode)
{
    if (bDarkMode) {
        SetBkColor(hdc, Settings2.DarkModeBkgColor);
        SetTextColor(hdc, Settings2.DarkModeTxtColor);
    }
    return (INT_PTR)(bDarkMode ? Globals.hbrDarkModeBkgBrush : FALSE);
}

#endif

// ----------------------------------------------------------------------------

void GetWinVersionString(LPWSTR szVersionStr, size_t cchVersionStr);

// ----------------------------------------------------------------------------

#define GET_X_LPARAM(lp)    ((int)(short)LOWORD(lp))  // windowsx.h
#define GET_Y_LPARAM(lp)    ((int)(short)HIWORD(lp))  // windowsx.h

// ----------------------------------------------------------------------------

bool SetClipboardText(HWND hwnd, LPCWSTR pszTextW, size_t cchTextW);

// ----------------------------------------------------------------------------

inline void GetCurrentMonitorResolution(HWND hwnd, int* pCXScreen, int* pCYScreen)
{
    HMONITOR const hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
    MONITORINFO mi = { sizeof(MONITORINFO) };
    GetMonitorInfo(hMonitor, &mi);
    *pCXScreen = (mi.rcMonitor.right - mi.rcMonitor.left);
    *pCYScreen = (mi.rcMonitor.bottom - mi.rcMonitor.top);
}

// FullHD? =>   0:'==',   -1:'<',   +1:'>'
inline int IsFullHD(HWND hwnd, int resX, int resY)
{
    int cxScreen, cyScreen;
    GetCurrentMonitorResolution(hwnd, &cxScreen, &cyScreen);
    if (resX <= 0) {
        resX = cxScreen;
    }
    if (resY <= 0) {
        resY = cyScreen;
    }
    return ((resX == 1920) && (resY == 1080)) ? 0 : (((resX < 1920) || (resY < 1080)) ? -1 : +1);
}

// ----------------------------------------------------------------------------

HRESULT PrivateSetCurrentProcessExplicitAppUserModelID(PCWSTR AppID);

bool IsProcessElevated();
//bool IsUserAdmin();
bool IsUserInAdminGroup();
bool IsRunAsAdmin();

typedef struct BackgroundWorker {
    HWND hwnd;
    HANDLE eventCancel;
    HANDLE workerThread;
    HPATHL hFilePath; // PATHLONG_MAX_CCH
} BackgroundWorker;

void BackgroundWorker_Init(BackgroundWorker* worker, HWND hwnd, HPATHL hFilePath);
void BackgroundWorker_Start(BackgroundWorker* worker, _beginthreadex_proc_type routine, LPVOID property);
void BackgroundWorker_End(unsigned int retcode);
void BackgroundWorker_Cancel(BackgroundWorker *worker);
void BackgroundWorker_Destroy(BackgroundWorker *worker);
__forceinline bool BackgroundWorker_Continue(BackgroundWorker* worker) { return (WaitForSingleObject(worker->eventCancel, 0) != WAIT_OBJECT_0); }

bool BitmapMergeAlpha(HBITMAP hbmp,COLORREF crDest);
bool BitmapAlphaBlend(HBITMAP hbmp,COLORREF crDest,BYTE alpha);
bool BitmapGrayScale(HBITMAP hbmp);

bool VerifyContrast(COLORREF cr1,COLORREF cr2);
bool IsFontAvailable(LPCWSTR lpszFontName);

bool IsCmdEnabled(HWND hwnd, UINT uId);


#define SetBtn(b) ((b) ? BST_CHECKED : BST_UNCHECKED)

inline bool IsButtonChecked(HWND hwnd, int iButtonID)
{
    return (IsDlgButtonChecked(hwnd, iButtonID) == BST_CHECKED);
}
inline bool IsButtonIntermediate(HWND hwnd, int iButtonID)
{
    return (IsDlgButtonChecked(hwnd, iButtonID) == BST_INDETERMINATE);
}
inline bool IsButtonUnchecked(HWND hwnd, int iButtonID)
{
    return (IsDlgButtonChecked(hwnd, iButtonID) == BST_UNCHECKED);
}

#define EnableItem(hwnd, id, b) EnableWindow(GetDlgItem((hwnd), (id)), (b))

#define EnableCmd(hmenu,id,b) EnableMenuItem((hmenu),(id),(b)?MF_BYCOMMAND|MF_ENABLED:MF_BYCOMMAND|MF_GRAYED)
#define CheckCmd(hmenu,id,b)  CheckMenuItem((hmenu),(id),(b)?MF_BYCOMMAND|MF_CHECKED:MF_BYCOMMAND|MF_UNCHECKED)

#define EnableTool(htbar,id,b) SendMessage((htbar),TB_ENABLEBUTTON,(id), MAKELONG(((b) ? 1 : 0), 0))
#define CheckTool(htbar,id,b)  SendMessage((htbar),TB_CHECKBUTTON,(id), MAKELONG((b),0))

#define EnableCmdPos(hmenu,pos,b) EnableMenuItem((hmenu),(pos),(b)?MF_BYPOSITION|MF_ENABLED:MF_BYPOSITION|MF_GRAYED)
#define CheckCmdPos(hmenu,pos,b)  CheckMenuItem((hmenu),(pos),(b)?MF_BYPOSITION|MF_CHECKED:MF_BYPOSITION|MF_UNCHECKED)


bool ReadFileXL(HANDLE hFile, char* const lpBuffer, const size_t nNumberOfBytesToRead, size_t* const lpNumberOfBytesRead);
bool WriteFileXL(HANDLE hFile, const char* const lpBuffer, const size_t nNumberOfBytesToWrite, size_t* const lpNumberOfBytesWritten);

bool  SplitFilePathLineNum(LPWSTR lpszPath, int *lineNum);

bool StrLTrimI(LPWSTR pszSource,LPCWSTR pszTrimChars);
bool StrRTrimI(LPWSTR pszSource,LPCWSTR pszTrimChars);

inline bool TrimSpcA(LPSTR lpString)
{
    if (!lpString || !*lpString) {
        return false;
    }
    return (bool)StrTrimA(lpString, " \t\v");
};

inline bool TrimSpcW(LPWSTR lpString)
{
    if (!lpString || !*lpString) {
        return false;
    }
    return (bool)StrTrim(lpString, L" \t\v");
};

#if (defined(UNICODE) || defined(_UNICODE))
#define TrimSpc TrimSpcW
#else
#define TrimSpc TrimSpcA
#endif

// return memory ownership: use FreeMem() to delete returned string
LPWSTR StrReplaceAll(LPCWSTR str, LPCWSTR from, LPCWSTR to);

bool ExtractFirstArgument(LPCWSTR lpArgs, LPWSTR lpArg1, LPWSTR lpArg2, int len);

void PrepareFilterStr(LPWSTR lpFilter);

void StrTab2Space(LPWSTR lpsz);
void PathFixBackslashes(LPWSTR lpsz);


size_t FormatNumberStr(LPWSTR lpNumberStr, size_t cch, int fixedWidth);
bool SetDlgItemIntEx(HWND hwnd,int nIdItem,UINT uValue);

///////////////////////////////////////////////////////////////////////
///  UINT  GDI CHARSET values  ==  Scintilla's  SC_CHARSET_XXX values
///////////////////////////////////////////////////////////////////////
#define GdiCharsetToSci(charset) ((int)(charset))
UINT CodePageFromCharSet(const UINT uCharSet);
//~UINT CharSetFromCodePage(const UINT uCodePage);


//==== UnSlash Functions ======================================================

size_t UnSlashA(LPSTR pchInOut, UINT cpEdit);
size_t UnSlashW(LPWSTR pchInOut, UINT cpEdit);

size_t UnSlashCharW(LPWSTR pchInOut, WCHAR wch);

size_t SlashCtrlW(LPWSTR pchOutput, size_t cchOutLen, LPCWSTR pchInput);
size_t UnSlashCtrlW(LPWSTR pchInOut);

void TransformBackslashesA(LPSTR pszInput, bool bRegEx, UINT cpEdit, int *iReplaceMsg);
void TransformBackslashesW(LPWSTR pszInput, bool bRegEx, UINT cpEdit, int *iReplaceMsg);
//void TransformMetaChars(char *pszInput, size_t cch, bool bRegEx, int iEOLMode);


//==== Large Text Conversion ==================================================

#undef WC2MB_EX
#undef MB2WC_EX

#ifdef WC2MB_EX
ptrdiff_t WideCharToMultiByteEx(
    UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, ptrdiff_t cchWideChar,
    LPSTR lpMultiByteStr, ptrdiff_t cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
#else

__forceinline ptrdiff_t WideCharToMultiByteEx(
    UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, ptrdiff_t cchWideChar,
    LPSTR lpMultiByteStr, ptrdiff_t cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar)
{
    return (ptrdiff_t)WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, (int)cchWideChar,
                                          lpMultiByteStr, (int)cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
}

#endif


#ifdef MB2WC_EX
ptrdiff_t MultiByteToWideCharEx(
    UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, ptrdiff_t cbMultiByte,
    LPWSTR lpWideCharStr, ptrdiff_t cchWideChar);
#else

__forceinline ptrdiff_t MultiByteToWideCharEx(
    UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, ptrdiff_t cbMultiByte,
    LPWSTR lpWideCharStr, ptrdiff_t cchWideChar)
{
    return (ptrdiff_t)MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, (int)cbMultiByte,
                                          lpWideCharStr, (int)cchWideChar);
}

#endif

// ============================================================================

inline int32_t bitmask32_n(unsigned short n)
{
    return ((n >= 32) ? 0 - ((int32_t)1) : (((int32_t)1) << n) - 1);
}

// ============================================================================

inline int wcscmp_s(const wchar_t* s1, const wchar_t* s2)
{
    return (s1 && s2) ? wcscmp(s1, s2) : ((s1 ? 1 : (s2 ? -1 : 0)));
}

inline int wcscoll_s(const wchar_t* s1, const wchar_t* s2)
{
    return (s1 && s2) ? wcscoll(s1, s2) : ((s1 ? 1 : (s2 ? -1 : 0)));
}

inline int wcsicmp_s(const wchar_t* s1, const wchar_t* s2)
{
    return (s1 && s2) ? _wcsicmp(s1, s2) : ((s1 ? 1 : (s2 ? -1 : 0)));
}

inline int wcsicoll_s(const wchar_t* s1, const wchar_t* s2)
{
    return (s1 && s2) ? _wcsicoll(s1, s2) : ((s1 ? 1 : (s2 ? -1 : 0)));
}

// ============================================================================

inline void SwabEx(char* src, char* dest, size_t n)
{
    static int const max = (INT_MAX - (INT_MAX % 2));

    if (n <= (size_t)max) {
        _swab(src, dest, (int)n);
    } else {
        size_t size = n - (n % 2LL);
        while (size > (size_t)max) {
            _swab(src, dest, max);
            size -= max;
            src += max;
            dest += max;
        }
        if (size > 0) {
            _swab(src, dest, (int)size);
        }
    }
}

// console helper from Print.cpp
//void RedirectIOToConsole();

//==== StrCut methods ===================

CHAR*  StrCutIA(CHAR* s,const CHAR* pattern);
WCHAR* StrCutIW(WCHAR* s,const WCHAR* pattern);
#if defined(UNICODE) || defined(_UNICODE)
#define StrCutI StrCutIW
#else
#define StrCutI _StrCutIA
#endif


//==== StrNextTok methods ===================
CHAR*  StrNextTokA(CHAR* strg, const CHAR* tokens);
WCHAR* StrNextTokW(WCHAR* strg, const WCHAR* tokens);
#if defined(UNICODE) || defined(_UNICODE)
#define StrNextTok StrNextTokW
#else
#define StrNextTok StrNextTokA
#endif

// ----------------------------------------------------------------------------

bool StrDelChrA(LPSTR pszSource, LPCSTR pCharsToRemove);
bool StrDelChrW(LPWSTR pszSource, LPCWSTR pCharsToRemove);
#if defined(UNICODE) || defined(_UNICODE)
#define StrDelChr(s,r) StrDelChrW((s),(r))
#else
#define StrDelChr(s,r) StrDelChrA((s),(r))
#endif


//==== StrSafe lstrlen() =======================================================
//inline size_t StringCchLenA(LPCSTR s, size_t n) {
//  n = (n ? n : STRSAFE_MAX_CCH); size_t len; return (size_t)(!s ? 0 : (SUCCEEDED(StringCchLengthA(s, n, &len)) ? len : n));
//}
//inline size_t StringCchLenW(LPCWSTR s, size_t n) {
//  n = (n ? n : STRSAFE_MAX_CCH); size_t len; return (size_t)(!s ? 0 : (SUCCEEDED(StringCchLengthW(s, n, &len)) ? len : n));
//}

inline size_t StringCchLenA(LPCSTR s, size_t n)
{
    n = (n ? n : STRSAFE_MAX_CCH);
    return (s ? strnlen_s(s, n) : 0LL);
}
inline size_t StringCchLenW(LPCWSTR s, size_t n)
{
    n = (n ? n : STRSAFE_MAX_CCH);
    return (s ? wcsnlen_s(s, n) : 0LL);
}

#if defined(UNICODE) || defined(_UNICODE)
#define StringCchLen(s,n)  StringCchLenW((s),(n))
#else
#define StringCchLen(s,n)  StringCchLenA((s),(n))
#endif

// ----------------------------------------------------------------------------

inline char* StrEndA(const char* pStart, size_t siz)
{
    // cppcheck-suppress cert-EXP05-C   // Attempt to cast away const - Intended(!)
    return (char*)(pStart + StringCchLenA(pStart, siz));
}

inline WCHAR* StrEndW(const WCHAR* pStart, size_t siz)
{
    // cppcheck-suppress cert-EXP05-C   // Attempt to cast away const - Intended(!)
    return (WCHAR*)(pStart + StringCchLenW(pStart, siz));
}

#if defined(UNICODE) || defined(_UNICODE)
#define StrEnd(s,n) StrEndW((s),(n))
#else
#define StrEnd(s,n) StrEndA((s),(n))
#endif

// ----------------------------------------------------------------------------

inline void StrReplChrA(CHAR* pStrg, const CHAR chSearch, const CHAR chReplace)
{
    while (pStrg && *pStrg) {
        if (*pStrg == chSearch) {
            *pStrg = chReplace;
        }
        ++pStrg;
    }
}

inline void StrReplChrW(WCHAR* pStrg, const WCHAR chSearch, const WCHAR chReplace)
{
    while (pStrg && *pStrg) {
        if (*pStrg == chSearch) {
            *pStrg = chReplace;
        }
        ++pStrg;
    }
}

#if defined(UNICODE) || defined(_UNICODE)
#define StrReplChr(str, cs, cr) StrReplChrW((str), (cs), (cr))
#else
#define StrReplChr(str, cs, cr) StrReplChrA((str), (cs), (cr))
#endif

// ----------------------------------------------------------------------------

//==== StrSafe lstrcmp(),lstrcmpi() =============================================

// NOTE: !!! differences in AutoCompleteList depending compare functions (CRT vs. Shlwapi)) !!!

#define StringCchCompareNA(s1,l1,s2,l2)   StrCmpNA((s1),(s2),min_i((int)(l1),(int)(l2)))
//#define StringCchCompareNA(s1,l1,s2,l2)   strncmp((s1),(s2),min_s((l1),(l2)))
#define StringCchCompareXA(s1,s2)         StrCmpA((s1),(s2))
//#define StringCchCompareXA(s1,s2)         strcmp((s1),(s2))

#define StringCchCompareNIA(s1,l1,s2,l2)  StrCmpNIA((s1),(s2),min_i((int)(l1),(int)(l2)))
//#define StringCchCompareNIA(s1,l1,s2,l2)  _strnicmp((s1),(s2),min_s((l1),(l2)))
#define StringCchCompareXIA(s1,s2)        StrCmpIA((s1),(s2))
//#define StringCchCompareXIA(s1,s2)        _stricmp((s1),(s2))


#define StringCchCompareNW(s1,l1,s2,l2)   StrCmpNW((s1),(s2),min_i((int)(l1),(int)(l2)))
#define StringCchCompareXW(s1,s2)         StrCmpW((s1),(s2))

#define StringCchCompareNIW(s1,l1,s2,l2)  StrCmpNIW((s1),(s2),min_i((int)(l1),(int)(l2)))
#define StringCchCompareXIW(s1,s2)        StrCmpIW((s1),(s2))

#if defined(UNICODE) || defined(_UNICODE)
#define StringCchCompareN(s1,l1,s2,l2)   StringCchCompareNW((s1),(l1),(s2),(l2))
#define StringCchCompareX(s1,s2)         StringCchCompareXW((s1),(s2))
#define StringCchCompareNI(s1,l1,s2,l2)  StringCchCompareNIW((s1),(l1),(s2),(l2))
#define StringCchCompareXI(s1,s2)        StringCchCompareXIW((s1),(s2))
#else
#define StringCchCompareN(s1,l1,s2,l2)   StringCchCompareNA((s1),(l1),(s2),(l2))
#define StringCchCompareX(s1,s2)         StringCchCompareXA((s1),(s2))
#define StringCchCompareNI(s1,l1,s2,l2)  StringCchCompareNIA((s1),(l1),(s2),(l2))
#define StringCchCompareXI(s1,s2)        StringCchCompareXIA((s1),(s2))
#endif

#ifdef __cplusplus
#undef NULL
#define NULL nullptr
#endif

// ----------------------------------------------------------------------------

// Is the character an octal digit?
#define IsOctalDigitA(ch) (((ch) >= '0') && ((ch) <= '7'))
#define IsOctalDigitW(wch) (((wch) >= L'0') && ((wch) <= L'7'))

// Is the character an octal digit?
inline bool IsDigitA(const char ch) {
    return ((ch >= '0') && (ch <= '9'));
}
inline bool IsDigitW(const WCHAR wch) {
    return ((wch >= L'0') && (wch <= L'9'));
}

// Is the character a white space char?
inline bool IsBlankCharA(const char ch) {
    return ((ch == ' ') || (ch == '\t'));
}
inline bool IsBlankCharW(const WCHAR wch) {
    return ((wch == L' ') || (wch == L'\t'));
}

// no encoding for safe chars
inline bool IsAlphaNumericA(const char ch) {
    return ((ch >= '0') && (ch <= '9')) ||
           ((ch >= 'a') && (ch <= 'z')) ||
           ((ch >= 'A') && (ch <= 'Z'));
}

inline bool IsAlphaNumericW(const WCHAR ch) {
    return
        ((ch >= L'0') && (ch <= L'9')) ||
        ((ch >= L'a') && (ch <= L'z')) ||
        ((ch >= L'A') && (ch <= L'Z'));
}

// If the character is an hexadecimal digit, get its value.
inline int GetHexDigitA(const char ch)
{
    if (ch >= '0' && ch <= '9') {
        return ch - '0';
    }
    if (ch >= 'A' && ch <= 'F') {
        return ch - 'A' + 10;
    }
    if (ch >= 'a' && ch <= 'f') {
        return ch - 'a' + 10;
    }
    return -1;
}

inline int GetHexDigitW(const WCHAR ch)
{
    if (ch >= L'0' && ch <= L'9') {
        return ch - L'0';
    }
    if (ch >= L'A' && ch <= L'F') {
        return ch - L'A' + 10;
    }
    if (ch >= L'a' && ch <= L'f') {
        return ch - L'a' + 10;
    }
    return -1;
}
// ----------------------------------------------------------------------------

void UrlEscapeEx(LPCWSTR lpURL, LPWSTR lpEscaped, DWORD* pcchEscaped, bool bEscReserved);
void UrlUnescapeEx(LPWSTR lpURL, LPWSTR lpUnescaped, DWORD* pcchUnescaped);

int    ReadStrgsFromCSV(LPCWSTR wchCSVStrg, prefix_t sMatrix[], int iCount, int iLen, LPCWSTR sDefault);
size_t ReadVectorFromString(LPCWSTR wchStrg, int iVector[], size_t iCount, int iMin, int iMax, int iDefault, bool ordered);
size_t NormalizeColumnVector(LPSTR chStrg_in, LPWSTR wchStrg_out, size_t iCount);

#define MAX_ESCAPE_HEX_DIGIT 4
int Hex2Char(char* ch, int cnt);

void CloseNonModalDialogs();
void CloseApplication();

// ----------------------------------------------------------------------------

inline int PointSizeToFontHeight(const float fPtHeight, const HDC hdc) {
    return -MulDiv(float2int(fPtHeight * 100.0f), GetDeviceCaps(hdc, LOGPIXELSY), 72 * SC_FONT_SIZE_MULTIPLIER);
}

// ----------------------------------------------------------------------------

#endif //_NP3_HELPERS_H_

///   End of Helpers.h   ///
