// Integration coverage for `Stack` alignment / margin / translation and for
// the way `Spacer` distributes leftover space inside a `Column`.

use ltk::core::Canvas;
use ltk::{ stack, spacer, HAlign, VAlign, Rect };

fn make_canvas() -> Canvas
{
	Canvas::new( 800, 600 )
}

fn rect( x: f32, y: f32, w: f32, h: f32 ) -> Rect
{
	Rect { x, y, width: w, height: h }
}

// ── Stack: alignment ──────────────────────────────────────────────────────────

#[ test ]
fn stack_fill_child_takes_full_rect()
{
	let canvas = make_canvas();
	let s = stack::<()>().push( spacer().width( 100.0 ).height( 100.0 ) );
	let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );

	assert_eq!( layout.len(), 1 );
	let ( child_rect, _ ) = layout[ 0 ];
	// Fill ignores the spacer's preferred size and stretches across the
	// entire stack rect.
	assert_eq!( child_rect, rect( 0.0, 0.0, 400.0, 300.0 ) );
}

#[ test ]
fn stack_center_centers_child_inside_rect()
{
	let canvas = make_canvas();
	let child  = spacer().width( 100.0 ).height( 50.0 );
	let s = stack::<()>().push_aligned( child, HAlign::Center, VAlign::Center );
	let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );

	let ( child_rect, _ ) = layout[ 0 ];
	assert_eq!( child_rect.x, 150.0 );
	assert_eq!( child_rect.y, 125.0 );
	assert_eq!( child_rect.width,  100.0 );
	assert_eq!( child_rect.height, 50.0 );
}

#[ test ]
fn stack_end_bottom_anchors_to_bottom_right()
{
	let canvas = make_canvas();
	let child  = spacer().width( 80.0 ).height( 40.0 );
	let s = stack::<()>().push_aligned( child, HAlign::End, VAlign::Bottom );
	let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );

	let ( child_rect, _ ) = layout[ 0 ];
	assert_eq!( child_rect.x, 320.0 );
	assert_eq!( child_rect.y, 260.0 );
}

#[ test ]
fn stack_start_top_anchors_to_top_left()
{
	let canvas = make_canvas();
	let child  = spacer().width( 40.0 ).height( 30.0 );
	let s = stack::<()>().push_aligned( child, HAlign::Start, VAlign::Top );
	let layout = s.layout( rect( 100.0, 50.0, 400.0, 300.0 ), &canvas );

	let ( child_rect, _ ) = layout[ 0 ];
	assert_eq!( child_rect.x, 100.0 );
	assert_eq!( child_rect.y, 50.0 );
}

// ── Stack: margin ─────────────────────────────────────────────────────────────

#[ test ]
fn stack_margin_insets_fill_child_on_all_sides()
{
	let canvas = make_canvas();
	let child  = spacer().width( 50.0 ).height( 50.0 );
	let s = stack::<()>()
		.push_aligned_margin( child, HAlign::Fill, VAlign::Fill, 12.0 );
	let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );

	let ( child_rect, _ ) = layout[ 0 ];
	// Fill + margin = original rect inset on every side.
	assert_eq!( child_rect, rect( 12.0, 12.0, 376.0, 276.0 ) );
}

#[ test ]
fn stack_margin_pushes_end_anchored_child_inward()
{
	let canvas = make_canvas();
	let child  = spacer().width( 50.0 ).height( 50.0 );
	let s = stack::<()>()
		.push_aligned_margin( child, HAlign::End, VAlign::Top, 8.0 );
	let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );

	let ( child_rect, _ ) = layout[ 0 ];
	// 400 - 50 - 8 = 342 from left edge; y starts at 8.
	assert_eq!( child_rect.x, 342.0 );
	assert_eq!( child_rect.y, 8.0 );
}

#[ test ]
fn stack_margin_larger_than_half_rect_clamps_inner_to_zero()
{
	let canvas = make_canvas();
	let child  = spacer().width( 10.0 ).height( 10.0 );
	let s = stack::<()>()
		.push_aligned_margin( child, HAlign::Fill, VAlign::Fill, 200.0 );
	// Stack rect is 100×100; margin 200 → inner is clamped to (0, 0).
	let layout = s.layout( rect( 0.0, 0.0, 100.0, 100.0 ), &canvas );

	let ( child_rect, _ ) = layout[ 0 ];
	assert_eq!( child_rect.width,  0.0 );
	assert_eq!( child_rect.height, 0.0 );
}

// ── Stack: translation ────────────────────────────────────────────────────────

#[ test ]
fn stack_translation_offsets_child_after_alignment()
{
	let canvas = make_canvas();
	let child  = spacer().width( 40.0 ).height( 30.0 );
	let s = stack::<()>()
		.push_translated( child, HAlign::Start, VAlign::Top, 5.0, 7.0 );
	let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );

	let ( child_rect, _ ) = layout[ 0 ];
	assert_eq!( child_rect.x, 5.0 );
	assert_eq!( child_rect.y, 7.0 );
}

