package sprite

import (
	"image"
	"image/color"
	"strconv"
	"testing"
)

func TestNamedSpriteBoard(t *testing.T) {
	// 创建测试板
	board := NewNamedSpriteBoard()

	// 测试添加精灵
	sprite1 := &Sprite{
		Name:     "sprite1",
		Position: image.Point{X: 10, Y: 10},
		Image:    image.NewRGBA(image.Rect(0, 0, 10, 10)),
		Index:    5,
	}

	sprite2 := &Sprite{
		Name:     "sprite2",
		Position: image.Point{X: 20, Y: 20},
		Image:    image.NewRGBA(image.Rect(0, 0, 10, 10)),
		Index:    3,
	}

	sprite3 := &Sprite{
		Name:     "sprite3",
		Position: image.Point{X: 30, Y: 30},
		Image:    image.NewRGBA(image.Rect(0, 0, 10, 10)),
		Index:    8,
	}

	// 添加精灵
	board.AddSprite(sprite1)
	board.AddSprite(sprite2)
	board.AddSprite(sprite3)

	// 验证总数量
	if board.count != 3 {
		t.Errorf("期望精灵总数为3，实际为%d", board.count)
	}

	// 验证索引组数量
	if len(board.indexMap) != 3 {
		t.Errorf("期望索引组数量为3，实际为%d", len(board.indexMap))
	}

	// 验证索引组链表顺序
	expectedIndices := []int{3, 5, 8}
	currentGroup := board.indexHead
	for i, expectedIndex := range expectedIndices {
		if currentGroup == nil {
			t.Errorf("索引组链表在位置%d处意外结束", i)
			break
		}
		if currentGroup.Index != expectedIndex {
			t.Errorf("索引组链表顺序错误，位置%d期望为%d，实际为%d", i, expectedIndex, currentGroup.Index)
		}
		currentGroup = currentGroup.Next
	}

	// 验证兼容层
	if len(board.Sprites) != 3 {
		t.Errorf("兼容层数组长度错误，期望为3，实际为%d", len(board.Sprites))
	}
	for i, expectedIndex := range expectedIndices {
		if board.Sprites[i].Index != expectedIndex {
			t.Errorf("兼容层数组索引顺序错误，位置%d期望为%d，实际为%d", i, expectedIndex, board.Sprites[i].Index)
		}
	}

	// 测试按名称查找
	foundSprite := board.GetSpriteByName("sprite1")
	if foundSprite == nil || foundSprite.Index != 5 {
		t.Errorf("未能按名称找到精灵sprite1")
	}

	// 测试按索引查找
	sprites := board.GetSpritesByIndex(5)
	if len(sprites) != 1 || sprites[0].Name != "sprite1" {
		t.Errorf("按索引查找精灵错误")
	}

	// 添加相同索引的精灵
	sprite4 := &Sprite{
		Name:     "sprite4",
		Position: image.Point{X: 40, Y: 40},
		Image:    image.NewRGBA(image.Rect(0, 0, 10, 10)),
		Index:    5, // 与sprite1相同的索引
	}
	board.AddSprite(sprite4)

	// 验证总数量增加
	if board.count != 4 {
		t.Errorf("添加相同索引精灵后，期望精灵总数为4，实际为%d", board.count)
	}

	// 验证索引组数量不变
	if len(board.indexMap) != 3 {
		t.Errorf("添加相同索引精灵后，期望索引组数量为3，实际为%d", len(board.indexMap))
	}

	// 验证索引5组内有两个精灵，且按名称排序
	index5Group := board.indexMap[5]
	if index5Group.Count != 2 {
		t.Errorf("索引5组内精灵数量错误，期望为2，实际为%d", index5Group.Count)
	}

	// 验证索引5组内精灵按名称排序（sprite1应在sprite4之前）
	if index5Group.Head.Sprite.Name != "sprite1" || index5Group.Head.Next.Sprite.Name != "sprite4" {
		t.Errorf("索引5组内精灵顺序错误，期望为sprite1, sprite4，实际为%s, %s",
			index5Group.Head.Sprite.Name, index5Group.Head.Next.Sprite.Name)
	}

	// 验证按索引5查找返回两个精灵
	sprites = board.GetSpritesByIndex(5)
	if len(sprites) != 2 {
		t.Errorf("按索引5查找应返回2个精灵，实际返回%d个", len(sprites))
	}

	// 测试删除精灵
	success := board.RemoveSpriteByName("sprite1")
	if !success {
		t.Errorf("删除精灵sprite1失败")
	}

	// 验证总数量减少
	if board.count != 3 {
		t.Errorf("删除后期望精灵总数为3，实际为%d", board.count)
	}

	// 验证无法查找到已删除的精灵
	foundSprite = board.GetSpriteByName("sprite1")
	if foundSprite != nil {
		t.Errorf("已删除的精灵仍可被查找到")
	}

	// 验证索引5组仍然存在且只有一个精灵
	index5Group = board.indexMap[5]
	if index5Group == nil || index5Group.Count != 1 || index5Group.Head.Sprite.Name != "sprite4" {
		t.Errorf("删除sprite1后，索引5组状态错误")
	}

	// 测试更新精灵索引
	success = board.UpdateSpriteIndex("sprite4", 10)
	if !success {
		t.Errorf("更新精灵索引失败")
	}

	// 验证索引已更新
	foundSprite = board.GetSpriteByName("sprite4")
	if foundSprite == nil || foundSprite.Index != 10 {
		t.Errorf("精灵索引未成功更新")
	}

	// 验证原索引组被删除，新索引组已创建
	_, exists := board.indexMap[5]
	if exists {
		t.Errorf("原索引5组未被删除")
	}
	_, exists = board.indexMap[10]
	if !exists {
		t.Errorf("新索引10组未被创建")
	}

	// 测试更新精灵名称
	t.Logf("测试更新名称，当前精灵：%+v", *board.GetSpriteByName("sprite4"))
	success = board.UpdateSpriteName("sprite4", "sprite5")
	if !success {
		t.Errorf("更新精灵名称失败")
	}

	// 验证名称已更新
	foundSprite = board.GetSpriteByName("sprite5")
	if foundSprite == nil {
		t.Errorf("无法用新名称找到精灵")
	}
	foundSprite = board.GetSpriteByName("sprite4")
	if foundSprite != nil {
		t.Errorf("仍能用旧名称找到精灵")
	}
}

