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

//! TabBar — segmented selector built as a composition over existing
//! widgets. The widget itself is stateless: the application owns the
//! current selection (`usize`) and calls back through
//! [`TabBar::on_select`] when the user taps another tab.
//!
//! Returns an [`Element`] directly via [`TabBar::build`] / `Into`, so it
//! drops into any layout that accepts a child widget.
//!
//! ```rust,no_run
//! # use ltk::{ tabs, TabBar };
//! # #[ derive( Clone ) ] enum Msg { SelectTab( usize ) }
//! # struct App { tab: usize }
//! # impl App { fn _ex( &self ) -> TabBar<Msg> {
//! tabs( [ "General", "Network", "Audio" ] )
//!     .selected( self.tab )
//!     .on_select( Msg::SelectTab )
//! # }}
//! ```

use crate::types::{ Color, Length };
use super::Element;

mod theme;

#[ cfg( test ) ]
mod tests;

/// Segmented horizontal tab selector. One row of pressable cells with
/// the active cell painted as a filled pill and inactive cells as plain
/// text. Build it from a slice of labels; produce an [`Element`] with
/// [`Self::build`] (or by `.into()`-ing it where an `Element` is
/// expected).
pub struct TabBar<Msg: Clone>
{
	pub( crate ) labels:   Vec<String>,
	pub( crate ) selected: usize,
	pub( crate ) on_select: Option<std::sync::Arc<dyn Fn( usize ) -> Msg>>,
	/// Background colour of the strip itself. `None` paints no
	/// background, letting the parent surface show through.
	pub( crate ) strip_bg: Option<Color>,
}

impl<Msg: Clone + 'static> TabBar<Msg>
{
	/// Build a tab bar from an iterable of labels.
	pub fn new<I, S>( labels: I ) -> Self
	where
		I: IntoIterator<Item = S>,
		S: Into<String>,
	{
		Self
		{
			labels:    labels.into_iter().map( Into::into ).collect(),
			selected:  0,
			on_select: None,
			strip_bg:  Some( crate::theme::palette().surface_alt ),
		}
	}

	/// Index of the currently selected tab. Out-of-range values are
	/// drawn as "no tab active".
	pub fn selected( mut self, idx: usize ) -> Self
	{
		self.selected = idx;
		self
	}

	/// Callback invoked with the index of the tapped tab.
	pub fn on_select( mut self, f: impl Fn( usize ) -> Msg + 'static ) -> Self
	{
		self.on_select = Some( std::sync::Arc::new( f ) );
		self
	}

	/// Override the strip background colour. Pass `None` (via
	/// [`Self::strip_bg_none`]) to disable the background entirely.
	pub fn strip_bg( mut self, c: Color ) -> Self
	{
		self.strip_bg = Some( c );
		self
	}

	/// Paint no strip background. Useful inside containers that already
	/// provide their own surface chrome.
	pub fn strip_bg_none( mut self ) -> Self
	{
		self.strip_bg = None;
		self
	}

	/// Build the [`Element`] tree representing this tab bar. Equivalent
	/// to `Element::from( self )`.
	pub fn build( self ) -> Element<Msg>
	{
		use super::{ button, container };
		use super::button::ButtonVariant;
		use crate::layout::row::row;
		use crate::layout::spacer::spacer;

		let selected = self.selected;
		let cb       = self.on_select.clone();
		let labels   = self.labels;

		let mut r = row::<Msg>().padding( Length::widget( theme::PADDING ) ).spacing( Length::widget( theme::SPACING ) );
		for ( i, label ) in labels.into_iter().enumerate()
		{
			let is_active = i == selected;
			let mut btn = button( label );
			if is_active
			{
				btn = btn.variant( ButtonVariant::Primary );
			} else {
				btn = btn.variant( ButtonVariant::Tertiary );
			}
			if let Some( ref f ) = cb
			{
				let f   = f.clone();
				let msg = f( i );
				btn = btn.on_press( msg );
			}
			r = r.push( btn );
		}
		// Trailing spacer so the strip claims the full row width and the
		// chips left-align inside it. Callers that want full-width tabs
		// can wrap the result in a Row of `flex` children themselves.
		r = r.push( spacer() );

		match self.strip_bg
		{
			Some( bg ) => container( r ).background( bg ).radius( theme::RADIUS ).into(),
			None       => Element::Row( r ),
		}
	}
}

impl<Msg: Clone + 'static> From<TabBar<Msg>> for Element<Msg>
{
	fn from( t: TabBar<Msg> ) -> Self
	{
		t.build()
	}
}

/// Create a [`TabBar`] from any iterable of label-likes.
///
/// ```rust,no_run
/// # use ltk::{ tabs, TabBar };
/// # #[ derive( Clone ) ] enum Msg { SelectTab( usize ) }
/// # struct App { tab: usize }
/// # impl App { fn _ex( &self ) -> TabBar<Msg> {
/// tabs( [ "Inbox", "Sent", "Drafts" ] )
///     .selected( self.tab )
///     .on_select( Msg::SelectTab )
/// # }}
/// ```
pub fn tabs<Msg, I, S>( labels: I ) -> TabBar<Msg>
where
	Msg: Clone + 'static,
	I:   IntoIterator<Item = S>,
	S:   Into<String>,
{
	TabBar::new( labels )
}
