import ctypes
import ctypes.wintypes
import os

def show_save_dialog_win32():
    """
    使用 ctypes 直接调用 Windows API (GetSaveFileNameW)
    不需要子进程，可以在线程中运行 (run.io_bound)
    """
    try:
        # 定义 OPENFILENAME 结构体
        class OPENFILENAME(ctypes.Structure):
            _fields_ = [
                ("lStructSize", ctypes.wintypes.DWORD),
                ("hwndOwner", ctypes.wintypes.HWND),
                ("hInstance", ctypes.wintypes.HINSTANCE),
                ("lpstrFilter", ctypes.wintypes.LPCWSTR),
                ("lpstrCustomFilter", ctypes.wintypes.LPWSTR),
                ("nMaxCustFilter", ctypes.wintypes.DWORD),
                ("nFilterIndex", ctypes.wintypes.DWORD),
                ("lpstrFile", ctypes.wintypes.LPWSTR),
                ("nMaxFile", ctypes.wintypes.DWORD),
                ("lpstrFileTitle", ctypes.wintypes.LPWSTR),
                ("nMaxFileTitle", ctypes.wintypes.DWORD),
                ("lpstrInitialDir", ctypes.wintypes.LPCWSTR),
                ("lpstrTitle", ctypes.wintypes.LPCWSTR),
                ("Flags", ctypes.wintypes.DWORD),
                ("nFileOffset", ctypes.wintypes.WORD),
                ("nFileExtension", ctypes.wintypes.WORD),
                ("lpstrDefExt", ctypes.wintypes.LPCWSTR),
                ("lCustData", ctypes.wintypes.LPARAM),
                ("lpfnHook", ctypes.wintypes.LPVOID),
                ("lpTemplateName", ctypes.wintypes.LPCWSTR),
                # 还有更多字段，但这通常足够了
                # ("pvReserved", ctypes.wintypes.LPVOID),
                # ("dwReserved", ctypes.wintypes.DWORD),
                # ("FlagsEx", ctypes.wintypes.DWORD),
            ]

        # 准备缓冲区
        filename_buffer = ctypes.create_unicode_buffer(260) # MAX_PATH
        # 设置初始文件名
        filename_buffer.value = "win32_save.xlsx"
        
        # 准备过滤器 (用 \0 分隔)
        # 格式: "描述\0模式\0描述\0模式\0\0"
        filter_str = "Excel Files (*.xlsx)\0*.xlsx\0All Files (*.*)\0*.*\0\0"
        
        ofn = OPENFILENAME()
        ofn.lStructSize = ctypes.sizeof(OPENFILENAME)
        ofn.hwndOwner = 0 # NULL
        ofn.lpstrFilter = filter_str
        ofn.lpstrFile = ctypes.cast(filename_buffer, ctypes.wintypes.LPWSTR)
        ofn.nMaxFile = 260
        ofn.lpstrDefExt = "xlsx"
        ofn.lpstrTitle = "保存文件 (Win32 API)"
        # OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR
        ofn.Flags = 0x00000002 | 0x00000800 | 0x00000008 
        
        comdlg32 = ctypes.windll.comdlg32
        
        # 调用 API
        # GetSaveFileNameW 返回非零值表示成功
        if comdlg32.GetSaveFileNameW(ctypes.byref(ofn)):
            return filename_buffer.value
        else:
            return None
            
    except Exception as e:
        print(f"Win32 API Error: {e}")
        return None


