/* Copyright 2022 the SumatraPDF project authors (see AUTHORS file).
   License: Simplified BSD (see COPYING.BSD) */

#define NO_COLOR (COLORREF) - 1

#define WIN_COL_WHITE RGB(0xff, 0xff, 0xff)
#define WIN_COL_BLACK RGB(0, 0, 0)

#define DRAGQUERY_NUMFILES 0xFFFFFFFF

bool ToBool(BOOL b);

UINT_PTR NextSubclassId();

RECT ClientRECT(HWND);
Rect ClientRect(HWND);
Rect WindowRect(HWND);
// Visible frame bounds (excludes DWM invisible resize/shadow border). Falls back to WindowRect.
Rect WindowVisibleRect(HWND);
Rect MapRectToWindow(Rect, HWND hwndFrom, HWND hwndTo);
Rect MapLtrClientRectToScreen(HWND hwnd, Rect r);

void EditSelectAll(HWND);
int EditIdealDy(HWND, bool hasBorder, int lines = 1);
void EditImplementCtrlBack(HWND hwnd);
bool EditDeleteChar(HWND hwnd, bool beforeCursor);

void ListBox_AppendString_NoSort(HWND, const WCHAR*);
int ListBoxGetTopIndex(HWND);
bool ListBoxSetTopIndex(HWND, int);

bool IsValidHandle(HANDLE);
bool SafeCloseHandle(HANDLE*);
bool SafeFindClose(HANDLE*);
void FillWndClassEx(WNDCLASSEX& wcex, const WCHAR* clsName, WNDPROC wndproc);
void MoveWindow(HWND hwnd, Rect rect);
void MoveWindow(HWND hwnd, RECT* r);

bool IsOs64();
bool IsProcess64();
bool IsProcess32();
bool IsArmBuild();
bool IsRunningInWow64();
bool IsProcessAndOsArchSame();

bool GetOsVersion(OSVERSIONINFOEX& ver);
TempStr OsNameFromVerTemp(const OSVERSIONINFOEX& ver);
TempStr GetWindowsVerTemp();

TempStr GetEnvVariableTemp(const char*);

TempStr GetLastErrorStrTemp(DWORD err = 0);
void LogLastError(DWORD err = 0);
void DbgOutLastError(DWORD err = 0);

// registry
const char* RegKeyNameTemp(HKEY key);
bool RegKeyExists(HKEY keySub, const char* keyName);
char* ReadRegStrTemp(HKEY keySub, const char* keyName, const char* valName);
char* LoggedReadRegStrTemp(HKEY keySub, const char* keyName, const char* valName);
char* ReadRegStr2Temp(const char* keyName, const char* valName);
char* LoggedReadRegStr2Temp(const char* keyName, const char* valName);
bool WriteRegStr(HKEY keySub, const char* keyName, const char* valName, const char* value);
bool LoggedWriteRegStr(HKEY keySub, const char* keyName, const char* valName, const char* value);
bool ReadRegDWORD(HKEY keySub, const char* keyName, const char* valName, DWORD& value);
bool WriteRegDWORD(HKEY keySub, const char* keyName, const char* valName, DWORD value);
bool LoggedWriteRegDWORD(HKEY keySub, const char* keyName, const char* valName, DWORD value);
bool LoggedWriteRegNone(HKEY hkey, const char* key, const char* valName);
bool CreateRegKey(HKEY keySub, const char* keyName);
bool DeleteRegKey(HKEY keySub, const char* keyName, bool resetACLFirst = false);
bool LoggedDeleteRegKey(HKEY keySub, const char* keyName, bool resetACLFirst = false);
bool DeleteRegValue(HKEY keySub, const char* keyName, const char* val);
bool LoggedDeleteRegValue(HKEY keySub, const char* keyName, const char* val);
HRESULT CLSIDFromString(const char* lpsz, LPCLSID pclsid);

// file and directory operations
TempStr GetSpecialFolderTemp(int csidl, bool createIfMissing = false);
TempStr GetTempDirTemp();
TempStr GetSelfExePathTemp();
WCHAR* GetSelfExePathW();
TempStr GetSelfExeDirTemp();
void ChangeCurrDirToDocuments();
int FileTimeDiffInSecs(const FILETIME& ft1, const FILETIME& ft2);
char* ResolveLnkTemp(const char* path);
bool CreateShortcut(const char* shortcutPath, const char* exePath, const char* args = nullptr,
                    const char* description = nullptr, int iconIndex = 0);
