#ifndef ZCPPWIN_WINTOOL_H
#define ZCPPWIN_WINTOOL_H

// ============================================================================
// wintool.h
// ----------------------------------------------------------------------------
// MSVC / Windows / C++20 常用 Win32 工具库声明文件。
//
// 使用方式：
//   1. 将 wintool.h 与 wintool.cpp 加入你的 CLion + CMake 工程。
//   2. 推荐在 CMake 中启用 C++20，并定义 UNICODE、_UNICODE、NOMINMAX。
//   3. 所有接口都位于命名空间 Z 中，直接调用 Z::xxx 即可。
//
// 设计约定：
//   - 默认优先使用宽字符版本，也就是 std::wstring / Win32 W 系列 API。
//   - 失败时大多数函数会抛出 Z::Win32Error、Z::HResultError 或 std::runtime_error。
//   - 资源对象采用 RAII，离开作用域后自动释放 HANDLE、HMODULE、HKEY 等资源。
//   - 返回 std::optional 的函数表示“失败或不存在”不一定是异常场景。
//
// 简单示例：
//   ZWINTOOL::SetConsoleUtf8();
//   auto exe = ZWINTOOL::ModulePathW();
//   ZWINTOOL::WriteTextFileUtf8(L"log.txt", "hello\n");
//   auto text = ZWINTOOL::ReadTextFileUtf8(L"log.txt");
// ============================================================================

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif

#include <windows.h>
#include <objbase.h>

#include <cstddef>
#include <cstdint>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>

#include "ztool_lib_macro.h"


namespace ZWINTOOL {

    // 通用字节数组类型，常用于二进制文件、注册表 REG_BINARY 等场景。
    using Bytes = std::vector<std::byte>;

    /* ---------------------------------------------------------------------------- */
    //region 错误处理工具
    /* ---------------------------------------------------------------------------- */

    // Win32 错误异常。
    // 作用：保存 GetLastError / Win32 API 返回的错误码，并生成可读错误文本。
    // 用法：catch (const Z::Win32Error& e) 后可通过 e.code() 获取 DWORD 错误码。
    class ZTOOL_API Win32Error final : public std::runtime_error {
    public:
        // error 为 Win32 错误码；context 用于标记失败位置，例如 "CreateFileW"。
        explicit Win32Error(DWORD error, std::string_view context = {});

        // 返回原始 Win32 错误码。
        [[nodiscard]] DWORD code() const noexcept;

    private:
        DWORD code_{};
    };

    // HRESULT 错误异常。
    // 作用：包装 COM、Shell、DirectX 等 API 常见的 HRESULT 失败值。
    // 用法：catch (const Z::HResultError& e) 后可通过 e.hr() 获取 HRESULT。
    class ZTOOL_API HResultError final : public std::runtime_error {
    public:
        // hr 为 HRESULT；context 用于标记失败位置，例如 "CoCreateInstance"。
        explicit HResultError(HRESULT hr, std::string_view context = {});

        // 返回原始 HRESULT。
        [[nodiscard]] HRESULT hr() const noexcept;

    private:
        HRESULT hr_{};
    };

    // 获取当前线程最近一次 Win32 错误码，相当于 GetLastError()。
    [[nodiscard]] ZTOOL_API DWORD LastError() noexcept;

    // 将 Win32 错误码格式化为宽字符串。
    // 用法：std::wstring msg = Z::ErrorMessageW(ERROR_FILE_NOT_FOUND);
    [[nodiscard]] ZTOOL_API std::wstring ErrorMessageW(DWORD error = ::GetLastError());

    // 将 Win32 错误码格式化为 UTF-8 字符串。
    [[nodiscard]] ZTOOL_API std::string ErrorMessageA(DWORD error = ::GetLastError());

    // 将 HRESULT 格式化为宽字符串。
    [[nodiscard]] ZTOOL_API std::wstring HResultMessageW(HRESULT hr);

    // 将 HRESULT 格式化为 UTF-8 字符串。
    [[nodiscard]] ZTOOL_API std::string HResultMessageA(HRESULT hr);

    // 直接抛出当前 GetLastError() 对应的 Win32Error。
    // 用法：if (!ok) Z::ThrowLastError("SomeWin32Api");
    ZTOOL_API void ThrowLastError(std::string_view context = {});

    // 检查 BOOL 返回值；为 FALSE 时抛出 Win32Error。
    ZTOOL_API void ThrowIfFalse(BOOL ok, std::string_view context = {});

