package engine

import (
	"testing"
)

// MockRenderer implements the Renderer interface for testing
type MockRenderer struct {
	Polygons [][]Point
	Circles  []MockCircle
}

type MockCircle struct {
	TopLeft Point
	Size    float64
	Invert  bool
}

func (m *MockRenderer) AddPolygon(points []Point) {
	m.Polygons = append(m.Polygons, points)
}

func (m *MockRenderer) AddCircle(topLeft Point, size float64, invert bool) {
	m.Circles = append(m.Circles, MockCircle{
		TopLeft: topLeft,
		Size:    size,
		Invert:  invert,
	})
}

func (m *MockRenderer) Reset() {
	m.Polygons = nil
	m.Circles = nil
}

func TestGraphicsAddRectangle(t *testing.T) {
	mock := &MockRenderer{}
	g := NewGraphics(mock)

	g.AddRectangle(10, 20, 30, 40, false)

	if len(mock.Polygons) != 1 {
		t.Errorf("Expected 1 polygon, got %d", len(mock.Polygons))
		return
	}

	expected := []Point{
		{X: 10, Y: 20},
		{X: 40, Y: 20},
		{X: 40, Y: 60},
		{X: 10, Y: 60},
	}

	polygon := mock.Polygons[0]
	if len(polygon) != len(expected) {
		t.Errorf("Expected %d points, got %d", len(expected), len(polygon))
		return
	}

	for i, point := range expected {
		if polygon[i].X != point.X || polygon[i].Y != point.Y {
			t.Errorf("Point %d: expected (%f, %f), got (%f, %f)",
				i, point.X, point.Y, polygon[i].X, polygon[i].Y)
		}
	}
}

func TestGraphicsAddCircle(t *testing.T) {
	mock := &MockRenderer{}
	g := NewGraphics(mock)

	g.AddCircle(10, 20, 30, false)

	if len(mock.Circles) != 1 {
		t.Errorf("Expected 1 circle, got %d", len(mock.Circles))
		return
	}

	circle := mock.Circles[0]
	expectedTopLeft := Point{X: 10, Y: 20}
	expectedSize := float64(30)

	if circle.TopLeft.X != expectedTopLeft.X || circle.TopLeft.Y != expectedTopLeft.Y {
		t.Errorf("Expected top-left (%f, %f), got (%f, %f)",
			expectedTopLeft.X, expectedTopLeft.Y, circle.TopLeft.X, circle.TopLeft.Y)
	}

	if circle.Size != expectedSize {
		t.Errorf("Expected size %f, got %f", expectedSize, circle.Size)
	}

	if circle.Invert != false {
		t.Errorf("Expected invert false, got %t", circle.Invert)
	}
}

func TestGraphicsAddRhombus(t *testing.T) {
	mock := &MockRenderer{}
	g := NewGraphics(mock)

	g.AddRhombus(0, 0, 20, 30, false)

	if len(mock.Polygons) != 1 {
		t.Errorf("Expected 1 polygon, got %d", len(mock.Polygons))
		return
	}

	expected := []Point{
		{X: 10, Y: 0},  // top
		{X: 20, Y: 15}, // right
		{X: 10, Y: 30}, // bottom
		{X: 0, Y: 15},  // left
	}

	polygon := mock.Polygons[0]
	if len(polygon) != len(expected) {
		t.Errorf("Expected %d points, got %d", len(expected), len(polygon))
		return
	}

	for i, point := range expected {
		if polygon[i].X != point.X || polygon[i].Y != point.Y {
			t.Errorf("Point %d: expected (%f, %f), got (%f, %f)",
				i, point.X, point.Y, polygon[i].X, polygon[i].Y)
		}
	}
}