IDataObject* GetDataObjectForFile(const char* filePath, HWND hwnd = nullptr);

HANDLE LaunchProcessWithCmdLine(const char* exe, const char* cmdLine);
HANDLE LaunchProcessInDir(const char* cmdLine, const char* currDir = nullptr, DWORD flags = 0);
bool CreateProcessHelper(const char* exe, const char* args);
bool LaunchFileShell(const char* path, const char* params = nullptr, const char* verb = nullptr, bool hidden = false);
bool LaunchBrowser(const char* url);
bool LaunchBrowserWithReuse(const char* url, bool navigateOnReuse = true, bool* reusedOut = nullptr,
                            HWND* browserHwndOut = nullptr, bool* navigatedOut = nullptr);
bool PasteClipboardToBrowserChatInput(HWND browserHwnd, int delayBeforePasteMs = 150);
bool PasteAndSubmitBrowserChatInput(HWND browserHwnd, int delayBeforePasteMs = 150);
bool PasteAndSubmitBrowserChatInputWhenReady(HWND browserHwnd, const char* url, bool waitForPageReady,
                                             bool dismissChromeFocus = false);

typedef void (*BrowserChatUiTaskFn)(void* ctx);
typedef void (*BrowserChatUiTaskRunner)(BrowserChatUiTaskFn fn, void* ctx);
void SetBrowserChatUiTaskRunner(BrowserChatUiTaskRunner runner);
void RunBrowserChatUiTask(BrowserChatUiTaskFn fn, void* ctx);

enum class AiChatService {
    Doubao,
    DeepSeek,
    ChatGPT,
};

bool LaunchAiChatBrowser(AiChatService service, bool* reusedOut = nullptr, HWND* browserHwndOut = nullptr,
                         bool* navigatedOut = nullptr);
bool PasteAndSubmitAiChatWhenReady(AiChatService service, HWND browserHwnd, bool waitForPageReady,
                                   bool dismissChromeFocus = false);
void LaunchAiChatWithPromptAsync(AiChatService service, const char* prompt);
void OpenPathInDefaultFileManager(const char* path);
void PaintCheckerboard(HDC hdc, int x, int y, int w, int h);

void RunNonElevated(const char* exePath);
bool LaunchElevated(const char* path, const char* cmdline);
bool IsProcessRunningElevated();
TempStr GetParentProcessPath(DWORD* pidOut = nullptr);
bool CanTalkToProcess(DWORD procId);
DWORD GetAccountType();
DWORD GetOriginalAccountType();

void DisableDataExecution();
bool RedirectIOToConsole();
bool RedirectIOToExistingConsole();
void HandleRedirectedConsoleOnShutdown();

bool IsKeyPressed(int key);
bool IsShiftPressed();
bool IsAltPressed();
bool IsCtrlPressed();

Rect ShiftRectToWorkArea(Rect rect, HWND hwnd = nullptr, bool bFully = false);
Rect GetWorkAreaRect(Rect rect, HWND hwnd);
void LimitWindowSizeToScreen(HWND hwnd, SIZE& size);
void HwndEnsureVisible(HWND hwnd);
Rect GetFullscreenRect(HWND);
Rect GetVirtualScreenRect();

void DrawRect(HDC, const Rect&);
void FillRect(HDC, const Rect&, HBRUSH);
void FillRect(HDC hdc, const Rect&, COLORREF);
void DrawLine(HDC, const Rect&);

void DrawCenteredText(HDC hdc, Rect r, const char* txt, bool isRTL = false);
Size HwndMeasureText(HWND hwnd, const char* txt, HFONT font = nullptr);
Size HwndMeasureTextWrapped(HWND hwnd, const char* txt, HFONT font, int maxDx);
int FontDyPx(HWND hwnd, HFONT hfont);

