package hud

import (
	"fmt"
	"image/color"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/text/v2"
	"golang.org/x/image/font/basicfont"

	"github.com/atridad/LilGuy/internal/status"
)

var (
	basicFace       = text.NewGoXFace(basicfont.Face7x13)
	basicFaceAscent = basicFace.Metrics().HAscent
	rectPixel       = newRectPixel()
)

// Drawing helpers

func newRectPixel() *ebiten.Image {
	img := ebiten.NewImage(1, 1)
	img.Fill(color.White)
	return img
}

func drawHUDText(screen *ebiten.Image, txt string, clr color.Color, x, y int) {
	if clr == nil {
		clr = color.White
	}
	op := &text.DrawOptions{}
	op.GeoM.Translate(float64(x), float64(y)-basicFaceAscent)
	op.ColorScale.ScaleWithColor(clr)
	text.Draw(screen, txt, basicFace, op)
}

// HUD elements

type Element interface {
	Draw(screen *ebiten.Image, x, y int) (width, height int)
}

type Label struct {
	Text  string
	Color color.Color
}

func (l Label) Draw(screen *ebiten.Image, x, y int) (int, int) {
	if l.Color == nil {
		l.Color = color.White
	}
	drawHUDText(screen, l.Text, l.Color, x, y)
	width := len(l.Text) * 7 // approximate width for basicfont
	return width, 13
}

type PercentageDisplay struct {
	Meter status.Meter
	Color color.Color
}

func (p PercentageDisplay) Draw(screen *ebiten.Image, x, y int) (int, int) {
	if p.Color == nil {
		p.Color = color.White
	}
	txt := fmt.Sprintf("%3.0f%%", p.Meter.Level)
	drawHUDText(screen, txt, p.Color, x, y)
	return len(txt) * 7, 13
}

type MeterLabel struct {
	Meter status.Meter
	Color color.Color
}

func (m MeterLabel) Draw(screen *ebiten.Image, x, y int) (int, int) {
	if m.Color == nil {
		m.Color = color.White
	}
	var txt string
	if m.Meter.Base < 0 {
		txt = m.Meter.Label
	} else {
		txt = fmt.Sprintf("%s: %3.0f%%", m.Meter.Label, m.Meter.Level)
	}
	drawHUDText(screen, txt, m.Meter.Color, x, y)
	return len(txt) * 7, 13
}

type Bar struct {
	Meter       status.Meter
	MaxWidth    int
	Height      int
	BorderColor color.Color
	ShowBorder  bool
}

func (b Bar) Draw(screen *ebiten.Image, x, y int) (int, int) {
	maxWidth := b.MaxWidth
	if maxWidth <= 0 {
		maxWidth = 180
	}
	height := b.Height
	if height <= 0 {
		height = 8
	}

	fillWidth := int((b.Meter.Level / 100.0) * float64(maxWidth))
	if fillWidth < 0 {
		fillWidth = 0
	}
	if fillWidth > maxWidth {
		fillWidth = maxWidth
	}

	// Draw dark background
	drawRect(screen, x, y, maxWidth, height, color.RGBA{R: 30, G: 30, B: 30, A: 255})

	// Draw filled portion
	if fillWidth > 0 {
		drawRect(screen, x, y, fillWidth, height, b.Meter.Color)
	}

	// Draw border
	borderColor := b.BorderColor
	if borderColor == nil {
		borderColor = color.RGBA{R: 180, G: 180, B: 180, A: 255}
	}
	drawRect(screen, x, y, maxWidth, 1, borderColor)
	drawRect(screen, x, y+height-1, maxWidth, 1, borderColor)
	drawRect(screen, x, y, 1, height, borderColor)
	drawRect(screen, x+maxWidth-1, y, 1, height, borderColor)

	return maxWidth, height
}

// Rectangle drawing

func drawRect(screen *ebiten.Image, x, y, width, height int, clr color.Color) {
	if width <= 0 || height <= 0 {
		return
	}
	if clr == nil {
		clr = color.White
	}

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Scale(float64(width), float64(height))
	op.GeoM.Translate(float64(x), float64(y))
	op.ColorScale.ScaleWithColor(clr)

	screen.DrawImage(rectPixel, op)
}

// Layout elements

type Spacer struct {
	Width  int
	Height int
}

func (s Spacer) Draw(screen *ebiten.Image, x, y int) (int, int) {
	return s.Width, s.Height
}

type Row struct {
	Elements []Element
	Spacing  int
}

func (r Row) Draw(screen *ebiten.Image, x, y int) (int, int) {
	totalWidth := 0
	maxHeight := 0
	currentX := x

	for i, elem := range r.Elements {
		w, h := elem.Draw(screen, currentX, y)
		totalWidth += w
		if h > maxHeight {
			maxHeight = h
		}
		currentX += w
		if i < len(r.Elements)-1 {
			currentX += r.Spacing
			totalWidth += r.Spacing
		}
	}

	return totalWidth, maxHeight
}

type Column struct {
	Elements []Element
	Spacing  int
}

func (c Column) Draw(screen *ebiten.Image, x, y int) (int, int) {
	maxWidth := 0
	totalHeight := 0
	currentY := y

	for i, elem := range c.Elements {
		w, h := elem.Draw(screen, x, currentY)
		if w > maxWidth {
			maxWidth = w
		}
		totalHeight += h
		currentY += h
		if i < len(c.Elements)-1 {
			currentY += c.Spacing
			totalHeight += c.Spacing
		}
	}

	return maxWidth, totalHeight
}
