package engine

import "math"

// Shape rendering constants for visual proportions
const (
	// Center shape proportions - these ratios determine the visual appearance
	centerShapeAsymmetricCornerRatio = 0.42 // Shape 0: corner cut proportion
	centerShapeTriangleWidthRatio    = 0.5  // Shape 1: triangle width relative to cell
	centerShapeTriangleHeightRatio   = 0.8  // Shape 1: triangle height relative to cell

	centerShapeInnerMarginRatio   = 0.1  // Shape 3,5,9,10: inner margin ratio
	centerShapeOuterMarginRatio   = 0.25 // Shape 3: outer margin ratio for large cells
	centerShapeOuterMarginRatio9  = 0.35 // Shape 9: outer margin ratio for large cells
	centerShapeOuterMarginRatio10 = 0.12 // Shape 10: inner ratio for circular cutout

	centerShapeCircleMarginRatio = 0.15 // Shape 4: circle margin ratio
	centerShapeCircleWidthRatio  = 0.5  // Shape 4: circle width ratio

	// Shape 6 complex polygon proportions
	centerShapeComplexHeight1Ratio = 0.7 // First height point
	centerShapeComplexPoint1XRatio = 0.4 // First point X ratio
	centerShapeComplexPoint1YRatio = 0.4 // First point Y ratio
	centerShapeComplexPoint2XRatio = 0.7 // Second point X ratio

	// Shape 9 rectangular cutout proportions
	centerShapeRect9InnerRatio = 0.14 // Shape 9: inner rectangle ratio

	// Shape 12 rhombus cutout proportion
	centerShapeRhombusCutoutRatio = 0.25 // Shape 12: rhombus cutout margin

	// Shape 13 large circle proportions (only for center position)
	centerShapeLargeCircleMarginRatio = 0.4 // Shape 13: circle margin ratio
	centerShapeLargeCircleWidthRatio  = 1.2 // Shape 13: circle width ratio

	// Outer shape proportions
	outerShapeCircleMarginRatio = 1.0 / 6.0 // Shape 3: circle margin (1/6 of cell)

	// Size thresholds for conditional rendering
	smallCellThreshold4 = 4 // Threshold for shape 3,9 outer margin calculation
	smallCellThreshold6 = 6 // Threshold for shape 3 outer margin calculation
	smallCellThreshold8 = 8 // Threshold for shape 3,9 inner margin floor calculation

	// Multipliers for margin calculations
	innerOuterMultiplier5  = 4 // Shape 5: inner to outer multiplier
	innerOuterMultiplier10 = 3 // Shape 10: inner to outer multiplier
)

// Point represents a 2D point
type Point struct {
	X, Y float64
}

// Renderer interface defines the methods that a renderer must implement
type Renderer interface {
	AddPolygon(points []Point)
	AddCircle(topLeft Point, size float64, invert bool)
}

// Graphics provides helper functions for rendering common basic shapes
type Graphics struct {
	renderer         Renderer
	currentTransform Transform
}

// NewGraphics creates a new Graphics instance with the given renderer
func NewGraphics(renderer Renderer) *Graphics {
	return &Graphics{
		renderer:         renderer,
		currentTransform: NoTransform,
	}
}

// NewGraphicsWithTransform creates a new Graphics instance with the given renderer and transform
func NewGraphicsWithTransform(renderer Renderer, transform Transform) *Graphics {
	return &Graphics{
		renderer:         renderer,
		currentTransform: transform,
	}
}

// AddPolygon adds a polygon to the underlying renderer
func (g *Graphics) AddPolygon(points []Point, invert bool) {
	// Transform all points
	transformedPoints := make([]Point, len(points))
	if invert {
		// Reverse the order and transform
		for i, p := range points {
			transformedPoints[len(points)-1-i] = g.currentTransform.TransformIconPoint(p.X, p.Y, 0, 0)
		}
	} else {
		// Transform in order
		for i, p := range points {
			transformedPoints[i] = g.currentTransform.TransformIconPoint(p.X, p.Y, 0, 0)
		}
	}
	g.renderer.AddPolygon(transformedPoints)
}

// AddCircle adds a circle to the underlying renderer
func (g *Graphics) AddCircle(x, y, size float64, invert bool) {
	// Transform the circle position
	transformedPoint := g.currentTransform.TransformIconPoint(x, y, size, size)
	g.renderer.AddCircle(transformedPoint, size, invert)
}

// AddRectangle adds a rectangle to the underlying renderer
func (g *Graphics) AddRectangle(x, y, w, h float64, invert bool) {
	points := []Point{
		{X: x, Y: y},
		{X: x + w, Y: y},
		{X: x + w, Y: y + h},
		{X: x, Y: y + h},
	}
	g.AddPolygon(points, invert)
}

// AddTriangle adds a right triangle to the underlying renderer
// r is the rotation (0-3), where 0 = right corner in lower left
func (g *Graphics) AddTriangle(x, y, w, h float64, r int, invert bool) {
	points := []Point{
		{X: x + w, Y: y},
		{X: x + w, Y: y + h},
		{X: x, Y: y + h},
		{X: x, Y: y},
	}

	// Remove one corner based on rotation
	removeIndex := (r % 4) * 1
	if removeIndex < len(points) {
		points = append(points[:removeIndex], points[removeIndex+1:]...)
	}

	g.AddPolygon(points, invert)
}

// AddRhombus adds a rhombus (diamond) to the underlying renderer
func (g *Graphics) AddRhombus(x, y, w, h float64, invert bool) {
	points := []Point{
		{X: x + w/2, Y: y},
		{X: x + w, Y: y + h/2},
		{X: x + w/2, Y: y + h},
		{X: x, Y: y + h/2},
	}
	g.AddPolygon(points, invert)
}

