package rdp

import "testing"

func TestNewPixelBuffer(t *testing.T) {
	pb := NewPixelBuffer(100, 50)

	if pb.Width != 100 {
		t.Errorf("Width = %d, want 100", pb.Width)
	}
	if pb.Height != 50 {
		t.Errorf("Height = %d, want 50", pb.Height)
	}

	expectedLen := 100 * 50 * 4
	if len(pb.Data) != expectedLen {
		t.Errorf("Data length = %d, want %d", len(pb.Data), expectedLen)
	}

	// All pixels should be initialized to zero
	for i, b := range pb.Data {
		if b != 0 {
			t.Errorf("Data[%d] = %d, want 0", i, b)
			break
		}
	}
}

func TestNewPixelBufferSmall(t *testing.T) {
	pb := NewPixelBuffer(1, 1)
	if len(pb.Data) != 4 {
		t.Errorf("1x1 buffer Data length = %d, want 4", len(pb.Data))
	}
}

func TestUpdateRegion(t *testing.T) {
	pb := NewPixelBuffer(10, 10) // 10x10 pixels

	// Create a 3x2 red patch
	patch := make([]byte, 3*2*4) // 3 wide, 2 tall
	for i := 0; i < 3*2; i++ {
		patch[i*4+0] = 255 // R
		patch[i*4+1] = 0   // G
		patch[i*4+2] = 0   // B
		patch[i*4+3] = 255 // A
	}

	// Apply at position (2, 3)
	pb.Update(2, 3, 3, 2, patch)

	// Check that pixel (2,3) is red
	offset := (3*10 + 2) * 4
	if pb.Data[offset+0] != 255 || pb.Data[offset+1] != 0 || pb.Data[offset+2] != 0 || pb.Data[offset+3] != 255 {
		t.Errorf("pixel (2,3) = [%d,%d,%d,%d], want [255,0,0,255]",
			pb.Data[offset+0], pb.Data[offset+1], pb.Data[offset+2], pb.Data[offset+3])
	}

	// Check that pixel (4,4) is red (last pixel of patch)
	offset = (4*10 + 4) * 4
	if pb.Data[offset+0] != 255 || pb.Data[offset+1] != 0 || pb.Data[offset+2] != 0 || pb.Data[offset+3] != 255 {
		t.Errorf("pixel (4,4) = [%d,%d,%d,%d], want [255,0,0,255]",
			pb.Data[offset+0], pb.Data[offset+1], pb.Data[offset+2], pb.Data[offset+3])
	}

	// Check that pixel (1,3) is still black (just outside patch)
	offset = (3*10 + 1) * 4
	if pb.Data[offset+0] != 0 || pb.Data[offset+3] != 0 {
		t.Errorf("pixel (1,3) should be untouched, got [%d,%d,%d,%d]",
			pb.Data[offset+0], pb.Data[offset+1], pb.Data[offset+2], pb.Data[offset+3])
	}

	// Check that pixel (5,3) is still black (just outside patch)
	offset = (3*10 + 5) * 4
	if pb.Data[offset+0] != 0 || pb.Data[offset+3] != 0 {
		t.Errorf("pixel (5,3) should be untouched, got [%d,%d,%d,%d]",
			pb.Data[offset+0], pb.Data[offset+1], pb.Data[offset+2], pb.Data[offset+3])
	}
}

func TestUpdateRegionClipping(t *testing.T) {
	pb := NewPixelBuffer(10, 10)

	// Create a 5x5 green patch and place it at (8, 8), so it overflows
	patch := make([]byte, 5*5*4)
	for i := 0; i < 5*5; i++ {
		patch[i*4+0] = 0
		patch[i*4+1] = 255
		patch[i*4+2] = 0
		patch[i*4+3] = 255
	}

	// Should not panic — overflowing regions are clipped
	pb.Update(8, 8, 5, 5, patch)

	// Pixel (8,8) should be green
	offset := (8*10 + 8) * 4
	if pb.Data[offset+1] != 255 {
		t.Errorf("pixel (8,8) G = %d, want 255", pb.Data[offset+1])
	}

	// Pixel (9,9) should also be green (last valid pixel)
	offset = (9*10 + 9) * 4
	if pb.Data[offset+1] != 255 {
		t.Errorf("pixel (9,9) G = %d, want 255", pb.Data[offset+1])
	}
}

func TestDirtyFlag(t *testing.T) {
	pb := NewPixelBuffer(10, 10)

	if pb.IsDirty() {
		t.Error("new buffer should not be dirty")
	}

	// Update a region
	patch := make([]byte, 4) // 1x1 pixel
	patch[0] = 255
	patch[3] = 255
	pb.Update(0, 0, 1, 1, patch)

	if !pb.IsDirty() {
		t.Error("buffer should be dirty after update")
	}

	pb.ClearDirty()

	if pb.IsDirty() {
		t.Error("buffer should not be dirty after ClearDirty")
	}
}

func TestGetFrameReturnsCopy(t *testing.T) {
	pb := NewPixelBuffer(2, 2)

	// Set first pixel to white
	patch := []byte{255, 255, 255, 255}
	pb.Update(0, 0, 1, 1, patch)

	frame1 := pb.GetFrame()

	// Modify the returned frame
	frame1[0] = 0

	// Get another frame — it should still have the original value
	frame2 := pb.GetFrame()
	if frame2[0] != 255 {
		t.Errorf("GetFrame did not return an independent copy: got %d, want 255", frame2[0])
	}
}

func TestFullFrameUpdate(t *testing.T) {
	pb := NewPixelBuffer(4, 4)

	// Create a full-frame update with all blue pixels
	fullFrame := make([]byte, 4*4*4)
	for i := 0; i < 4*4; i++ {
		fullFrame[i*4+0] = 0
		fullFrame[i*4+1] = 0
		fullFrame[i*4+2] = 255
		fullFrame[i*4+3] = 255
	}

	pb.Update(0, 0, 4, 4, fullFrame)

	frame := pb.GetFrame()
	for i := 0; i < 4*4; i++ {
		if frame[i*4+2] != 255 {
			t.Errorf("pixel %d blue channel = %d, want 255", i, frame[i*4+2])
			break
		}
	}
}