    // 检查指针；为空时抛出 Win32Error。
    ZTOOL_API void ThrowIfNull(const void *pointer, std::string_view context = {});

    // 检查 HANDLE；nullptr 或 INVALID_HANDLE_VALUE 时抛出 Win32Error。
    ZTOOL_API void ThrowIfInvalidHandle(HANDLE handle, std::string_view context = {});

    // 检查 HRESULT；FAILED(hr) 时抛出 HResultError。
    ZTOOL_API void CheckHResult(HRESULT hr, std::string_view context = {});

    //endregion

    /* ---------------------------------------------------------------------------- */
    //region 字符串编码转换工具
    /* ---------------------------------------------------------------------------- */

    // UTF-8 转 UTF-16。
    // 典型用途：把配置文件、网络文本、std::string 传给 Windows W 系列 API。
    [[nodiscard]] ZTOOL_API std::wstring Utf8ToWide(std::string_view text);

    // UTF-16 转 UTF-8。
    // 典型用途：把 Win32 返回的 std::wstring 输出到日志、文件或控制台。
    [[nodiscard]] ZTOOL_API std::string WideToUtf8(std::wstring_view text);

    // 指定代码页的多字节字符串转 UTF-16。
    // 用法：Z::MultiByteToWide(text, CP_ACP) 或 Z::MultiByteToWide(text, CP_UTF8)。
    [[nodiscard]] ZTOOL_API std::wstring MultiByteToWide(std::string_view text, UINT codePage);

    // UTF-16 转指定代码页多字节字符串。
    [[nodiscard]] ZTOOL_API std::string WideToMultiByte(std::wstring_view text, UINT codePage);

    //endregion

    /* ---------------------------------------------------------------------------- */
    //region RAII Win32 资源封装
    /* ---------------------------------------------------------------------------- */

    // HANDLE RAII 封装。
    // 作用：自动调用 CloseHandle，避免文件、进程、线程、事件等句柄泄漏。
    // 用法：Z::Handle file(::CreateFileW(...)); if (file) { ... }
    class ZTOOL_API Handle final {
    public:
        constexpr Handle() noexcept = default;

        // 接管一个已有 HANDLE；析构时自动 CloseHandle。
        explicit Handle(HANDLE handle) noexcept;
        ~Handle() noexcept;

        Handle(const Handle &) = delete;
        Handle &operator=(const Handle &) = delete;

        Handle(Handle &&other) noexcept;
        Handle &operator=(Handle &&other) noexcept;

        // 获取原始 HANDLE，不转移所有权。
        [[nodiscard]] HANDLE get() const noexcept;

        // 获取用于输出参数的地址；会先释放旧句柄。
        // 用法：SomeApi(handle.put());
        [[nodiscard]] HANDLE *put() noexcept;

        // 判断是否持有有效句柄。
        [[nodiscard]] bool valid() const noexcept;
        explicit operator bool() const noexcept;

        // 放弃所有权并返回原始 HANDLE；调用者之后需要自行 CloseHandle。
        [[nodiscard]] HANDLE release() noexcept;

        // 释放旧句柄并接管新句柄。
        void reset(HANDLE handle = nullptr) noexcept;

    private:
        HANDLE handle_ = nullptr;
    };

    // HMODULE RAII 封装。
    // 作用：LoadLibraryW / FreeLibrary 的安全封装，适合动态加载 DLL。
    // 用法：Z::Library dll(L"user32.dll"); auto p = dll.proc<Fn*>("MessageBoxW");
    class ZTOOL_API Library final {
    public:
        Library() noexcept = default;
        explicit Library(std::wstring_view path);
        ~Library() noexcept;

        Library(const Library &) = delete;
        Library &operator=(const Library &) = delete;

        Library(Library &&other) noexcept;
        Library &operator=(Library &&other) noexcept;

        [[nodiscard]] HMODULE get() const noexcept;
        [[nodiscard]] bool valid() const noexcept;
        explicit operator bool() const noexcept;

        // 加载指定 DLL；已加载其他模块时会先释放。
        void load(std::wstring_view path);

        // 放弃所有权，调用者之后需要自行 FreeLibrary。
        [[nodiscard]] HMODULE release() noexcept;

        // 释放旧模块并接管新模块。
        void reset(HMODULE module = nullptr) noexcept;