int HdcDrawText(HDC hdc, const char* s, RECT* r, uint format, HFONT font = nullptr);
int HdcDrawText(HDC hdc, const char* s, const Rect& r, uint format, HFONT font = nullptr);
int HdcDrawText(HDC hdc, const char* s, const Point& pos, uint fmt, HFONT font = nullptr);
Size HdcMeasureText(HDC hdc, const char* s, uint format, HFONT font);
Size HdcMeasureText(HDC hdc, const char* s, HFONT font = nullptr);

HWND HwndSetFocus(HWND hwnd);
bool HwndIsFocused(HWND);
bool IsCursorOverWindow(HWND);

HWND HwndGetParent(HWND hwnd);
TempStr HwndGetClassName(HWND hwnd);
Point HwndGetCursorPos(HWND hwnd);
Point& UnmirrorRtl(HWND hwnd, Point& p);
int MapWindowPoints(HWND, HWND, Point*, int);
void HwndScreenToClient(HWND, Point&);
void HwndMakeVisible(HWND);

bool IsMouseOverRect(HWND hwnd, const Rect& r);
void CenterDialog(HWND hDlg, HWND hParent = nullptr);
void SetDlgItemFont(HWND hDlg, int nIDDlgItem, HFONT fnt);

char* GetDefaultPrinterNameTemp();

bool CopyTextToClipboard(const char*);
bool AppendTextToClipboard(const char*);

bool CopyImageToClipboard(HBITMAP hbmp, bool appendOnly);

bool IsWindowStyleSet(HWND hwnd, DWORD flags);
bool IsWindowStyleExSet(HWND hwnd, DWORD flags);
void SetWindowStyle(HWND hwnd, DWORD flags, bool enable);
void SetWindowExStyle(HWND hwnd, DWORD flags, bool enable);

bool HwndIsRtl(HWND hwnd);
void HwndSetRtl(HWND hwnd, bool isRtl);

Rect ChildPosWithinParent(HWND);

HFONT GetMenuFont();
HFONT CreateSimpleFont(HDC hdc, const char* fontName, int fontSize);
HFONT GetDefaultGuiFont(bool bold = false, bool italic = false);
HFONT GetDefaultGuiFontOfSize(int size);
HFONT GetUserGuiFont(const char* fontName, int size);
HFONT GetUserGuiFontForDpi(const char* fontName, int size, int dpi);
int GetSizeOfDefaultGuiFont();
int GetSizeOfDefaultGuiFontForDpi(int dpi);
bool GetNonClientMetricsForDpi(int dpi, NONCLIENTMETRICS* ncm);
void DeleteCreatedFonts();

IStream* CreateStreamFromData(const ByteSlice&);
ByteSlice GetDataFromStream(IStream* stream, HRESULT* resOpt);
ByteSlice GetStreamOrFileData(IStream* stream, const char* filePath);
bool ReadDataFromStream(IStream* stream, void* buffer, size_t len, size_t offset = 0);
uint GuessTextCodepage(const char* data, size_t len, uint defVal = CP_ACP);
char* NormalizeString(const char* str, int /* NORM_FORM */ form);
void ResizeHwndToClientArea(HWND hwnd, int dx, int dy, bool hasMenu);
void ResizeWindow(HWND, int dx, int dy);

void MessageBoxWarningSimple(HWND hwnd, const WCHAR* msg, const WCHAR* title = nullptr);
void MessageBoxNYI(HWND hwnd);

bool RegisterServerDLL(const char* dllPath, const char* args = nullptr);
bool UnRegisterServerDLL(const char* dllPath, const char* args = nullptr);
bool RegisterOrUnregisterServerDLL(const char* dllPath, bool install, const char* args = nullptr);

inline BOOL toBOOL(bool b) {
    return b ? TRUE : FALSE;
}

inline bool fromBOOL(BOOL b) {
    return b != 0;
}

inline bool tobool(BOOL b) {
    return b != 0;
}

void MenuSetChecked(HMENU m, int id, bool isChecked);
bool MenuSetEnabled(HMENU m, int id, bool isEnabled);
void MenuRemove(HMENU m, int id);
// TODO: this doesn't recognize enum Cmd, why?
// void Remove(HMENU m, enum Cmd id);
void MenuEmpty(HMENU m);
void MenuSetText(HMENU m, int id, const WCHAR* s);
void MenuSetText(HMENU m, int id, const char* s);
TempStr MenuToSafeStringTemp(const char* s);