// ── Stack: order ─────────────────────────────────────────────────────────────

#[ test ]
fn stack_layout_preserves_child_order()
{
	let canvas = make_canvas();
	let s = stack::<()>()
		.push( spacer().width( 10.0 ).height( 10.0 ) )
		.push( spacer().width( 20.0 ).height( 20.0 ) )
		.push( spacer().width( 30.0 ).height( 30.0 ) );
	let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );

	assert_eq!( layout.len(), 3 );
	assert_eq!( layout[ 0 ].1, 0 );
	assert_eq!( layout[ 1 ].1, 1 );
	assert_eq!( layout[ 2 ].1, 2 );
}

// ── Stack: preferred_size ─────────────────────────────────────────────────────

#[ test ]
fn stack_preferred_size_returns_max_child_height()
{
	let canvas = make_canvas();
	let s = stack::<()>()
		.push( spacer().width( 40.0 ).height( 30.0 ) )
		.push( spacer().width( 60.0 ).height( 80.0 ) );
	let ( w, h ) = s.preferred_size( 400.0, &canvas );
	// Stack always reports `max_width` for width, and the tallest child for
	// height — Stack is meant for overlaying, not flow.
	assert_eq!( w, 400.0 );
	assert_eq!( h, 80.0 );
}

#[ test ]
fn stack_empty_preferred_size_height_is_zero()
{
	let canvas = make_canvas();
	let s: ltk::Stack<()> = stack();
	let ( _, h ) = s.preferred_size( 400.0, &canvas );
	assert_eq!( h, 0.0 );
}

// ── Spacer: builder semantics ─────────────────────────────────────────────────

// Builder-field storage (weight / fixed dimensions) is covered by internal
// unit tests in `src/layout/spacer.rs`; here we exercise only the public
// observable — `preferred_size` — which reflects those fields.

#[ test ]
fn spacer_default_has_no_fixed_size()
{
	let canvas = make_canvas();
	assert_eq!( spacer().preferred_size( &canvas ), ( 0.0, 0.0 ) );
}

#[ test ]
fn spacer_height_pins_vertical_axis_only()
{
	let canvas = make_canvas();
	assert_eq!( spacer().height( 24.0 ).preferred_size( &canvas ), ( 0.0, 24.0 ) );
}

#[ test ]
fn spacer_width_pins_horizontal_axis_only()
{
	let canvas = make_canvas();
	assert_eq!( spacer().width( 12.0 ).preferred_size( &canvas ), ( 12.0, 0.0 ) );
}

// ── Spacer in Column: leftover distribution ───────────────────────────────────

// These use `column().layout(...)` directly (Column re-exports its `layout()`
// method as `pub`). Fixed-height children consume known space; flexible spacers
// must absorb the rest.

#[ test ]
fn flexible_spacer_in_column_consumes_leftover_height()
{
	use ltk::column;

	let canvas = make_canvas();
	let col = column::<()>()
		.padding( 0.0 )
		.spacing( 0.0 )
		.push( spacer().width( 50.0 ).height( 100.0 ) )
		.push( spacer() )                 // flexible
		.push( spacer().width( 50.0 ).height( 100.0 ) );

	let layout = col.layout( rect( 0.0, 0.0, 200.0, 500.0 ), &canvas );
	assert_eq!( layout.len(), 3 );

	// Two fixed children of 100 each + one spacer absorbing the leftover 300.
	assert!( ( layout[ 0 ].0.height - 100.0 ).abs() < 1e-3 );
	assert!( ( layout[ 1 ].0.height - 300.0 ).abs() < 1e-3 );
	assert!( ( layout[ 2 ].0.height - 100.0 ).abs() < 1e-3 );
}

#[ test ]
fn weighted_spacers_split_leftover_proportionally()
{
	use ltk::column;

	let canvas = make_canvas();
	let col = column::<()>()
		.padding( 0.0 )
		.spacing( 0.0 )
		.push( spacer().weight( 1 ) )
		.push( spacer().weight( 3 ) );

	let layout = col.layout( rect( 0.0, 0.0, 200.0, 400.0 ), &canvas );
	// Total weight 4; leftover 400; weight 1 → 100, weight 3 → 300.
	assert!( ( layout[ 0 ].0.height - 100.0 ).abs() < 1e-3 );
	assert!( ( layout[ 1 ].0.height - 300.0 ).abs() < 1e-3 );
}

#[ test ]
fn fixed_height_spacer_does_not_consume_leftover()
{
	use ltk::column;

	let canvas = make_canvas();
	let col = column::<()>()
		.padding( 0.0 )
		.spacing( 0.0 )
		.push( spacer().height( 50.0 ) )  // fixed — keeps its 50 px
		.push( spacer() );                // flexible — absorbs the rest

	let layout = col.layout( rect( 0.0, 0.0, 200.0, 500.0 ), &canvas );
	assert!( ( layout[ 0 ].0.height -  50.0 ).abs() < 1e-3 );
	assert!( ( layout[ 1 ].0.height - 450.0 ).abs() < 1e-3 );
}
