// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

use super::*;
use crate::layout::spacer::spacer;
use crate::render::Canvas;

fn canvas() -> Canvas { Canvas::new( 1, 1 ) }

fn three_spacer_carousel() -> Carousel<()>
{
	carousel()
		.focused_width_frac( 0.8 )
		.gap( 0.0 )
		.push( spacer() )
		.push( spacer() )
		.push( spacer() )
}

#[ test ]
fn empty_layout_is_empty()
{
	let c: Carousel<()> = carousel();
	let rect = Rect { x: 0.0, y: 0.0, width: 100.0, height: 100.0 };
	assert!( c.layout( rect, &canvas() ).is_empty() );
}

#[ test ]
fn first_child_centred_at_offset_zero()
{
	let c = three_spacer_carousel();
	let rect = Rect { x: 0.0, y: 0.0, width: 100.0, height: 200.0 };
	let rects = c.layout( rect, &canvas() );
	// child_w = 80, base_x = (100 - 80)/2 = 10
	assert!( ( rects[0].0.x - 10.0 ).abs() < 0.01 );
	assert!( ( rects[0].0.width - 80.0 ).abs() < 0.01 );
}

#[ test ]
fn children_strided_by_child_width_plus_gap()
{
	let c = carousel::<()>()
		.focused_width_frac( 0.5 )
		.gap( 8.0 )
		.push( spacer() )
		.push( spacer() );
	let rect = Rect { x: 0.0, y: 0.0, width: 200.0, height: 100.0 };
	let rects = c.layout( rect, &canvas() );
	// child_w = 100; stride = 108; base_x = (200 - 100)/2 = 50
	assert!( ( rects[1].0.x - ( rects[0].0.x + 108.0 ) ).abs() < 0.01 );
}

#[ test ]
fn snap_offset_centres_target_index()
{
	let c = three_spacer_carousel();
	// child_w = 80, stride = 80; index 2 → offset -160 to land it centred.
	assert!( ( c.snap_offset( 100.0, 2 ) - ( -160.0 ) ).abs() < 0.01 );
}

#[ test ]
fn focused_index_rounds_to_nearest_tile()
{
	let mut c = three_spacer_carousel();
	c.offset = -120.0; // halfway between index 1 (-80) and index 2 (-160) → rounds away from 0
	assert_eq!( c.focused_index( 100.0 ), 2 );
	c.offset = -100.0; // closer to index 1
	assert_eq!( c.focused_index( 100.0 ), 1 );
}

#[ test ]
fn focused_index_clamps_to_valid_range()
{
	let mut c = three_spacer_carousel();
	c.offset = 500.0;
	assert_eq!( c.focused_index( 100.0 ), 0 );
	c.offset = -10_000.0;
	assert_eq!( c.focused_index( 100.0 ), 2 );
}

#[ test ]
fn offset_shifts_all_children_horizontally()
{
	let c = three_spacer_carousel().offset( -25.0 );
	let rect = Rect { x: 0.0, y: 0.0, width: 100.0, height: 200.0 };
	let rects = c.layout( rect, &canvas() );
	// child_w=80, base_x_at_offset_0 = 10. With offset = -25, base_x = -15.
	assert!( ( rects[0].0.x - ( -15.0 ) ).abs() < 0.01 );
	// Stride = 80, so each subsequent child is +80 of the previous.
	assert!( ( rects[1].0.x - rects[0].0.x - 80.0 ).abs() < 0.01 );
}

#[ test ]
fn focused_width_frac_is_clamped_to_unit_range()
{
	let c: Carousel<()> = carousel().focused_width_frac( 5.0 ).push( spacer() );
	assert!( c.focused_width_frac <= 1.0 );
	let c2: Carousel<()> = carousel().focused_width_frac( -3.0 ).push( spacer() );
	assert!( c2.focused_width_frac > 0.0 );
}

#[ test ]
fn children_use_full_rect_height()
{
	let c = three_spacer_carousel();
	let rect = Rect { x: 0.0, y: 0.0, width: 100.0, height: 200.0 };
	let rects = c.layout( rect, &canvas() );
	for ( r, _ ) in &rects {
		assert!( ( r.height - 200.0 ).abs() < 0.01 );
		assert!( r.y.abs() < 0.01 );
	}
}