struct DoubleBuffer {
    HWND hTarget = nullptr;
    HDC hdcCanvas = nullptr;
    HDC hdcBuffer = nullptr;
    HBITMAP doubleBuffer = nullptr;
    Rect rect{};

    DoubleBuffer(HWND hwnd, Rect rect);
    DoubleBuffer(const DoubleBuffer&) = delete;
    DoubleBuffer& operator=(const DoubleBuffer&) = delete;
    ~DoubleBuffer();

    HDC GetDC() const;
    void Flush(HDC hdc, RECT* rcPaint = nullptr) const;
};

class DeferWinPosHelper {
    HDWP hdwp;

  public:
    DeferWinPosHelper();
    ~DeferWinPosHelper();
    void End();
    void SetWindowPos(HWND hwnd, Rect rc);
    void SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
    void MoveWindow(HWND hWnd, int x, int y, int cx, int cy, BOOL bRepaint = TRUE);
    void MoveWindow(HWND hWnd, Rect r);
};

struct BitmapPixels {
    u8* pixels;
    Size size;
    int nBytes;
    int nBytesPerPixel;
    int nBytesPerRow;

    HBITMAP hbmp;
    BITMAPINFO bmi;
    HDC hdc;
};

struct BlittableBitmap {
    Size size = {};

    BlittableBitmap() {};

    Size GetSize();

    virtual bool Blit(HDC hdc, Rect target) = 0;
    virtual bool IsValid() = 0;

    virtual ~BlittableBitmap() {};
};

i64 BlittableBitmapByteSize(BlittableBitmap*);

struct RenderedBitmap : BlittableBitmap {
    HBITMAP hbmp = nullptr;
    HANDLE hMap = nullptr;

    RenderedBitmap(HBITMAP hbmp, Size size, HANDLE hMap = nullptr);
    ~RenderedBitmap() override;

    RenderedBitmap* Clone() const;
    HBITMAP GetBitmap() const;
    bool IsValid() override;
    bool Blit(HDC hdc, Rect target) override;
};

void InitAllCommonControls();
Size GetBitmapSize(HBITMAP hbmp);
BitmapPixels* GetBitmapPixels(HBITMAP hbmp);
void FinalizeBitmapPixels(BitmapPixels* bitmapPixels);
COLORREF GetPixel(BitmapPixels* bitmap, int x, int y);
void UpdateBitmapColors(HBITMAP hbmp, COLORREF textColor, COLORREF bgColor, COLORREF linkColor,
                        Vec<Rect>* skipRects = nullptr);
ByteSlice SerializeBitmap(HBITMAP hbmp);
HBITMAP CreateMemoryBitmap(Size size, HANDLE* hDataMapping = nullptr);
bool BlitHBITMAP(HBITMAP hbmp, HDC hdc, Rect target);
double GetProcessRunningTime();

void VariantInitBstr(VARIANT& urlVar, const WCHAR* s);
bool DDEExecute(const WCHAR* server, const WCHAR* topic, const WCHAR* command);

void RectInflateTB(RECT& r, int top, int bottom);
void DivideRectH(const RECT& r, int y, int dy, RECT& r1, RECT& r2, RECT& r3);
void DivideRectV(const RECT& r, int x, int dx, RECT& r1, RECT& r2, RECT& r3);

HCURSOR GetCachedCursor(LPWSTR id);
void SetCursorCached(LPWSTR id);
void DeleteCachedCursors();

int GetMeasurementSystem();
bool TrackMouseLeave(HWND);

struct LoadedDataResource {
    const u8* data = nullptr;
    int dataSize = 0;
};
bool LockDataResource(int resId, LoadedDataResource*);

HINSTANCE GetInstance();
Size ButtonGetIdealSize(HWND hwnd);
bool IsValidDelayType(int type);

void HwndResizeClientSize(HWND, int, int);

size_t HwndGetTextLen(HWND hwnd);
TempWStr HwndGetTextWTemp(HWND hwnd);
TempStr HwndGetTextTemp(HWND hwnd);
void HwndSetText(HWND, const char* s);
bool HwndHasFrameThickness(HWND hwnd);
bool HwndHasCaption(HWND hwnd);

void HwndSetDlgItemText(HWND, int, const char*);