        // 获取 DLL 导出函数地址，并转换为指定函数指针类型。
        // 用法：using Fn = int (WINAPI*)(HWND,LPCWSTR,LPCWSTR,UINT);
        //       auto MessageBoxWFn = dll.proc<Fn>("MessageBoxW");
        template<class T>
        [[nodiscard]] T proc(std::string_view name) const {
            if (!module_) {
                throw std::logic_error("Z::Library::proc called on empty library");
            }
            std::string procName{name};
            FARPROC p = ::GetProcAddress(module_, procName.c_str());
            if (!p) {
                ThrowLastError("GetProcAddress");
            }
            return reinterpret_cast<T>(p);
        }

    private:
        HMODULE module_ = nullptr;
    };

    // COM 初始化 RAII 封装。
    // 作用：构造时 CoInitializeEx，析构时 CoUninitialize。
    // 用法：Z::ComApartment com; 然后安全调用 COM / Shell API。
    class ZTOOL_API ComApartment final {
    public:
        // coinit 可传 COINIT_APARTMENTTHREADED 或 COINIT_MULTITHREADED。
        explicit ComApartment(DWORD coinit = COINIT_APARTMENTTHREADED);
        ~ComApartment() noexcept;

        ComApartment(const ComApartment &) = delete;
        ComApartment &operator=(const ComApartment &) = delete;

        // 返回 CoInitializeEx 的结果，通常 S_OK 或 S_FALSE 表示可用。
        [[nodiscard]] HRESULT hr() const noexcept;

    private:
        HRESULT hr_ = S_OK;
        bool initialized_ = false;
    };

    //endregion

    /* ---------------------------------------------------------------------------- */
    //region 路径与环境变量工具
    /* ---------------------------------------------------------------------------- */

    // 获取当前 EXE 或指定模块的完整路径。
    [[nodiscard]] ZTOOL_API std::wstring ModulePathW(HMODULE module = nullptr);

    // 获取当前 EXE 或指定模块所在目录。
    [[nodiscard]] ZTOOL_API std::wstring ModuleDirW(HMODULE module = nullptr);

    // 获取当前工作目录。
    [[nodiscard]] ZTOOL_API std::wstring CurrentDirectoryW();

    // 获取系统临时目录。
    [[nodiscard]] ZTOOL_API std::wstring TempDirectoryW();

    // 获取父目录路径。示例：C:\\a\\b.txt -> C:\\a。
    [[nodiscard]] ZTOOL_API std::wstring ParentPathW(std::wstring_view path);

    // 获取文件名部分。示例：C:\\a\\b.txt -> b.txt。
    [[nodiscard]] ZTOOL_API std::wstring FileNameW(std::wstring_view path);

    // 拼接两个路径片段，会自动处理反斜杠。
    [[nodiscard]] ZTOOL_API std::wstring JoinPathW(std::wstring_view left, std::wstring_view right);

    // 转换为 Windows 长路径形式。绝对路径会加 \\?\ 前缀，便于突破 MAX_PATH 限制。
    [[nodiscard]] ZTOOL_API std::wstring ToLongPathW(std::wstring_view path);

    // 读取环境变量；不存在时返回 std::nullopt。
    [[nodiscard]] ZTOOL_API std::optional<std::wstring> EnvironmentVariableW(std::wstring_view name);

    // 设置环境变量；value 为 nullopt 时删除环境变量。
    ZTOOL_API void SetEnvironmentVariableWZ(std::wstring_view name,
                                            std::optional<std::wstring_view> value);

    // 展开字符串中的环境变量。示例：%TEMP%\\a.txt。
    [[nodiscard]] ZTOOL_API std::wstring ExpandEnvironmentStringsWZ(std::wstring_view text);

    //endregion

    /* ---------------------------------------------------------------------------- */
    //region 文件系统工具
    /* ---------------------------------------------------------------------------- */

    // 判断路径是否存在，文件或目录均可。
    [[nodiscard]] ZTOOL_API bool PathExistsW(std::wstring_view path);

    // 判断路径是否存在且为普通文件。
    [[nodiscard]] ZTOOL_API bool FileExistsW(std::wstring_view path);

    // 判断路径是否存在且为目录。
    [[nodiscard]] ZTOOL_API bool DirectoryExistsW(std::wstring_view path);

    // 递归创建目录。目录已存在时返回 true。
    [[nodiscard]] ZTOOL_API bool CreateDirectoryTreeW(std::wstring_view path);

    // 读取整个文件为字节数组。失败时抛出 Win32Error。
    [[nodiscard]] ZTOOL_API Bytes ReadFileBytes(std::wstring_view path);

