package ui

import "strings"

// brailleGrid is a 4×2 dot-per-cell rasteriser shared by visualizers that draw
// to a fine subgrid (sand, speaker, lightning, crack, quake, geyser, strings).
// Each cell stores a tier (1..3 = low/mid/high colour, 0 = empty) and the
// renderer composes one Braille glyph per character cell.
type brailleGrid struct {
	cells   []int8
	dotRows int
	dotCols int
}

func (g *brailleGrid) ensure(rows, cols int) {
	if rows == g.dotRows && cols == g.dotCols && len(g.cells) == rows*cols {
		for i := range g.cells {
			g.cells[i] = 0
		}
		return
	}
	g.cells = make([]int8, rows*cols)
	g.dotRows = rows
	g.dotCols = cols
}

func (g *brailleGrid) clear() {
	for i := range g.cells {
		g.cells[i] = 0
	}
}

func (g *brailleGrid) set(x, y int, tier int8) {
	if x < 0 || x >= g.dotCols || y < 0 || y >= g.dotRows {
		return
	}
	if tier > g.cells[y*g.dotCols+x] {
		g.cells[y*g.dotCols+x] = tier
	}
}

// render flattens the dot grid to len(rows) lines, packing 4×2 dot blocks into
// Braille glyphs and emitting tier-coloured runs.
func (g *brailleGrid) render(rows int) string {
	if g.dotRows < rows*4 || g.dotCols < PanelWidth*2 {
		return strings.Repeat("\n", max(0, rows-1))
	}
	lines := make([]string, rows)
	for row := 0; row < rows; row++ {
		var sb, run strings.Builder
		tag := -1
		for col := 0; col < PanelWidth; col++ {
			var braille rune = '⠀'
			cellTag := -1
			for dr := 0; dr < 4; dr++ {
				for dc := 0; dc < 2; dc++ {
					y := row*4 + dr
					x := col*2 + dc
					t := g.cells[y*g.dotCols+x]
					if t == 0 {
						continue
					}
					braille |= brailleBit[dr][dc]
					if int(t)-1 > cellTag {
						cellTag = int(t) - 1
					}
				}
			}
			if cellTag < 0 {
				cellTag = 0
			}
			if cellTag != tag {
				flushStyleRun(&sb, &run, tag)
				tag = cellTag
			}
			run.WriteRune(braille)
		}
		flushStyleRun(&sb, &run, tag)
		lines[row] = sb.String()
	}
	return strings.Join(lines, "\n")
}

// drawLine plots a Bresenham line into the grid at the given tier.
func (g *brailleGrid) drawLine(x0, y0, x1, y1 int, tier int8) {
	dx := x1 - x0
	if dx < 0 {
		dx = -dx
	}
	dy := -(y1 - y0)
	if dy > 0 {
		dy = -dy
	}
	sx := 1
	if x0 >= x1 {
		sx = -1
	}
	sy := 1
	if y0 >= y1 {
		sy = -1
	}
	err := dx + dy
	for {
		g.set(x0, y0, tier)
		if x0 == x1 && y0 == y1 {
			return
		}
		e2 := 2 * err
		if e2 >= dy {
			err += dy
			x0 += sx
		}
		if e2 <= dx {
			err += dx
			y0 += sy
		}
	}
}

// rng64 advances a 64-bit LCG and returns a [0,1) double.
func rng64(state *uint64) float64 {
	*state = *state*6364136223846793005 + 1442695040888963407
	return float64((*state>>33)%1000) / 1000.0
}
