//go:build windows

package main

import (
	"syscall"
	"unsafe"

	"hamlog/internal/applog"
)

// Moving the window to an ABSOLUTE virtual-desktop position.
//
// Wails' own WindowSetPosition cannot do it. Its Windows implementation reads:
//
//	func (cba *ControlBase) SetPos(x, y int) {
//	    info := getMonitorInfo(cba.hwnd)
//	    w32.SetWindowPos(cba.hwnd, HWND_TOP, int(info.RcWork.Left)+x, int(info.RcWork.Top)+y, ...)
//	}
//
// — the coordinates are relative to the CURRENT monitor's work area, while
// WindowGetPosition returns GetWindowRect, which is absolute. Saving one and
// restoring the other is only harmless on the primary monitor, where the work
// area starts at 0.
//
// On a second monitor to the LEFT it compounds at every launch. Reported from a
// two-screen station with the left monitor at x = -3840: OpsLog saved -3844,
// reopened on that monitor, added the monitor's own origin, and stored -7684 —
// then -11524, each launch one screen further into nowhere.
//
// So we place the window ourselves. Same call the toolkit makes, without the
// offset.
const (
	swpNoSize     = 0x0001
	swpNoZOrder   = 0x0004
	swpNoActivate = 0x0010
)

var (
	procSetWindowPos              = user32Dll.NewProc("SetWindowPos")
	procEnumWindows               = user32Dll.NewProc("EnumWindows")
	procGetWindowThreadProcessID  = user32Dll.NewProc("GetWindowThreadProcessId")
	procGetWindowTextLengthW      = user32Dll.NewProc("GetWindowTextLengthW")
	procGetWindow                 = user32Dll.NewProc("GetWindow")
	kernel32Dll                   = syscall.NewLazyDLL("kernel32.dll")
	procGetCurrentProcessIDWinPos = kernel32Dll.NewProc("GetCurrentProcessId")
)

// mainWindowHandle finds this process's own top-level window.
//
// Wails does not expose the handle, so it is looked up: the first top-level
// window belonging to this process id that has no owner and a title. The window
// is created hidden (StartHidden), and EnumWindows lists hidden windows too,
// which is what makes this usable before the window is shown.
func mainWindowHandle() uintptr {
	self, _, _ := procGetCurrentProcessIDWinPos.Call()
	var found uintptr
	cb := syscall.NewCallback(func(hwnd uintptr, _ uintptr) uintptr {
		var pid uint32
		procGetWindowThreadProcessID.Call(hwnd, uintptr(unsafe.Pointer(&pid)))
		if uintptr(pid) != self {
			return 1 // keep going
		}
		// GW_OWNER = 4: skip tool windows and dialogs owned by the main one.
		if owner, _, _ := procGetWindow.Call(hwnd, 4); owner != 0 {
			return 1
		}
		if n, _, _ := procGetWindowTextLengthW.Call(hwnd); n == 0 {
			return 1
		}
		found = hwnd
		return 0 // stop
	})
	procEnumWindows.Call(cb, 0)
	return found
}

// setWindowPosAbsolute moves the window to a virtual-desktop coordinate.
// Reports whether it could; the caller falls back to the toolkit's own call.
func setWindowPosAbsolute(x, y int) bool {
	hwnd := mainWindowHandle()
	if hwnd == 0 {
		applog.Printf("window: could not find our own window handle — falling back to the toolkit's placement")
		return false
	}
	r, _, err := procSetWindowPos.Call(hwnd, 0, uintptr(int32(x)), uintptr(int32(y)), 0, 0,
		swpNoSize|swpNoZOrder|swpNoActivate)
	if r == 0 {
		applog.Printf("window: SetWindowPos(%d,%d) failed: %v", x, y, err)
		return false
	}
	return true
}