def show_save_dialog_com():
    """
    使用 COM 接口 IFileSaveDialog (Windows Vista+)
    提供更现代化的文件保存对话框，支持更多功能
    """
    try:
        import ctypes
        import ctypes.wintypes
        import uuid

        # 定义必要的常量
        CLSCTX_INPROC_SERVER = 1
        S_OK = 0
        FOS_OVERWRITEPROMPT = 0x00000002
        FOS_PATHMUSTEXIST = 0x00000800
        FOS_NOCHANGEDIR = 0x00000008
        SIGDN_FILESYSPATH = 0x80058000

        # IFileSaveDialog 的 CLSID 和 IID
        CLSID_FileSaveDialog = uuid.UUID("{C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}")
        IID_IFileSaveDialog = uuid.UUID("{84bccd23-5fde-4cdb-aea4-af64b83d78ab}")
        IID_IShellItem = uuid.UUID("{43826d1e-e718-42ee-bc55-a1e261c37bfe}")

        # 加载 ole32.dll
        ole32 = ctypes.windll.ole32

        # CoInitialize
        ole32.CoInitialize(None)

        # CoCreateInstance
        p_dialog = ctypes.c_void_p()
        hr = ole32.CoCreateInstance(
            ctypes.byref(CLSID_FileSaveDialog),
            None,
            CLSCTX_INPROC_SERVER,
            ctypes.byref(IID_IFileSaveDialog),
            ctypes.byref(p_dialog)
        )

        if hr != S_OK:
            print(f"CoCreateInstance failed: {hr}")
            return None

        # 定义 IFileSaveDialog 的 vtable 方法
        # 我们只需要调用 Show, GetResult, SetOptions, SetFileName, SetDefaultExtension, SetFileTypeIndex
        # 这些方法在 IFileOpenDialog 基类中定义

        # SetOptions
        class IFileSaveDialogVtbl(ctypes.Structure):
            _fields_ = [
                ("QueryInterface", ctypes.c_void_p),
                ("AddRef", ctypes.c_void_p),
                ("Release", ctypes.c_void_p),
                # IModalWindow
                ("Show", ctypes.c_void_p),
                # IFileDialog
                ("SetFileTypes", ctypes.c_void_p),
                ("SetFileTypeIndex", ctypes.c_void_p),
                ("GetFileTypeIndex", ctypes.c_void_p),
                ("Advise", ctypes.c_void_p),
                ("Unadvise", ctypes.c_void_p),
                ("SetOptions", ctypes.CFUNCTYPE(ctypes.c_long, ctypes.c_void_p, ctypes.c_ulong)),
                ("GetOptions", ctypes.c_void_p),
                ("SetDefaultFolder", ctypes.c_void_p),
                ("SetFolder", ctypes.c_void_p),
                ("GetFolder", ctypes.c_void_p),
                ("GetCurrentSelection", ctypes.c_void_p),
                ("SetFileName", ctypes.CFUNCTYPE(ctypes.c_long, ctypes.c_void_p, ctypes.c_wchar_p)),
                ("GetFileName", ctypes.c_void_p),
                ("SetTitle", ctypes.c_void_p),
                ("SetOkButtonLabel", ctypes.c_void_p),
                ("SetFileNameLabel", ctypes.c_void_p),
                ("GetResult", ctypes.CFUNCTYPE(ctypes.c_long, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p))),
                ("AddPlace", ctypes.c_void_p),
                ("SetDefaultExtension", ctypes.CFUNCTYPE(ctypes.c_long, ctypes.c_void_p, ctypes.c_wchar_p)),
                ("Close", ctypes.c_void_p),
                ("SetClientGuid", ctypes.c_void_p),
                ("ClearClientData", ctypes.c_void_p),
                ("SetFilter", ctypes.c_void_p),
            ]

        # 获取 vtable
        vtable = ctypes.cast(p_dialog, ctypes.POINTER(ctypes.POINTER(IFileSaveDialogVtbl))).contents.contents

        # 调用 SetOptions
        hr = vtable.SetOptions(p_dialog, FOS_OVERWRITEPROMPT | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR)
        if hr != S_OK:
            print(f"SetOptions failed: {hr}")
            return None

        # 调用 SetFileName
        hr = vtable.SetFileName(p_dialog, "com_save.xlsx")
        if hr != S_OK:
            print(f"SetFileName failed: {hr}")
            return None

        # 调用 SetDefaultExtension
        hr = vtable.SetDefaultExtension(p_dialog, "xlsx")
        if hr != S_OK:
            print(f"SetDefaultExtension failed: {hr}")
            return None

        # 调用 SetFileTypeIndex
        hr = vtable.SetFileTypeIndex(p_dialog, 1)
        if hr != S_OK:
            print(f"SetFileTypeIndex failed: {hr}")
            return None

        # 调用 Show
        hr = vtable.Show(p_dialog, 0)  # 0 表示没有父窗口
        if hr != S_OK:
            # 用户取消
            return None

        # 调用 GetResult
        p_result = ctypes.c_void_p()
        hr = vtable.GetResult(p_dialog, ctypes.byref(p_result))
        if hr != S_OK:
            print(f"GetResult failed: {hr}")
            return None

        # 定义 IShellItem 接口
        class IShellItemVtbl(ctypes.Structure):
            _fields_ = [
                ("QueryInterface", ctypes.c_void_p),
                ("AddRef", ctypes.c_void_p),
                ("Release", ctypes.c_void_p),
                ("BindToHandler", ctypes.c_void_p),
                ("GetParent", ctypes.c_void_p),
                ("GetDisplayName", ctypes.CFUNCTYPE(ctypes.c_long, ctypes.c_void_p, ctypes.c_ulong, ctypes.POINTER(ctypes.c_wchar_p))),
                ("GetAttributes", ctypes.c_void_p),
                ("Compare", ctypes.c_void_p),
            ]

        # 获取 IShellItem 的 vtable
        result_vtable = ctypes.cast(p_result, ctypes.POINTER(ctypes.POINTER(IShellItemVtbl))).contents.contents

        # 调用 GetDisplayName
        p_display_name = ctypes.c_wchar_p()
        hr = result_vtable.GetDisplayName(p_result, SIGDN_FILESYSPATH, ctypes.byref(p_display_name))
        if hr != S_OK:
            print(f"GetDisplayName failed: {hr}")
            return None

        filepath = p_display_name.value

        # 清理
        ole32.CoUninitialize()

        return filepath

    except ImportError as e:
        print(f"COM Error: {e}")
        return None
    except Exception as e:
        print(f"COM Error: {e}")
        import traceback
        traceback.print_exc()
        return None
