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

use crate::render::Canvas;
use super::Element;

/// Wraps an [`Element`] so its parent treats it like a
/// [`Spacer`](crate::layout::spacer::Spacer) for leftover-space
/// distribution, but draws the child inside the allocated rect: leftover
/// **width** inside a [`Row`](crate::layout::row::Row), leftover
/// **height** inside a [`Column`](crate::layout::column::Column).
///
/// Use this to make a non-trivial child fill remaining space without
/// resorting to a hard-coded size. The parent computes how much of its
/// main axis is left after the fixed-size siblings, splits it across all
/// flex / spacer children proportionally to their `weight`, and gives
/// the flex its share. The wrapped child sees that share as its layout
/// rect — text inside it triggers its own elide path on overflow,
/// columns adjust their inner width, etc.
///
/// ```rust,no_run
/// # use ltk::{ column, flex, row, Element };
/// # #[ derive( Clone ) ] enum Msg {}
/// # fn _ex(
/// #     icon:     Element<Msg>,
/// #     title:    Element<Msg>,
/// #     subtitle: Element<Msg>,
/// # ) -> Element<Msg> {
/// row()
///     .push( icon )
///     .push( flex( column().push( title ).push( subtitle ) ) )
/// .into()
/// # }
/// ```
///
pub struct Flex<Msg: Clone>
{
	pub( crate ) child:  Box<Element<Msg>>,
	pub( crate ) weight: u32,
}

impl<Msg: Clone> Flex<Msg>
{
	pub fn new( child: impl Into<Element<Msg>> ) -> Self
	{
		Self
		{
			child:  Box::new( child.into() ),
			weight: 1,
		}
	}

	/// Relative weight when sharing leftover space with other flex / spacer
	/// children in the same row or column (default `1`). A flex with
	/// `weight = 2` claims twice the space of a sibling with `weight = 1`.
	pub fn weight( mut self, w: u32 ) -> Self
	{
		self.weight = w;
		self
	}

	pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> ( f32, f32 )
	{
		// Width contribution to the parent row is zero — the actual width
		// comes from the flex distribution. Height comes from the child so
		// the row's row-height calculation still picks us up.
		let ( _, h ) = self.child.preferred_size( max_width, canvas );
		( 0.0, h )
	}

	pub( crate ) fn map_msg<U>( self, f: &super::MapFn<Msg, U> ) -> Flex<U>
	where
		U: Clone + 'static,
		Msg: 'static,
	{
		Flex
		{
			child:  Box::new( self.child.map_arc( f ) ),
			weight: self.weight,
		}
	}
}

pub fn flex<Msg: Clone>( child: impl Into<Element<Msg>> ) -> Flex<Msg>
{
	Flex::new( child )
}

impl<Msg: Clone + 'static> From<Flex<Msg>> for Element<Msg>
{
	fn from( f: Flex<Msg> ) -> Self
	{
		Element::Flex( f )
	}
}

#[ cfg( test ) ]
mod tests;
