// Package layout provides a simple API for creating text-based layouts
// that can be rendered to fbdraw grids for display in a carousel.
package layout

import (
	"fmt"
	"image/color"

	"git.eeqj.de/sneak/hdmistat/internal/fbdraw"
	"git.eeqj.de/sneak/hdmistat/internal/font"
)

// Font represents a bundled monospace font.
type Font int

const (
	// PlexMono is IBM Plex Mono, a modern monospace font with good readability.
	PlexMono Font = iota
	// Terminus is a bitmap font optimized for long-term reading on terminals.
	Terminus
	// SourceCodePro is Adobe's Source Code Pro, designed for coding environments.
	SourceCodePro
)

// Color returns a standard color by name
func Color(name string) color.Color {
	switch name {
	case "black":
		return color.RGBA{0, 0, 0, 255}
	case "white":
		return color.RGBA{255, 255, 255, 255}
	case "red":
		return color.RGBA{255, 0, 0, 255}
	case "green":
		return color.RGBA{0, 255, 0, 255}
	case "blue":
		return color.RGBA{0, 0, 255, 255}
	case "yellow":
		return color.RGBA{255, 255, 0, 255}
	case "cyan":
		return color.RGBA{0, 255, 255, 255}
	case "magenta":
		return color.RGBA{255, 0, 255, 255}
	case "orange":
		return color.RGBA{255, 165, 0, 255}
	case "purple":
		return color.RGBA{128, 0, 128, 255}
	case "gray10":
		return color.RGBA{26, 26, 26, 255}
	case "gray20":
		return color.RGBA{51, 51, 51, 255}
	case "gray30":
		return color.RGBA{77, 77, 77, 255}
	case "gray40":
		return color.RGBA{102, 102, 102, 255}
	case "gray50":
		return color.RGBA{128, 128, 128, 255}
	case "gray60":
		return color.RGBA{153, 153, 153, 255}
	case "gray70":
		return color.RGBA{179, 179, 179, 255}
	case "gray80":
		return color.RGBA{204, 204, 204, 255}
	case "gray90":
		return color.RGBA{230, 230, 230, 255}
	default:
		return color.RGBA{255, 255, 255, 255} // Default to white
	}
}

// Draw provides the drawing context for creating a layout.
// It maintains state for font, size, colors, and text styling.
type Draw struct {
	// Drawing state
	font     Font
	fontSize int
	bold     bool
	italic   bool
	fgColor  color.Color
	bgColor  color.Color

	// Grid to render to
	grid *fbdraw.CharGrid

	// Cached dimensions
	Width  int
	Height int
}

// NewDraw creates a new drawing context with the specified dimensions
func NewDraw(width, height int) *Draw {
	grid := fbdraw.NewCharGrid(width, height)
	return &Draw{
		grid:     grid,
		Width:    width,
		Height:   height,
		fontSize: 14,
		fgColor:  color.RGBA{255, 255, 255, 255},
		bgColor:  color.RGBA{0, 0, 0, 255},
	}
}

// Render returns the current grid for rendering by the carousel
func (d *Draw) Render() *fbdraw.CharGrid {
	return d.grid
}

// Clear fills the entire display with black.
func (d *Draw) Clear() {
	d.grid.Clear(color.RGBA{0, 0, 0, 255})
}

// ClearColor fills the entire display with the specified color.
func (d *Draw) ClearColor(c color.Color) {
	// Fill all cells with spaces and the background color
	for y := 0; y < d.Height; y++ {
		for x := 0; x < d.Width; x++ {
			weight := font.WeightRegular
			if d.bold {
				weight = font.WeightBold
			}
			d.grid.SetCell(x, y, ' ', d.fgColor, c, weight, d.italic)
		}
	}
}

// Font sets the current font for text operations.
func (d *Draw) Font(f Font) *Draw {
	d.font = f
	return d
}

// Size sets the current font size in points.
func (d *Draw) Size(points int) *Draw {
	d.fontSize = points
	d.grid.FontSize = float64(points)
	return d
}

// Bold enables bold text rendering.
func (d *Draw) Bold() *Draw {
	d.bold = true
	return d
}

