package main

// Reserved-chord tables.
//
// These are chords the operating system (or its shell) claims for itself. An
// application binding that lands on one of them either never fires or fires
// unpredictably, so WindowDeck flags it.
//
// This is a CURATED list of the chords that actually bite teams in practice.
// It is not, and does not pretend to be, an exhaustive dump of every shortcut
// either OS ships. See "SCOPE / WHAT THIS DOES NOT DO" in README.txt.

import "sort"

// OS identifiers used throughout.
const (
	OSWindows = "windows"
	OSMacOS   = "macos"
)

// ReservedChord is one entry of a built-in table.
type ReservedChord struct {
	OS     string `json:"os"`
	Chord  string `json:"chord"`
	Does   string `json:"does"`
	Source string `json:"source"`
}

const (
	srcWin = `Microsoft, "Keyboard shortcuts in Windows" (support.microsoft.com)`
	srcMac = `Apple, "Mac keyboard shortcuts" (support.apple.com/HT201236)`
)

// reservedRaw is the table as written by a human. Chords are normalised on
// startup by buildReserved, so entries here may use any accepted spelling.
var reservedRaw = []ReservedChord{
	// --- Windows ------------------------------------------------------------
	{OSWindows, "Ctrl+Alt+Delete", "open the security screen (Secure Attention Sequence; an application can never intercept it)", srcWin},
	{OSWindows, "Ctrl+Shift+Escape", "open Task Manager", srcWin},
	{OSWindows, "Alt+Tab", "switch between open windows", srcWin},
	{OSWindows, "Alt+F4", "close the active window, or shut down from the desktop", srcWin},
	{OSWindows, "Win+L", "lock the workstation / switch user", srcWin},
	{OSWindows, "Win+D", "show or restore the desktop", srcWin},
	{OSWindows, "Win+E", "open File Explorer", srcWin},
	{OSWindows, "Win+R", "open the Run dialog", srcWin},
	{OSWindows, "Win+S", "open Search", srcWin},
	{OSWindows, "Win+I", "open Settings", srcWin},
	{OSWindows, "Win+A", "open the notification / quick settings pane", srcWin},
	{OSWindows, "Win+X", "open the Quick Link (power user) menu", srcWin},
	{OSWindows, "Win+Tab", "open Task View", srcWin},
	{OSWindows, "Win+Space", "switch the input language and keyboard layout", srcWin},
	{OSWindows, "Win+Period", "open the emoji panel", srcWin},
	{OSWindows, "Win+PrintScreen", "capture the screen to a file", srcWin},
	{OSWindows, "Win+Shift+S", "start a screen snip (Snipping Tool)", srcWin},
	{OSWindows, "Win+Left", "snap the active window to the left half", srcWin},
	{OSWindows, "Win+Right", "snap the active window to the right half", srcWin},
	{OSWindows, "Win+Up", "maximise the active window", srcWin},
	{OSWindows, "Win+Down", "restore or minimise the active window", srcWin},
	{OSWindows, "Win+Ctrl+D", "create a new virtual desktop", srcWin},
	{OSWindows, "Win+Ctrl+Left", "switch to the virtual desktop on the left", srcWin},
	{OSWindows, "Win+Ctrl+Right", "switch to the virtual desktop on the right", srcWin},
	{OSWindows, "F11", "toggle full screen in File Explorer and every major browser", srcWin},

	// --- macOS --------------------------------------------------------------
	{OSMacOS, "Cmd+Space", "open Spotlight search", srcMac},
	{OSMacOS, "Cmd+Alt+Space", "open a Finder search window", srcMac},
	{OSMacOS, "Cmd+Tab", "switch between open applications", srcMac},
	{OSMacOS, "Cmd+Q", "quit the active application", srcMac},
	{OSMacOS, "Cmd+W", "close the active window", srcMac},
	{OSMacOS, "Cmd+H", "hide the active application", srcMac},
	{OSMacOS, "Cmd+M", "minimise the active window to the Dock", srcMac},
	{OSMacOS, "Cmd+Comma", "open the active application's Settings/Preferences", srcMac},
	{OSMacOS, "Cmd+Alt+Escape", "open Force Quit Applications", srcMac},
	{OSMacOS, "Ctrl+Cmd+Q", "lock the screen", srcMac},
	{OSMacOS, "Ctrl+Cmd+F", "toggle full screen for the active window", srcMac},
	{OSMacOS, "Cmd+Shift+3", "capture the whole screen to a screenshot", srcMac},
	{OSMacOS, "Cmd+Shift+4", "capture a selected area to a screenshot", srcMac},
	{OSMacOS, "Cmd+Shift+5", "open the screenshot and screen-recording panel", srcMac},
	{OSMacOS, "Cmd+Shift+Q", "log out of the current user account", srcMac},
	{OSMacOS, "Cmd+Alt+D", "show or hide the Dock", srcMac},
	{OSMacOS, "Ctrl+Up", "open Mission Control", srcMac},
	{OSMacOS, "Ctrl+Down", "open Application Windows (App Expose)", srcMac},
	{OSMacOS, "Ctrl+Left", "move one space to the left", srcMac},
	{OSMacOS, "Ctrl+Right", "move one space to the right", srcMac},
	{OSMacOS, "Cmd+Ctrl+Space", "open the Character/Emoji viewer", srcMac},
	{OSMacOS, "F11", "show the desktop (Mission Control default)", srcMac},
}

// reservedByChord indexes the tables by canonical single-chord string.
var reservedByChord = buildReserved()

func buildReserved() map[string][]ReservedChord {
	m := map[string][]ReservedChord{}
	for _, r := range reservedRaw {
		seq := mustSeq(r.Chord)
		if len(seq) != 1 {
			panic("windowdeck: reserved entries must be single chords: " + r.Chord)
		}
		r.Chord = seq[0].String()
		m[r.Chord] = append(m[r.Chord], r)
	}
	for k := range m {
		v := m[k]
		sort.Slice(v, func(i, j int) bool {
			if v[i].OS != v[j].OS {
				return v[i].OS < v[j].OS
			}
			return v[i].Does < v[j].Does
		})
		m[k] = v
	}
	return m
}

// ReservedHit is a reserved-table match against one chord of a binding.
type ReservedHit struct {
	ReservedChord
	// Position is the 1-based index of the offending chord inside the binding's
	// sequence. It is 1 for an ordinary single-chord binding.
	Position int `json:"position"`
}

// reservedHits returns every reserved-table match for a sequence, in a stable
// order: by position, then OS, then description.
func reservedHits(seq Sequence) []ReservedHit {
	var hits []ReservedHit
	for i, c := range seq {
		for _, r := range reservedByChord[c.String()] {
			hits = append(hits, ReservedHit{ReservedChord: r, Position: i + 1})
		}
	}
	sort.SliceStable(hits, func(i, j int) bool {
		if hits[i].Position != hits[j].Position {
			return hits[i].Position < hits[j].Position
		}
		if hits[i].OS != hits[j].OS {
			return hits[i].OS < hits[j].OS
		}
		return hits[i].Does < hits[j].Does
	})
	return hits
}

// isReservedAnywhere reports whether a single chord is claimed by either OS.
func isReservedAnywhere(c Chord) bool {
	return len(reservedByChord[c.String()]) > 0
}