// 性能测试
func BenchmarkNamedSpriteBoardAdd(b *testing.B) {
	board := NewNamedSpriteBoard()
	img := image.NewRGBA(image.Rect(0, 0, 10, 10))

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sprite := &Sprite{
			Name:     "sprite" + strconv.Itoa(i),
			Position: image.Point{X: i % 100, Y: i / 100},
			Image:    img,
			Index:    i % 100, // 使用100个不同的索引，模拟多个精灵共享索引
		}
		board.AddSprite(sprite)
	}
}

func BenchmarkNamedSpriteBoardGetByName(b *testing.B) {
	// 准备测试数据
	board := NewNamedSpriteBoard()
	img := image.NewRGBA(image.Rect(0, 0, 10, 10))
	names := make([]string, 1000)

	for i := 0; i < 1000; i++ {
		name := "sprite" + strconv.Itoa(i)
		names[i] = name
		sprite := &Sprite{
			Name:     name,
			Position: image.Point{X: i % 100, Y: i / 100},
			Image:    img,
			Index:    i % 100,
		}
		board.AddSprite(sprite)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		board.GetSpriteByName(names[i%1000])
	}
}

func BenchmarkNamedSpriteBoardGetByIndex(b *testing.B) {
	// 准备测试数据
	board := NewNamedSpriteBoard()
	img := image.NewRGBA(image.Rect(0, 0, 10, 10))

	for i := 0; i < 1000; i++ {
		sprite := &Sprite{
			Name:     "sprite" + strconv.Itoa(i),
			Position: image.Point{X: i % 100, Y: i / 100},
			Image:    img,
			Index:    i % 100,
		}
		board.AddSprite(sprite)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		board.GetSpritesByIndex(i % 100)
	}
}