    // 写入字节数组到文件；append=true 时追加写入，否则覆盖。
    ZTOOL_API void WriteFileBytes(std::wstring_view path, std::span<const std::byte> data,
                                  bool append = false);

    // 按 UTF-8 文本读取文件。不会自动去除 BOM。
    [[nodiscard]] ZTOOL_API std::string ReadTextFileUtf8(std::wstring_view path);

    // 按 UTF-8 文本写入文件；writeBom=true 时写入 UTF-8 BOM。
    ZTOOL_API void WriteTextFileUtf8(std::wstring_view path, std::string_view text, bool append = false,
                                     bool writeBom = false);

    //endregion

    /* ---------------------------------------------------------------------------- */
    //region 进程与命令行工具
    /* ---------------------------------------------------------------------------- */

    // 启动进程参数。
    // application：可执行文件路径，可为空。
    // commandLine：完整命令行；如果 application 为空，应包含可执行文件。
    // workingDirectory：工作目录，可为空。
    // creationFlags：CreateProcessW 标志。
    // inheritHandles：是否继承句柄。
    // showWindow/showCommand：控制窗口显示状态。
    struct ZTOOL_API ProcessStartInfo {
        std::wstring application;
        std::wstring commandLine;
        std::wstring workingDirectory;
        DWORD creationFlags = 0;
        bool inheritHandles = false;
        bool showWindow = true;
        WORD showCommand = SW_SHOWNORMAL;
    };

    // 启动进程后的结果。
    // process/thread 使用 RAII 管理，离开作用域自动 CloseHandle。
    struct ZTOOL_API ProcessInfo {
        Handle process;
        Handle thread;
        DWORD processId = 0;
        DWORD threadId = 0;
    };

    // 获取当前进程命令行参数，等价于 main/wmain 的 argv。
    [[nodiscard]] ZTOOL_API std::vector<std::wstring> CommandLineArgsW();

    // 将单个参数转义为 Windows 命令行安全格式。
    [[nodiscard]] ZTOOL_API std::wstring QuoteCommandLineArgW(std::wstring_view arg);

    // 把多个参数拼接成安全命令行。
    [[nodiscard]] ZTOOL_API std::wstring BuildCommandLineW(std::span<const std::wstring> args);

    // 启动进程。失败时抛出 Win32Error。
    [[nodiscard]] ZTOOL_API ProcessInfo StartProcess(const ProcessStartInfo &info);

    // 等待进程结束。
    // 返回：进程退出码；如果超时返回 std::nullopt。
    [[nodiscard]] ZTOOL_API std::optional<DWORD> WaitForProcess(HANDLE process,
                                                                DWORD milliseconds = INFINITE);

    // 判断当前进程是否以管理员权限运行。
    [[nodiscard]] ZTOOL_API bool IsProcessElevated();

    // 获取当前 Windows 用户名。
    [[nodiscard]] ZTOOL_API std::wstring UserNameW();

    // 获取计算机名。
    [[nodiscard]] ZTOOL_API std::wstring ComputerNameW();

    // 获取当前进程 ID。
    [[nodiscard]] ZTOOL_API DWORD CurrentProcessId() noexcept;

    // 获取当前线程 ID。
    [[nodiscard]] ZTOOL_API DWORD CurrentThreadId() noexcept;

    //endregion

    /* ---------------------------------------------------------------------------- */
    //region 注册表工具
    /* ---------------------------------------------------------------------------- */

    // HKEY RAII 封装。
    // 作用：自动 RegCloseKey，提供常用注册表读写和枚举功能。
    // 用法：
    //   auto key = Z::RegKey::Open(HKEY_CURRENT_USER, L"Software\\MyApp");
    //   auto name = key.GetStringValue(L"Name");
    class ZTOOL_API RegKey final {
    public:
        RegKey() noexcept = default;

        // 接管已有 HKEY；析构时自动 RegCloseKey。
        explicit RegKey(HKEY key) noexcept;
        ~RegKey() noexcept;

        RegKey(const RegKey &) = delete;
        RegKey &operator=(const RegKey &) = delete;

        RegKey(RegKey &&other) noexcept;
        RegKey &operator=(RegKey &&other) noexcept;

        // 打开注册表键；失败时抛出 Win32Error。
        static RegKey Open(HKEY root, std::wstring_view subKey, REGSAM access = KEY_READ);