// Plain disables bold and italic text rendering.
func (d *Draw) Plain() *Draw {
	d.bold = false
	d.italic = false
	return d
}

// Italic enables italic text rendering.
func (d *Draw) Italic() *Draw {
	d.italic = true
	return d
}

// Color sets the foreground color for text operations.
func (d *Draw) Color(c color.Color) *Draw {
	d.fgColor = c
	return d
}

// Background sets the background color for text operations.
func (d *Draw) Background(c color.Color) *Draw {
	d.bgColor = c
	return d
}

// Text draws text at the specified character coordinates.
func (d *Draw) Text(x, y int, format string, args ...interface{}) {
	text := fmt.Sprintf(format, args...)
	writer := fbdraw.NewGridWriter(d.grid)
	writer.MoveAbs(x, y)
	writer.SetColor(d.fgColor)
	writer.SetBackground(d.bgColor)
	if d.bold {
		writer.SetWeight(font.WeightBold)
	} else {
		writer.SetWeight(font.WeightRegular)
	}
	writer.SetItalic(d.italic)
	writer.Write(text)
}

// TextCenter draws centered text at the specified y coordinate.
func (d *Draw) TextCenter(x, y int, format string, args ...interface{}) {
	text := fmt.Sprintf(format, args...)
	// Calculate starting position for centered text
	startX := x + (d.Width-len(text))/2
	d.Text(startX, y, text)
}

// Grid creates a text grid region for simplified text layout.
// The grid uses the current font settings from the Draw context.
func (d *Draw) Grid(x, y, cols, rows int) *Grid {
	return &Grid{
		draw: d,
		x:    x,
		y:    y,
		cols: cols,
		rows: rows,
	}
}

// Grid represents a rectangular text grid for structured text layout.
// All positions are in character cells, not pixels.
type Grid struct {
	draw       *Draw
	x, y       int
	cols, rows int

	// Grid-specific state that can override draw state
	fgColor     color.Color
	bgColor     color.Color
	borderColor color.Color
	hasBorder   bool
}

// Write places text at the specified row and column within the grid.
// Text that exceeds the grid bounds is clipped.
func (g *Grid) Write(col, row int, format string, args ...interface{}) {
	if row < 0 || row >= g.rows || col < 0 || col >= g.cols {
		return
	}
	text := fmt.Sprintf(format, args...)
	
	// Calculate absolute position
	absX := g.x + col
	absY := g.y + row
	
	// Write text with clipping
	writer := fbdraw.NewGridWriter(g.draw.grid)
	writer.MoveAbs(absX, absY)
	if g.fgColor != nil {
		writer.SetColor(g.fgColor)
	} else {
		writer.SetColor(g.draw.fgColor)
	}
	if g.bgColor != nil {
		writer.SetBackground(g.bgColor)
	} else {
		writer.SetBackground(g.draw.bgColor)
	}
	
	// Clip text to grid bounds
	maxLen := g.cols - col
	if len(text) > maxLen {
		text = text[:maxLen]
	}
	writer.Write(text)
}

// WriteCenter centers text within the specified row.
func (g *Grid) WriteCenter(row int, format string, args ...interface{}) {
	if row < 0 || row >= g.rows {
		return
	}
	text := fmt.Sprintf(format, args...)
	col := (g.cols - len(text)) / 2
	if col < 0 {
		col = 0
	}
	g.Write(col, row, text)
}

// Color sets the foreground color for subsequent Write operations.
func (g *Grid) Color(c color.Color) *Grid {
	g.fgColor = c
	return g
}

// Background sets the background color for the entire grid.
func (g *Grid) Background(c color.Color) *Grid {
	g.bgColor = c
	// Fill the grid area with the background color
	for row := 0; row < g.rows; row++ {
		for col := 0; col < g.cols; col++ {
			weight := font.WeightRegular
			if g.draw.bold {
				weight = font.WeightBold
			}
			g.draw.grid.SetCell(g.x+col, g.y+row, ' ', g.draw.fgColor, c, weight, g.draw.italic)
		}
	}
	return g
}