func TestNamedSpriteBoardRenderToImage(t *testing.T) {
	// 创建测试板
	board := NewNamedSpriteBoard()

	// 创建几个具有不同索引的彩色精灵
	// 索引值小的精灵会被先绘制，索引值大的精灵会覆盖在上面

	// 创建红色精灵（索引1，会被绘制在最底层）
	redImg := image.NewRGBA(image.Rect(0, 0, 50, 50))
	for y := 0; y < 50; y++ {
		for x := 0; x < 50; x++ {
			redImg.Set(x, y, color.RGBA{255, 0, 0, 255}) // 红色
		}
	}
	redSprite := &Sprite{
		Name:     "red",
		Position: image.Point{X: 50, Y: 50},
		Image:    redImg,
		Index:    1,
	}

	// 创建绿色精灵（索引2，会覆盖红色精灵）
	greenImg := image.NewRGBA(image.Rect(0, 0, 50, 50))
	for y := 0; y < 50; y++ {
		for x := 0; x < 50; x++ {
			greenImg.Set(x, y, color.RGBA{0, 255, 0, 255}) // 绿色
		}
	}
	greenSprite := &Sprite{
		Name:     "green",
		Position: image.Point{X: 75, Y: 75},
		Image:    greenImg,
		Index:    2,
	}

	// 创建蓝色精灵（索引2，与绿色相同索引，按名称排序在绿色之前）
	blueImg := image.NewRGBA(image.Rect(0, 0, 50, 50))
	for y := 0; y < 50; y++ {
		for x := 0; x < 50; x++ {
			blueImg.Set(x, y, color.RGBA{0, 0, 255, 255}) // 蓝色
		}
	}
	blueSprite := &Sprite{
		Name:     "blue",
		Position: image.Point{X: 85, Y: 85},
		Image:    blueImg,
		Index:    2,
	}

	// 将精灵添加到精灵板（添加顺序混乱，但应按index和name排序渲染）
	board.AddSprite(greenSprite)
	board.AddSprite(redSprite)
	board.AddSprite(blueSprite)

	// 验证精灵被正确添加
	if board.count != 3 {
		t.Errorf("期望精灵数量为3，实际为%d", board.count)
	}

	// 验证索引组数量
	if len(board.indexMap) != 2 {
		t.Errorf("期望索引组数量为2，实际为%d", len(board.indexMap))
	}

	// 验证索引为2的组内有两个精灵
	index2Group := board.indexMap[2]
	if index2Group.Count != 2 {
		t.Errorf("索引2组内精灵数量错误，期望为2，实际为%d", index2Group.Count)
	}

	// 验证索引为2的组内精灵按名称排序（blue应在green之前）
	if index2Group.Head.Sprite.Name != "blue" || index2Group.Head.Next.Sprite.Name != "green" {
		t.Errorf("索引2组内精灵顺序错误，期望为blue, green，实际为%s, %s",
			index2Group.Head.Sprite.Name, index2Group.Head.Next.Sprite.Name)
	}

	// 渲染图像
	img := board.RenderToImage()

	// 验证图像大小
	if img.Bounds().Dx() != 200 || img.Bounds().Dy() != 200 {
		t.Errorf("渲染图像大小错误，期望200x200，实际%dx%d", img.Bounds().Dx(), img.Bounds().Dy())
	}

	// 将saveImage设置为true查看实际渲染效果以调试
	saveImage := false
	if saveImage {
		err := board.SaveToPng("test_render.png")
		if err != nil {
			t.Errorf("保存图像失败: %v", err)
		}
		t.Log("图像已保存至 test_render.png")
	}

	// 检查渲染顺序：
	// 索引排序：索引小的先渲染，后渲染的会覆盖先渲染的
	// 在同一索引中，按名称字母顺序排序：blue在green之前渲染

	// 检查红色精灵位置（索引1最小，最底层）
	redPos := image.Point{X: 60, Y: 60}
	redColor := img.At(redPos.X, redPos.Y)
	r, g, b, _ := redColor.RGBA()
	if r>>8 != 255 || g>>8 != 0 || b>>8 != 0 {
		t.Errorf("位置(%d,%d)颜色错误，期望为红色(255,0,0)，实际为(%d,%d,%d)",
			redPos.X, redPos.Y, r>>8, g>>8, b>>8)
	}

	// 检查绿色特有区域
	greenPos := image.Point{X: 120, Y: 120}
	greenColor := img.At(greenPos.X, greenPos.Y)
	r, g, b, _ = greenColor.RGBA()
	if r>>8 != 0 || g>>8 != 255 || b>>8 != 0 {
		t.Errorf("位置(%d,%d)颜色错误，期望为绿色(0,255,0)，实际为(%d,%d,%d)",
			greenPos.X, greenPos.Y, r>>8, g>>8, b>>8)
	}

	// 检查蓝色特有区域
	bluePos := image.Point{X: 130, Y: 130}
	blueColor := img.At(bluePos.X, bluePos.Y)
	r, g, b, _ = blueColor.RGBA()
	if r>>8 != 0 || g>>8 != 0 || b>>8 != 255 {
		t.Errorf("位置(%d,%d)颜色错误，期望为蓝色(0,0,255)，实际为(%d,%d,%d)",
			bluePos.X, bluePos.Y, r>>8, g>>8, b>>8)
	}

	// 检查重叠区域（按实际绘制顺序）
	// 记住绘制的顺序：red(索引1) -> blue(索引2) -> green(索引2)
	// 所以重叠区域应该是最后绘制的颜色
	overlapPos := image.Point{X: 90, Y: 90}
	overlapColor := img.At(overlapPos.X, overlapPos.Y)
	r, g, b, _ = overlapColor.RGBA()

	// 即使blue按名称排序在前（b < g），但按添加顺序渲染后，green会覆盖blue
	// 按正确的排序，索引大的排后面，同索引按名称排序
	if r>>8 != 0 || g>>8 != 255 || b>>8 != 0 {
		t.Errorf("重叠位置(%d,%d)颜色错误，期望为绿色(0,255,0)，实际为(%d,%d,%d)",
			overlapPos.X, overlapPos.Y, r>>8, g>>8, b>>8)
	}
}
