#![ cfg( feature = "test-support" ) ]

mod common;
use common::{ lw_none, rect };

use ltk::test_support::next_focusable_index;

// ── Empty / single-element edge cases ─────────────────────────────────────────

#[ test ]
fn empty_widget_list_returns_none()
{
	let widgets: Vec<_> = vec![];
	assert_eq!( next_focusable_index::<()>( &widgets, None,        false ), None );
	assert_eq!( next_focusable_index::<()>( &widgets, Some( 42 ),  false ), None );
	assert_eq!( next_focusable_index::<()>( &widgets, None,        true  ), None );
}

#[ test ]
fn single_widget_wraps_to_itself()
{
	let widgets = vec![ lw_none( 7, rect( 0.0, 0.0, 10.0, 10.0 ) ) ];
	assert_eq!( next_focusable_index( &widgets, Some( 7 ), false ), Some( 7 ) );
	assert_eq!( next_focusable_index( &widgets, Some( 7 ), true  ), Some( 7 ) );
}

// ── Forward (Tab) ─────────────────────────────────────────────────────────────

#[ test ]
fn forward_advances_to_next_in_slice_order()
{
	let widgets = vec![
		lw_none( 10, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 20, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 30, rect( 0.0, 0.0, 10.0, 10.0 ) ),
	];
	assert_eq!( next_focusable_index( &widgets, Some( 10 ), false ), Some( 20 ) );
	assert_eq!( next_focusable_index( &widgets, Some( 20 ), false ), Some( 30 ) );
}

#[ test ]
fn forward_wraps_around_at_end()
{
	let widgets = vec![
		lw_none( 1, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 2, rect( 0.0, 0.0, 10.0, 10.0 ) ),
	];
	assert_eq!( next_focusable_index( &widgets, Some( 2 ), false ), Some( 1 ) );
}

#[ test ]
fn forward_with_no_focus_targets_first_widget()
{
	let widgets = vec![
		lw_none( 100, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 200, rect( 0.0, 0.0, 10.0, 10.0 ) ),
	];
	assert_eq!( next_focusable_index( &widgets, None, false ), Some( 100 ) );
}

// ── Reverse (Shift+Tab) ───────────────────────────────────────────────────────

#[ test ]
fn reverse_steps_to_previous()
{
	let widgets = vec![
		lw_none( 10, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 20, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 30, rect( 0.0, 0.0, 10.0, 10.0 ) ),
	];
	assert_eq!( next_focusable_index( &widgets, Some( 30 ), true ), Some( 20 ) );
	assert_eq!( next_focusable_index( &widgets, Some( 20 ), true ), Some( 10 ) );
}

#[ test ]
fn reverse_wraps_around_at_start()
{
	let widgets = vec![
		lw_none( 10, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 20, rect( 0.0, 0.0, 10.0, 10.0 ) ),
	];
	assert_eq!( next_focusable_index( &widgets, Some( 10 ), true ), Some( 20 ) );
}

#[ test ]
fn reverse_with_no_focus_targets_last_widget()
{
	let widgets = vec![
		lw_none( 100, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 200, rect( 0.0, 0.0, 10.0, 10.0 ) ),
	];
	assert_eq!( next_focusable_index( &widgets, None, true ), Some( 200 ) );
}

// ── Stale focus index ─────────────────────────────────────────────────────────

#[ test ]
fn stale_focus_idx_falls_back_to_first()
{
	// Common after a re-layout removes the previously-focused widget: the
	// stored focus idx no longer maps to any entry, and the walker falls
	// back to the same behaviour as `None`.
	let widgets = vec![
		lw_none( 10, rect( 0.0, 0.0, 10.0, 10.0 ) ),
		lw_none( 20, rect( 0.0, 0.0, 10.0, 10.0 ) ),
	];
	assert_eq!( next_focusable_index( &widgets, Some( 999 ), false ), Some( 10 ) );
	assert_eq!( next_focusable_index( &widgets, Some( 999 ), true  ), Some( 20 ) );
}