// RenderCenterShape renders one of the 14 distinct center shape patterns
func RenderCenterShape(g *Graphics, shapeIndex int, cell, positionIndex float64) {
	index := shapeIndex % 14

	switch index {
	case 0:
		// Shape 0: Asymmetric polygon
		k := cell * centerShapeAsymmetricCornerRatio
		points := []Point{
			{X: 0, Y: 0},
			{X: cell, Y: 0},
			{X: cell, Y: cell - k*2},
			{X: cell - k, Y: cell},
			{X: 0, Y: cell},
		}
		g.AddPolygon(points, false)

	case 1:
		// Shape 1: Triangle
		w := math.Floor(cell * centerShapeTriangleWidthRatio)
		h := math.Floor(cell * centerShapeTriangleHeightRatio)
		g.AddTriangle(cell-w, 0, w, h, 2, false)

	case 2:
		// Shape 2: Rectangle
		w := math.Floor(cell / 3)
		g.AddRectangle(w, w, cell-w, cell-w, false)

	case 3:
		// Shape 3: Nested rectangles
		inner := cell * centerShapeInnerMarginRatio
		var outer float64
		if cell < smallCellThreshold6 {
			outer = 1
		} else if cell < smallCellThreshold8 {
			outer = 2
		} else {
			outer = math.Floor(cell * centerShapeOuterMarginRatio)
		}

		if inner > 1 {
			inner = math.Floor(inner)
		} else if inner > 0.5 {
			inner = 1
		}

		g.AddRectangle(outer, outer, cell-inner-outer, cell-inner-outer, false)

	case 4:
		// Shape 4: Circle
		m := math.Floor(cell * centerShapeCircleMarginRatio)
		w := math.Floor(cell * centerShapeCircleWidthRatio)
		g.AddCircle(cell-w-m, cell-w-m, w, false)

	case 5:
		// Shape 5: Rectangle with triangular cutout
		inner := cell * centerShapeInnerMarginRatio
		outer := inner * innerOuterMultiplier5

		if outer > 3 {
			outer = math.Floor(outer)
		}

		g.AddRectangle(0, 0, cell, cell, false)
		points := []Point{
			{X: outer, Y: outer},
			{X: cell - inner, Y: outer},
			{X: outer + (cell-outer-inner)/2, Y: cell - inner},
		}
		g.AddPolygon(points, true)

	case 6:
		// Shape 6: Complex polygon
		points := []Point{
			{X: 0, Y: 0},
			{X: cell, Y: 0},
			{X: cell, Y: cell * centerShapeComplexHeight1Ratio},
			{X: cell * centerShapeComplexPoint1XRatio, Y: cell * centerShapeComplexPoint1YRatio},
			{X: cell * centerShapeComplexPoint2XRatio, Y: cell},
			{X: 0, Y: cell},
		}
		g.AddPolygon(points, false)

	case 7:
		// Shape 7: Small triangle
		g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 3, false)

	case 8:
		// Shape 8: Composite shape
		g.AddRectangle(0, 0, cell, cell/2, false)
		g.AddRectangle(0, cell/2, cell/2, cell/2, false)
		g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 1, false)

	case 9:
		// Shape 9: Rectangle with rectangular cutout
		inner := cell * centerShapeRect9InnerRatio
		var outer float64
		if cell < smallCellThreshold4 {
			outer = 1
		} else if cell < smallCellThreshold6 {
			outer = 2
		} else {
			outer = math.Floor(cell * centerShapeOuterMarginRatio9)
		}

		if cell >= smallCellThreshold8 {
			inner = math.Floor(inner)
		}

		g.AddRectangle(0, 0, cell, cell, false)
		g.AddRectangle(outer, outer, cell-outer-inner, cell-outer-inner, true)

	case 10:
		// Shape 10: Rectangle with circular cutout
		inner := cell * centerShapeOuterMarginRatio10
		outer := inner * innerOuterMultiplier10

		g.AddRectangle(0, 0, cell, cell, false)
		g.AddCircle(outer, outer, cell-inner-outer, true)

	case 11:
		// Shape 11: Small triangle (same as 7)
		g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 3, false)

	case 12:
		// Shape 12: Rectangle with rhombus cutout
		m := cell * centerShapeRhombusCutoutRatio
		g.AddRectangle(0, 0, cell, cell, false)
		g.AddRhombus(m, m, cell-m, cell-m, true)

	case 13:
		// Shape 13: Large circle (only for position 0)
		if positionIndex == 0 {
			m := cell * centerShapeLargeCircleMarginRatio
			w := cell * centerShapeLargeCircleWidthRatio
			g.AddCircle(m, m, w, false)
		}
	}
}

// RenderOuterShape renders one of the 4 distinct outer shape patterns
func RenderOuterShape(g *Graphics, shapeIndex int, cell float64) {
	index := shapeIndex % 4

	switch index {
	case 0:
		// Shape 0: Triangle
		g.AddTriangle(0, 0, cell, cell, 0, false)

	case 1:
		// Shape 1: Triangle (different orientation)
		g.AddTriangle(0, cell/2, cell, cell/2, 0, false)

	case 2:
		// Shape 2: Rhombus
		g.AddRhombus(0, 0, cell, cell, false)

	case 3:
		// Shape 3: Circle
		m := cell * outerShapeCircleMarginRatio
		g.AddCircle(m, m, cell-2*m, false)
	}
}
