// 基于 CFFI 的 Windows GUI 编程
// 仓颉语言团队 刘俊杰 2024.11.04
package windows
import std.math.sin

unsafe main() {
    let instance = GetModuleHandleA(EMPTY_STRING)
    // 注册窗口类
    let className = LibC.mallocCString('Cangjie Window')
    var windowClass = WNDCLASSEX(lpszClassName: className,
        hInstance: instance,
        lpfnWndProc: onMessage,
        hbrBackground: CreateSolidBrush(0x00FFFFFF)
    )
    if (RegisterClassExA(inout windowClass) == 0) {
        println('RegisterClass Failed: ${GetLastError()}')
        return
    }
    // 创建窗口实例
    let windowName = LibC.mallocCString('Cangjie')
    let window = CreateWindowExA(
        0,                                   // 扩展样式
        className,                           // 窗口类名
        windowName,                          // 窗口标题
        WS_OVERLAPPEDWINDOW,                 // 窗口风格
        CW_USEDEFAULT, CW_USEDEFAULT,        // 窗口位置
        500, 500,                            // 窗口大小
        NULL,                                // 父窗口句柄
        NULL,                                // 菜单句柄
        instance,                            // 实例句柄
        NULL                                 // 附加参数
    )
    if (window.isNull()) {
        println('CreateWindow Failed: ${GetLastError()}')
        return
    }
    // 显示窗口
    ShowWindow(window, SW_SHOWNORMAL)
    UpdateWindow(window)
    // 启动消息循环
    var message = MSG()
    while (GetMessageA(inout message, NULL, 0, 0)) {
        TranslateMessage(inout message)
        DispatchMessageA(inout message)
    }
    // 退出消息循环
    println('Out of Message Loop')
    LibC.free(className)
    LibC.free(windowName)
}

func paint(hWnd: Handle, draw: (hDC: Handle) -> Unit) {
    var ps = PAINTSTRUCT()
    let hDC = unsafe { BeginPaint(hWnd, inout ps) }
    draw(hDC)
    unsafe { EndPaint(hWnd, inout ps) }
}

let points = fractal(100000, 80.0) // 分形树坐标数组
unsafe func process(hWnd: Handle, msg: UInt32,
        wParam: UInt64, lParam: UInt64) {
    var result = 0
    if (msg == WM_PAINT) {
        paint(hWnd) { hDC =>
            var rect = RECT()
            GetClientRect(hWnd, inout rect)
            let (width, height) = (rect.right, rect.bottom)
            for ((x, y) in points) {
                let u = Int32(x) + width / 2
                let v = height - Int32(y) - 50
                SetPixel(hDC, u, v, 0x559690)
            }
        }
    } else if (msg == WM_KEYDOWN && wParam == UInt64(VK_ESCAPE)) {
        DestroyWindow(hWnd)
    } else if (msg == WM_DESTROY) {
        PostQuitMessage(0)
    } else {
        result = DefWindowProcA(hWnd, msg, wParam, lParam)
    }
    return result
}

@C
@CallingConv[STDCALL]
func onMessage(hWnd: Handle, msg: UInt32,
        wParam: UInt64, lParam: UInt64): Int64 {
    unsafe { process(hWnd, msg, wParam, lParam) }
}