void CbAddString(HWND, const char*);
void CbSetCurrentSelection(HWND, int);

HICON HwndGetIcon(HWND);
HICON HwndSetIcon(HWND, HICON);

void HwndRepaintNow(HWND);
void HwndScheduleRepaint(HWND hwnd);

HFONT HwndGetFont(HWND);
void HwndSetFont(HWND, HFONT);
void HwndSetTreeFont(HWND hwndTree, HFONT font);
void HwndSetTreeFontForDpi(HWND hwndTree, HFONT font, int dpi);

void HwndPositionToTheRightOf(HWND hwnd, HWND hwndRelative);
// Position an owned popup against the frame's visible right edge at y (screen
// coords). Clamps to the frame monitor's work area so DPI/shadow mismatches
// cannot push the popup off-screen.
void PositionOwnedPopupAtFrameRight(HWND hwndPopup, HWND hwndFrame, int y);
// Dock hwnd against the right of hwndRelative.
// Maximized main window: overlay the right side (raised above the frame).
// Otherwise: place flush outside to the right, clamped to the main window's monitor.
void HwndDockToRightOf(HWND hwnd, HWND hwndRelative, int preferredWidth = 0);
void HwndPositionInCenterOf(HWND hwnd, HWND hwndRelative);
void HwndSendCommand(HWND hwnd, int cmdId, LPARAM lp = 0);
void HwndPostCommand(HWND hwnd, int cmdId, LPARAM lp = 0);
void HwndDestroyWindowSafe(HWND* hwnd);
void HwndToForeground(HWND hwnd);
void HwndSetVisibility(HWND hwnd, bool visible);

bool DeleteObjectSafe(HGDIOBJ*);
bool DeleteBrushSafe(HBRUSH*);
bool DestroyIconSafe(HICON*);

void TbSetButtonInfoById(HWND hwnd, int buttonId, TBBUTTONINFO* info);
void TbGetPadding(HWND, int* padX, int* padY);
void TbSetPadding(HWND, int padX, int padY);
void TbGetMetrics(HWND hwnd, TBMETRICS* metrics);
void TbSetMetrics(HWND hwnd, TBMETRICS* metrics);
void TbGetRectById(HWND hwnd, int buttonId, RECT* rc);
void TbGetRectByIdx(HWND hwnd, int buttonIdx, RECT* rc);

void TreeViewExpandRecursively(HWND hTree, HTREEITEM hItem, uint flag, bool subtree);
void AddPathToRecentDocs(const char*);

TempStr HGLOBALToStrTemp(HGLOBAL h, bool isUnicode);
HGLOBAL MemToHGLOBAL(void* src, int n, UINT flags = GMEM_MOVEABLE);
HGLOBAL StrToHGLOBAL(const char* s, UINT flags = GMEM_MOVEABLE);
TempStr AtomToStrTemp(ATOM a);
int MsgBox(HWND, const char*, const char*, UINT);
HWND ShowTextInWindow(const char* title, const char* text, HWND* hwndPtr = nullptr);
void ShowTextInWindowDialog(const char* title, const char* text);

constexpr u32 kCpuMMX = 1 << 1;
constexpr u32 kCpuSSE = 1 << 2;
constexpr u32 kCpuSSE2 = 1 << 2;
constexpr u32 kCpuSSE3 = 1 << 3;
constexpr u32 kCpuSSE41 = 1 << 4;
constexpr u32 kCpuSSE42 = 1 << 5;
constexpr u32 kCpuAVX = 1 << 6;
constexpr u32 kCpuAVX2 = 1 << 7;
// ARM
constexpr u32 kCpuNEON = 1 << 8;
constexpr u32 kCpuArmCrypto = 1 << 9;
constexpr u32 kCpuArmAtomics = 1 << 10;
constexpr u32 kCpuArmDotProd = 1 << 11;

u32 CpuID();
const char* LatestSupportedSIMD();

LARGE_INTEGER TimeNow();
double TimeDiffSecs(const LARGE_INTEGER& start, const LARGE_INTEGER& end);
double TimeDiffMs(const LARGE_INTEGER& start, const LARGE_INTEGER& end);
bool IsPEFileSigned(const char* filePath);
TempStr GetExecutableSignerTemp(const char* exePath);
