//go:build windows

package main

import (
	"fmt"
	"strings"
	"syscall"
	"unsafe"

	"hamlog/internal/applog"
)

// GetSystemMetrics indices for the virtual desktop — the rectangle spanning
// every attached monitor.
const (
	smXVirtualScreen  = 76
	smYVirtualScreen  = 77
	smCXVirtualScreen = 78
	smCYVirtualScreen = 79
)

var (
	user32Dll               = syscall.NewLazyDLL("user32.dll")
	procGetSystemMetrics    = user32Dll.NewProc("GetSystemMetrics")
	procEnumDisplayMonitors = user32Dll.NewProc("EnumDisplayMonitors")
	procGetMonitorInfoW     = user32Dll.NewProc("GetMonitorInfoW")
)

func systemMetric(index int) int {
	r, _, _ := procGetSystemMetrics.Call(uintptr(index))
	return int(int32(r)) // signed: the virtual desktop origin is negative with a monitor to the left
}

// virtualScreenBounds returns the rectangle covering all monitors, and whether
// it could be determined at all.
func virtualScreenBounds() (x, y, w, h int, ok bool) {
	w, h = systemMetric(smCXVirtualScreen), systemMetric(smCYVirtualScreen)
	if w <= 0 || h <= 0 {
		return 0, 0, 0, 0, false
	}
	return systemMetric(smXVirtualScreen), systemMetric(smYVirtualScreen), w, h, true
}

// winRect is Win32's RECT.
type winRect struct{ Left, Top, Right, Bottom int32 }

// monitorInfo is MONITORINFO: the monitor's whole rectangle and its work area
// (what is left once the taskbar is taken out).
type monitorInfo struct {
	CbSize    uint32
	RcMonitor winRect
	RcWork    winRect
	DwFlags   uint32
}

// monitorRects enumerates the attached monitors.
//
// THE BOUNDING BOX IS NOT THE DESKTOP. The virtual screen is the rectangle that
// spans every monitor, and monitors are rarely arranged to fill it: a wide
// screen beside a tall one, or one offset vertically, leaves rectangular HOLES
// inside the box that belong to no monitor at all. A window placed in a hole
// passes a bounding-box test and is invisible — which is exactly what was
// reported from a four-monitor station whose window.json held x=-7680.
//
// So the test has to be against the monitors themselves.
func monitorRects() []screenRect {
	var out []screenRect
	cb := syscall.NewCallback(func(hMonitor, hdc uintptr, lprc *winRect, data uintptr) uintptr {
		var mi monitorInfo
		mi.CbSize = uint32(unsafe.Sizeof(mi))
		if r, _, _ := procGetMonitorInfoW.Call(hMonitor, uintptr(unsafe.Pointer(&mi))); r != 0 {
			// The WORK area, not the full rectangle: a title bar under the
			// taskbar is a window that cannot be dragged, which is the fault
			// being guarded against in the first place.
			out = append(out, screenRect{
				X: int(mi.RcWork.Left), Y: int(mi.RcWork.Top),
				W: int(mi.RcWork.Right - mi.RcWork.Left),
				H: int(mi.RcWork.Bottom - mi.RcWork.Top),
			})
		}
		return 1 // keep enumerating
	})
	procEnumDisplayMonitors.Call(0, 0, cb, 0)
	return out
}

// describeMonitors renders the layout for the log. A window that opens where
// nobody can see it is reported as "OpsLog did not start", and the first
// question is what the screens looked like at that moment.
func describeMonitors(rects []screenRect) string {
	if len(rects) == 0 {
		return "none detected"
	}
	parts := make([]string, 0, len(rects))
	for _, r := range rects {
		parts = append(parts, fmt.Sprintf("%dx%d at %d,%d", r.W, r.H, r.X, r.Y))
	}
	return strings.Join(parts, " · ")
}

// onSomeMonitorImpl reports whether a window at these coordinates would land
// where it can be seen and grabbed.
func onSomeMonitorImpl(x, y, w, h int) bool {
	rects := monitorRects()
	if len(rects) == 0 {
		// Enumeration failed. Fall back to the bounding box rather than refuse
		// the operator's own saved position on the strength of a failed call.
		vx, vy, vw, vh, ok := virtualScreenBounds()
		if !ok {
			return true
		}
		return overlapsEnough(x, y, w, h, vx, vy, vw, vh)
	}
	return onAnyScreen(x, y, w, h, rects)
}

// clampToVisible moves a window rectangle onto the monitor it is closest to.
func clampToVisible(x, y, w, h int) (int, int, bool) {
	return clampRectToScreens(x, y, w, h, monitorRects())
}

// logMonitorLayout writes the current screen arrangement once at startup.
func logMonitorLayout() {
	applog.Printf("window: monitors — %s", describeMonitors(monitorRects()))
}