func TestRenderCenterShape(t *testing.T) {
	mock := &MockRenderer{}
	g := NewGraphics(mock)
	cell := float64(60)

	// Test each center shape
	for i := 0; i < 14; i++ {
		mock.Reset()
		RenderCenterShape(g, i, cell, 0)

		// Verify that some drawing commands were issued
		if len(mock.Polygons) == 0 && len(mock.Circles) == 0 {
			// Shape 13 at position != 0 doesn't draw anything, which is expected
			if i == 13 {
				continue
			}
			t.Errorf("Shape %d: expected some drawing commands, got none", i)
		}
	}
}

func TestRenderCenterShapeSpecific(t *testing.T) {
	mock := &MockRenderer{}
	g := NewGraphics(mock)
	cell := float64(60)

	// Test shape 2 (rectangle)
	mock.Reset()
	RenderCenterShape(g, 2, cell, 0)

	if len(mock.Polygons) != 1 {
		t.Errorf("Shape 2: expected 1 polygon, got %d", len(mock.Polygons))
	}

	// Test shape 4 (circle)
	mock.Reset()
	RenderCenterShape(g, 4, cell, 0)

	if len(mock.Circles) != 1 {
		t.Errorf("Shape 4: expected 1 circle, got %d", len(mock.Circles))
	}

	// Test shape 13 at position 0 (should draw)
	mock.Reset()
	RenderCenterShape(g, 13, cell, 0)

	if len(mock.Circles) != 1 {
		t.Errorf("Shape 13 at position 0: expected 1 circle, got %d", len(mock.Circles))
	}

	// Test shape 13 at position 1 (should not draw)
	mock.Reset()
	RenderCenterShape(g, 13, cell, 1)

	if len(mock.Circles) != 0 {
		t.Errorf("Shape 13 at position 1: expected 0 circles, got %d", len(mock.Circles))
	}
}

func TestRenderOuterShape(t *testing.T) {
	mock := &MockRenderer{}
	g := NewGraphics(mock)
	cell := float64(60)

	// Test each outer shape
	for i := 0; i < 4; i++ {
		mock.Reset()
		RenderOuterShape(g, i, cell)

		// Verify that some drawing commands were issued
		if len(mock.Polygons) == 0 && len(mock.Circles) == 0 {
			t.Errorf("Outer shape %d: expected some drawing commands, got none", i)
		}
	}
}

func TestRenderOuterShapeSpecific(t *testing.T) {
	mock := &MockRenderer{}
	g := NewGraphics(mock)
	cell := float64(60)

	// Test outer shape 2 (rhombus)
	mock.Reset()
	RenderOuterShape(g, 2, cell)

	if len(mock.Polygons) != 1 {
		t.Errorf("Outer shape 2: expected 1 polygon, got %d", len(mock.Polygons))
	}

	// Test outer shape 3 (circle)
	mock.Reset()
	RenderOuterShape(g, 3, cell)

	if len(mock.Circles) != 1 {
		t.Errorf("Outer shape 3: expected 1 circle, got %d", len(mock.Circles))
	}
}

func TestShapeIndexModulo(t *testing.T) {
	mock := &MockRenderer{}
	g := NewGraphics(mock)
	cell := float64(60)

	// Test that shape indices wrap around correctly
	mock.Reset()
	RenderCenterShape(g, 0, cell, 0)
	polygonsShape0 := len(mock.Polygons)
	circlesShape0 := len(mock.Circles)

	mock.Reset()
	RenderCenterShape(g, 14, cell, 0) // Should be same as shape 0

	if len(mock.Polygons) != polygonsShape0 || len(mock.Circles) != circlesShape0 {
		t.Errorf("Shape 14 should be equivalent to shape 0")
	}

	// Test outer shapes
	mock.Reset()
	RenderOuterShape(g, 0, cell)
	polygonsOuter0 := len(mock.Polygons)
	circlesOuter0 := len(mock.Circles)

	mock.Reset()
	RenderOuterShape(g, 4, cell) // Should be same as outer shape 0

	if len(mock.Polygons) != polygonsOuter0 || len(mock.Circles) != circlesOuter0 {
		t.Errorf("Outer shape 4 should be equivalent to outer shape 0")
	}
}
