package main

// Placing a window on a set of monitors — the arithmetic, with no Win32 in it.
//
// Kept apart from the platform code so it can be tested: the fault it guards
// against (a window that opens where nobody can see it) is reported as "OpsLog
// does not start", is invisible by definition, and cannot be reproduced without
// the reporter's screen layout. A table test can hold that layout.

// screenRect is one monitor's work area, in virtual-desktop coordinates.
type screenRect struct{ X, Y, W, H int }

// screenRectOf is the same shape under the name the helpers below read with.
type screenRectOf = screenRect

// onAnyScreen reports whether a window would land where it can be seen and
// grabbed on ONE of the screens.
//
// One of them, not their bounding box: monitors rarely tile the box they span,
// and the leftover rectangles belong to no screen at all. A window in one of
// those holes passes a bounding-box test and is invisible.
func onAnyScreen(x, y, w, h int, screens []screenRectOf) bool {
	for _, r := range screens {
		if overlapsEnough(x, y, w, h, r.X, r.Y, r.W, r.H) {
			return true
		}
	}
	return false
}

// nearestScreen picks the screen whose centre is closest to the window's.
func nearestScreen(x, y, w, h int, screens []screenRectOf) (screenRectOf, bool) {
	if len(screens) == 0 {
		return screenRectOf{}, false
	}
	best, bestDist := screens[0], int64(1)<<62
	cx, cy := x+w/2, y+h/2
	for _, r := range screens {
		rcx, rcy := r.X+r.W/2, r.Y+r.H/2
		dx, dy := int64(cx-rcx), int64(cy-rcy)
		if d := dx*dx + dy*dy; d < bestDist {
			best, bestDist = r, d
		}
	}
	return best, true
}

// clampRectToScreens moves a window onto the nearest screen, keeping its size
// where the screen can hold it. Returns the new position and whether it moved.
func clampRectToScreens(x, y, w, h int, screens []screenRectOf) (int, int, bool) {
	best, ok := nearestScreen(x, y, w, h, screens)
	if !ok {
		return x, y, false
	}
	if w > best.W {
		w = best.W
	}
	if h > best.H {
		h = best.H
	}
	nx, ny := x, y
	if nx < best.X {
		nx = best.X
	}
	if ny < best.Y {
		ny = best.Y
	}
	if nx+w > best.X+best.W {
		nx = best.X + best.W - w
	}
	if ny+h > best.Y+best.H {
		ny = best.Y + best.H - h
	}
	return nx, ny, nx != x || ny != y
}