        // 尝试打开注册表键；不存在时返回 std::nullopt，其他错误抛异常。
        static std::optional<RegKey> TryOpen(HKEY root, std::wstring_view subKey,
                                             REGSAM access = KEY_READ);

        // 创建或打开注册表键。
        static RegKey Create(HKEY root, std::wstring_view subKey,
                             REGSAM access = KEY_READ | KEY_WRITE);

        [[nodiscard]] HKEY get() const noexcept;
        [[nodiscard]] HKEY *put() noexcept;
        [[nodiscard]] bool valid() const noexcept;
        explicit operator bool() const noexcept;

        [[nodiscard]] HKEY release() noexcept;
        void reset(HKEY key = nullptr) noexcept;

        // 读取字符串值。支持 REG_SZ / REG_EXPAND_SZ；不存在返回 nullopt。
        [[nodiscard]] std::optional<std::wstring> GetStringValue(
            std::wstring_view valueName = {}) const;

        // 写入字符串值。type 通常为 REG_SZ 或 REG_EXPAND_SZ。
        void SetStringValue(std::wstring_view valueName, std::wstring_view value,
                            DWORD type = REG_SZ) const;

        // 读取 DWORD 值；不存在返回 nullopt。
        [[nodiscard]] std::optional<DWORD> GetDwordValue(
            std::wstring_view valueName = {}) const;

        // 写入 DWORD 值。
        void SetDwordValue(std::wstring_view valueName, DWORD value) const;

        // 读取 QWORD 值；不存在返回 nullopt。
        [[nodiscard]] std::optional<unsigned long long> GetQwordValue(
            std::wstring_view valueName = {}) const;

        // 写入 QWORD 值。
        void SetQwordValue(std::wstring_view valueName, unsigned long long value) const;

        // 读取二进制值；不存在返回 nullopt。
        [[nodiscard]] std::optional<Bytes> GetBinaryValue(
            std::wstring_view valueName = {}) const;

        // 写入二进制值。
        void SetBinaryValue(std::wstring_view valueName,
                            std::span<const std::byte> data) const;

        // 枚举子键名。
        [[nodiscard]] std::vector<std::wstring> EnumSubKeys() const;

        // 枚举值名。
        [[nodiscard]] std::vector<std::wstring> EnumValueNames() const;

    private:
        HKEY key_ = nullptr;
    };

    //endregion

    /* ---------------------------------------------------------------------------- */
    //region 窗口工具
    /* ---------------------------------------------------------------------------- */

    // 获取窗口标题文本。
    [[nodiscard]] ZTOOL_API std::wstring WindowTextW(HWND hwnd);

    // 获取窗口标题文本并转换为 UTF-8。
    [[nodiscard]] ZTOOL_API std::string WindowTextUtf8(HWND hwnd);

    // 使用 UTF-8 文本设置窗口标题。
    ZTOOL_API void SetWindowTextUtf8(HWND hwnd, std::string_view text);

    // 获取窗口类名。
    [[nodiscard]] ZTOOL_API std::wstring ClassNameW(HWND hwnd);

    // 获取窗口屏幕坐标矩形。
    [[nodiscard]] ZTOOL_API RECT WindowRect(HWND hwnd);

    // 获取窗口客户区矩形。
    [[nodiscard]] ZTOOL_API RECT ClientRect(HWND hwnd);

    // 将窗口移动到父窗口或屏幕中央。
    ZTOOL_API void CenterWindow(HWND hwnd, HWND parent = nullptr);

    //endregion

    /* ---------------------------------------------------------------------------- */
    //region 剪贴板与控制台工具
    /* ---------------------------------------------------------------------------- */

    // 设置剪贴板 Unicode 文本。
    ZTOOL_API void SetClipboardTextW(std::wstring_view text, HWND owner = nullptr);

    // 读取剪贴板 Unicode 文本；没有文本时返回 nullopt。
    [[nodiscard]] ZTOOL_API std::optional<std::wstring> GetClipboardTextW(HWND owner = nullptr);

    // 将控制台输入/输出代码页设置为 UTF-8。
    // 常用于 main 开头，配合中文日志输出。
    ZTOOL_API void SetConsoleUtf8();

    // 判断当前进程是否已经拥有控制台。
    [[nodiscard]] ZTOOL_API bool HasConsole() noexcept;

    // 尝试附加父进程控制台；失败则分配一个新控制台。
    [[nodiscard]] ZTOOL_API bool AttachOrAllocConsole();

    //endregion

} // namespace Z



#endif //ZCPPWIN_WINTOOL_H
