package action

// TODO: No more global settings, window-wide settings
type WinOptions struct {
	// Number bool
	// Wrap bool
	// Relnumber bool
	ScrollOff int
}

type Window struct {
	Id     int
	Number int // Ignored for now, will be used when splits come into play
	Buffer *Buffer

	Cursor Position // DO NOT MODIFY DIRECTLY, USE SETTERS
	Anchor Position

	ScrollY int
	Height  int
	Width   int

	// Folds TODO
	Options WinOptions
}

// ==================================================
// Helper methods
// ==================================================

// Window.ClampCursor: Clamps the cursor in the all directions to ensure the cursor
// does not go into an invalid position. Such as negative values or past the end of
// the line. In the Y direction it validates that the cursor does not pass the end
// of the content or attempt to be "above" the content (negative value).
func (w *Window) clampCursor() {
	// Clamp line to valid range [0, lineCount-1]
	maxLine := w.Buffer.LineCount() - 1
	if maxLine < 0 {
		maxLine = 0 // Empty buffer edge case
	}
	if w.Cursor.Line < 0 {
		w.Cursor.Line = 0
	} else if w.Cursor.Line > maxLine {
		w.Cursor.Line = maxLine
	}

	// Clamp column to valid range [0, lineLen]
	lineLen := len(w.Buffer.Lines[w.Cursor.Line]) // Safe now - Line is valid
	if w.Cursor.Col < 0 {
		w.Cursor.Col = 0
	} else if lineLen == 0 {
		w.Cursor.Col = 0
	} else if w.Cursor.Col >= lineLen {
		w.Cursor.Col = lineLen // Allow cursor after last char (insert mode)
	}
}

// Window.AdjustScroll ensures the cursor stays within the height with scrollOff margins.
// Call this after any cursor movement.
func (w *Window) AdjustScroll() {
	if w.Height <= 0 {
		return
	}

	// Effective scrollOff (can't be more than half the viewport)
	off := min(w.Options.ScrollOff, w.Height/2)

	// Cursor too close to top — scroll up
	if w.Cursor.Line < w.ScrollY+off {
		w.ScrollY = w.Cursor.Line - off
	}

	// Cursor too close to bottom — scroll down
	if w.Cursor.Line > w.ScrollY+w.Height-1-off {
		w.ScrollY = w.Cursor.Line - w.Height + 1 + off
	}

	// Clamp scrollY to valid range
	maxScroll := max(0, w.Buffer.LineCount()-w.Height)
	w.ScrollY = max(0, min(w.ScrollY, maxScroll))
}

// ==================================================
// Setters
// ==================================================

// Window.SetNumber: Sets the position-based number of this window. Currently ignored
// until splits are implemented.
func (w *Window) SetNumber(number int) {
	w.Number = number
}

// Window.SetBuffer: Sets the buffer that this window should display. This is used when
// switching between buffers or opening a new file in the current window.
func (w *Window) SetBuffer(buffer *Buffer) {
	w.Buffer = buffer
}

// Window.SetCursor: Sets the cursor position in this window to the given position.
func (w *Window) SetCursor(cursor Position) {
	w.Cursor = cursor
	w.clampCursor()
}

// Window.SetCursorLine: Sets the line number of the cursor position.
func (w *Window) SetCursorLine(line int) {
	w.Cursor.Line = line
	w.clampCursor()
}

// Window.SetCursorCol: Sets the column number of the cursor position.
func (w *Window) SetCursorCol(col int) {
	w.Cursor.Col = col
	w.clampCursor()
}

// Window.SetCursorPos: Sets both the line and column of the cursor position. This is
// a convenience method for setting both components at once.
func (w *Window) SetCursorPos(line, col int) {
	w.Cursor.Line = line
	w.Cursor.Col = col
	w.clampCursor()
}

// Window.SetAnchor: Sets the anchor position in this window. The anchor is used for
// visual mode selections as the starting point of the selection.
func (w *Window) SetAnchor(anchor Position) {
	w.Anchor = anchor
}

// Window.SetAnchorLine: Sets the line number of the anchor position.
func (w *Window) SetAnchorLine(line int) {
	w.Anchor.Line = line
}

// Window.SetAnchorCol: Sets the column number of the anchor position.
func (w *Window) SetAnchorCol(col int) {
	w.Anchor.Col = col
}

// Window.SetAnchorPos: Sets both the line and column of the anchor position. This is
// a convenience method for setting both components at once.
func (w *Window) SetAnchorPos(line, col int) {
	w.Anchor.Line = line
	w.Anchor.Col = col
}

// Window.SetScrollY: Sets the vertical scroll offset of this window. This determines
// which line appears at the top of the visible viewport.
func (w *Window) SetScrollY(scrollY int) {
	w.ScrollY = scrollY
}

// Window.SetHeight: Sets the height of this window in lines.
func (w *Window) SetHeight(height int) {
	w.Height = height
}

// Window.SetWidth: Sets the width of this window in columns.
func (w *Window) SetWidth(width int) {
	w.Width = width
}

// Window.SetDimensions: Sets both the width and height of this window. This is a
// convenience method for setting both dimensions at once.
func (w *Window) SetDimensions(width, height int) {
	w.Width = width
	w.Height = height
}