// Border draws a border around the grid in the specified color.
func (g *Grid) Border(c color.Color) *Grid {
	g.borderColor = c
	g.hasBorder = true
	// Draw border using box drawing characters
	writer := fbdraw.NewGridWriter(g.draw.grid)
	writer.SetColor(c)
	
	// Top border
	writer.MoveAbs(g.x-1, g.y-1)
	writer.Write("┌")
	for i := 0; i < g.cols; i++ {
		writer.Write("─")
	}
	writer.Write("┐")
	
	// Side borders
	for row := 0; row < g.rows; row++ {
		writer.MoveAbs(g.x-1, g.y+row)
		writer.Write("│")
		writer.MoveAbs(g.x+g.cols, g.y+row)
		writer.Write("│")
	}
	
	// Bottom border
	writer.MoveAbs(g.x-1, g.y+g.rows)
	writer.Write("└")
	for i := 0; i < g.cols; i++ {
		writer.Write("─")
	}
	writer.Write("┘")
	
	return g
}

// RowBackground sets the background color for a specific row.
func (g *Grid) RowBackground(row int, c color.Color) {
	if row < 0 || row >= g.rows {
		return
	}
	for col := 0; col < g.cols; col++ {
		g.draw.grid.SetCell(g.x+col, g.y+row, ' ', g.draw.fgColor, c, font.WeightRegular, false)
	}
}

// RowColor sets the text color for an entire row.
func (g *Grid) RowColor(row int, c color.Color) {
	if row < 0 || row >= g.rows {
		return
	}
	// This would need to track row colors for subsequent writes
	// For now, we'll just update existing text in the row
	for col := 0; col < g.cols; col++ {
		// Get current cell to preserve other attributes
		if g.y+row < g.draw.grid.Height && g.x+col < g.draw.grid.Width {
			cell := &g.draw.grid.Cells[g.y+row][g.x+col]
			cell.Foreground = c
		}
	}
}

// Bar draws a horizontal progress bar within the grid cell.
func (g *Grid) Bar(col, row, width int, percent float64, c color.Color) {
	if row < 0 || row >= g.rows || col < 0 || col >= g.cols {
		return
	}
	
	// Ensure width doesn't exceed grid bounds
	maxWidth := g.cols - col
	if width > maxWidth {
		width = maxWidth
	}
	
	writer := fbdraw.NewGridWriter(g.draw.grid)
	writer.MoveAbs(g.x+col, g.y+row)
	writer.SetColor(c)
	writer.DrawMeter(percent, width)
}

// Bold enables bold text for subsequent Write operations.
func (g *Grid) Bold() *Grid {
	// TODO: Track bold state for grid
	return g
}

// Plain disables text styling for subsequent Write operations.
func (g *Grid) Plain() *Grid {
	// TODO: Clear text styling for grid
	return g
}

// Meter creates a text-based progress meter using Unicode block characters.
// The width parameter specifies the number of characters.
// Returns a string like "█████████░░░░░░" for 60% with width 15.
func Meter(percent float64, width int) string {
	if percent < 0 {
		percent = 0
	}
	if percent > 100 {
		percent = 100
	}
	
	filled := int(percent / 100.0 * float64(width))
	result := ""
	
	for i := 0; i < width; i++ {
		if i < filled {
			result += "█"
		} else {
			result += "░"
		}
	}
	
	return result
}

// Bytes formats a byte count as a human-readable string.
// For example: 1234567890 becomes "1.2 GB".
func Bytes(bytes uint64) string {
	const unit = 1024
	if bytes < unit {
		return fmt.Sprintf("%d B", bytes)
	}
	div, exp := uint64(unit), 0
	for n := bytes / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}

// Heat returns a color between blue and red based on the value.
// 0.0 returns blue, 1.0 returns red, with gradients in between.
func Heat(value float64) color.Color {
	if value < 0 {
		value = 0
	}
	if value > 1 {
		value = 1
	}
	
	// Simple linear interpolation between blue and red
	r := uint8(255 * value)
	g := uint8(0)
	b := uint8(255 * (1 - value))
	
	return color.RGBA{r, g, b, 255}
}

// RGB creates a color from red, green, and blue values (0-255).
func RGB(r, g, b uint8) color.Color {
	return color.RGBA{r, g, b, 255}
}