package main

import (
	"log"
	"unsafe"

	"github.com/jchv/go-webview2"
	"golang.org/x/sys/windows"
)

var (
	user32                  = windows.NewLazySystemDLL("user32.dll")
	procFindWindowW         = user32.NewProc("FindWindowW")
	procFindWindowExW       = user32.NewProc("FindWindowExW")
	procSendMessageTimeoutW = user32.NewProc("SendMessageTimeoutW")
	procSetParent           = user32.NewProc("SetParent")
	procGetForegroundWindow = user32.NewProc("GetForegroundWindow")
	procGetWindowRect       = user32.NewProc("GetWindowRect")
	procGetSystemMetrics    = user32.NewProc("GetSystemMetrics")
	procMoveWindow          = user32.NewProc("MoveWindow")
	procShowWindow          = user32.NewProc("ShowWindow")
	procSetProcessDPIAware  = user32.NewProc("SetProcessDPIAware")
	procSetWindowLongPtrW   = user32.NewProc("SetWindowLongPtrW")
	procGetWindowLongPtrW   = user32.NewProc("GetWindowLongPtrW")
	procGetDpiForWindow     = user32.NewProc("GetDpiForWindow")
	procGetClientRect       = user32.NewProc("GetClientRect")
	procMessageBoxW         = user32.NewProc("MessageBoxW")
	procGetMessageW         = user32.NewProc("GetMessageW")
	procPostMessageW        = user32.NewProc("PostMessageW")
	procTranslateMessage    = user32.NewProc("TranslateMessage")
	procDispatchMessageW    = user32.NewProc("DispatchMessageW")
	procGetParent           = user32.NewProc("GetParent")
)

var wv webview2.WebView
var wvHwnd uintptr
var jsQueue = make(chan string, 64)
var paused int32

const (
	wmEvalJS  = 0x0401
	wmSetHtml = 0x0402
)

const (
	gwlStyle   = uintptr(0xFFFFFFF0) // GWL_STYLE = -16
	gwlExStyle = uintptr(0xFFFFFFFEC) // GWL_EXSTYLE = -20
	wsPopup    = uintptr(0x80000000)
	wsVisible  = uintptr(0x10000000)
	wsChild    = uintptr(0x02000000)
	wsSizebox  = uintptr(0x00040000)
	wsMaxbox   = uintptr(0x00010000)
)

const (
	wsExTransparent = uintptr(0x00000020)
	wsExLayered     = uintptr(0x00080000)
	wsExToolwindow  = uintptr(0x00000080)
)

var htmlQueue = make(chan string, 1)

var (
	classProgman      = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("Progman")))
	classShellDefView = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("SHELLDLL_DefView")))
	classWorkerW      = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("WorkerW")))
)

func evalJS(js string) {
	select {
	case jsQueue <- js:
		log.Println("evalJS queued:", js[:min(len(js), 60)])
	default:
		log.Println("jsQueue full, dropping eval")
	}
	if wvHwnd != 0 {
		procPostMessageW.Call(wvHwnd, wmEvalJS, 0, 0)
	}
}

func findWorkerW() uintptr {
	progman, _, _ := procFindWindowW.Call(
		classProgman, 0)
	if progman == 0 {
		log.Println("findWorkerW: Progman not found")
		return 0
	}
	// 发送 0x052C 触发 Progman 创建 WorkerW
	var result uintptr
	procSendMessageTimeoutW.Call(progman, 0x052C, 0, 0, 0x0000, 1000, uintptr(unsafe.Pointer(&result)))

	// 方法1: Progman 下找 SHELLDLL_DefView，再找其后的 WorkerW
	shellDefView, _, _ := procFindWindowExW.Call(progman, 0, classShellDefView, 0)
	if shellDefView != 0 {
		ww, _, _ := procFindWindowExW.Call(progman, shellDefView, classWorkerW, 0)
		if ww != 0 {
			log.Printf("findWorkerW: 方法1成功, WorkerW=0x%x", ww)
			return ww
		}
	}

	// 方法2: 遍历顶层 WorkerW，找到含 SHELLDLL_DefView 的那个，取它后面的 WorkerW
	var prev uintptr
	for {
		ww, _, _ := procFindWindowExW.Call(0, prev, classWorkerW, 0)
		if ww == 0 {
			break
		}
		child, _, _ := procFindWindowExW.Call(ww, 0, classShellDefView, 0)
		if child != 0 {
			// 这个 WorkerW 包含 SHELLDLL_DefView，找它后面的 WorkerW
			next, _, _ := procFindWindowExW.Call(0, ww, classWorkerW, 0)
			if next != 0 {
				log.Printf("findWorkerW: 方法2成功, WorkerW=0x%x (after 0x%x)", next, ww)
				return next
			}
		}
		prev = ww
	}

	// 方法3: 遍历顶层 WorkerW，找任意不含 SHELLDLL_DefView 的可见 WorkerW
	prev = 0
	for {
		ww, _, _ := procFindWindowExW.Call(0, prev, classWorkerW, 0)
		if ww == 0 {
			break
		}
		child, _, _ := procFindWindowExW.Call(ww, 0, classShellDefView, 0)
		if child == 0 {
			log.Printf("findWorkerW: 方法3成功, WorkerW=0x%x", ww)
			return ww
		}
		prev = ww
	}

	// 方法4: 兜底，Progman 下的第一个 WorkerW
	ww, _, _ := procFindWindowExW.Call(progman, 0, classWorkerW, 0)
	if ww != 0 {
		log.Printf("findWorkerW: 方法4(兜底), WorkerW=0x%x", ww)
	}
	return ww
}

func getDPI(hwnd uintptr) int {
	dpi, _, _ := procGetDpiForWindow.Call(hwnd)
	if dpi == 0 {
		return 96
	}
	return int(dpi)
}

func getScreenSize() (int32, int32) {
	w, _, _ := procGetSystemMetrics.Call(0)
	h, _, _ := procGetSystemMetrics.Call(1)
	return int32(w), int32(h)
}
