/*
 * Copyright (c)2009-2025 DemiVision, LLC. All Rights Reserved. The information
 * herein is the CONFIDENTIAL and PROPRIETARY information of DemiVision, LLC.
 */

using DXLib.UI;

using DXLib.UI.Layout;
using DXLib.UI.Control;
using DXLib.UI.Container;

using DXLib.Utils;

namespace iStatVball3;

/*
 * Displays a list of players in a vertical scrolling list. Used to select a player for a lineup, substitution, stat
 * editing, etc. Each player row contains an image, jersey number, player name, and roster positions. Can be embedded
 * either in a parent layout or popup menu.
 */
public class LineupMenuView : DXGridLayout
{
	/* Constants */

	// Multiselect limited to 3 (blockers)
	public const int MaxSelected = 3;

	// Possible selection modes
	public enum SelectMode
	{
		None,
		Single,
		Multiple
	};

	// Layout
	private const double ScrollRows = 7.5;
	private const double SeparatorHt = 1;

	/* Events */
	public Action<Player,bool> PlayerSelected { get; set; }
	public Action<LineupEntry> EntrySelected { get; set; }

	/* Properties */

	// Embedded view or standalone popup?
	public bool Embedded { get; set; }

	// Single/multi select
	public SelectMode Mode { get; set; }
	public bool IsMultiSelect => Mode == SelectMode.Multiple;

	// Custom styling
	public string Header { set => headerLbl.Text = value; }
	public Color Color { get => rowColor; set => SetColor( value ); }

	public bool HasTallRows { get; set; }
	public bool HasClear { set => footer.HasClear = value; }
	public bool HasModified { get; set; }

	/* Fields */
	private DXGridLayout innerLayout;

	private DXLabel headerLbl;
	private DXScroll scroll;
	private LineupMenuFooter footer;

	// Underlying player rows
	private readonly List<LineupMenuRow> rows;

	private double headerHt;
	private double scrollHt;
	private double footerHt;

	private Color rowColor;

	/* Methods */
	public LineupMenuView()
	{
		// Allocate container
		rows = [];

		BackgroundColor = DXColors.Dark1;

		// Layout
		Padding = 0;
		RowSpacing = 0;
		ColumnSpacing = 0;

		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;

		// Defaults
		Mode = SelectMode.None;
	}

	// Post construction initialization
	public void Init( int rowCount, bool hasFooter, bool hasHeader = true )
	{
		bool tablet = DXDevice.IsTablet;

		// Optional header
		if ( hasHeader )
		{
			headerHt = tablet ? 36 : 25;

			// Header
			headerLbl = new DXLabel
			{
				TextColor = DXColors.Light2,
				
				Font = DXFonts.RobotoBold,
				FontSize = DXDevice.IsTablet ? 20 : 15,

				Horizontal = LayoutOptions.Center,
				Vertical = LayoutOptions.Center
			};

			AddFixedRow( headerHt );
			Add( headerLbl, 0, 0 );
		}

		// Vertical scroll area
		scroll = new DXScroll
		{
			BackgroundColor = DXColors.Light1,
			Padding = 0
		};

		// Player rows
		innerLayout = new DXGridLayout
		{
			BackgroundColor = Embedded ? DXColors.Light2 : DXColors.Dark1,

			Padding = new Thickness( 0, 0, 0, 1 ),
			RowSpacing = SeparatorHt,
			ColumnSpacing = 0,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill
		};

		// Variable row height
		double rowHt = HasTallRows ? 50 : 36;

		// Position label
		double posSize = hasHeader ? (tablet ? 11 : 8) : (tablet ? 13 : 10);

		// Add fixed number rows
		for ( int i = 0; i < rowCount; i++ )
		{
			LineupMenuRow row = new( this )
			{
				HasModified = HasModified,

				RowHt = rowHt,
				PositionSize = posSize,

				PlayerSelected = OnPlayerSelected,
				EntrySelected = OnEntrySelected
			};

			row.Init();
			rows.Add( row );

			innerLayout.AddFixedRow( rowHt );
			innerLayout.Add(row, 0, (innerLayout.RowCount - 1) ); 
		}

		scroll.Content = innerLayout;

		// Embedded fills layout area
		if ( Embedded )
		{
			AddStarRow();
		}
		// Popup shows partial list
		else
		{
			double count = Embedded ? rowCount : Math.Min( rowCount, ScrollRows );
			scrollHt = (count * rowHt) + ((Math.Floor( count - 1 ) * SeparatorHt)) + 1;

			AddFixedRow( scrollHt );
		}

		Add( scroll, 0, (RowCount - 1) );

		// Footer optional
		if ( hasFooter )
		{
			footerHt = headerHt;

			// Footer
			footer = new LineupMenuFooter
			{
				Selected = OnPlayerSelected
			};

			AddFixedRow( footerHt );
			Add( footer, 0, (RowCount - 1) );
		}
	}

	// Returns dynamic display height for lineup view
	public double GetHeight()
	{
		return (Padding.VerticalThickness + headerHt + scrollHt + footerHt);
	}

	// Returns menu row matching specified player
	private LineupMenuRow RowForPlayer( Player player )
	{
		return (player == null) ? null : rows.FirstOrDefault( row => Equals( row.Player, player ) );
	}

	/* Set */

	// Populates menu with list of players (roster, available subs, etc)
	public void SetPlayers( IList<Player> players, IList<LineupMenuRow.RowState> states = null )
	{
		int count = Math.Min( rows.Count, players.Count );

		for ( int i = 0; i < count; i++ )
		{
			rows[i].SetPlayer( players[i] );
			rows[i].SetState( (states == null) ? LineupMenuRow.RowState.Normal : states[i] );
		}
	}

	// Populates menu with list of lineup entries (Legacy libero swap)
	public void SetEntries( IList<LineupEntry> entries )
	{
		for ( int i = 0; i < rows.Count; i++ )
		{
			rows[i].SetEntry( entries[i] );
		}
	}

	// Reduces emphasis for subset of players (typically those already in lineup)
	public void SetReduced( List<Player> players )
	{
		foreach ( LineupMenuRow row in rows )
		{
			bool reduced = players.Contains( row.Player );

			row.SetState( reduced ? LineupMenuRow.RowState.Reduced : LineupMenuRow.RowState.Normal );
		}
	}

	// Visually disables subset of players
	public void SetDisabled( List<Player> players )
	{
		foreach ( LineupMenuRow row in rows.Where( row => players.Contains( row.Player ) ) )
		{
			row.SetState( LineupMenuRow.RowState.Disabled );
		}
	}

	// Marks menu row for specified player as being modified
	public void SetModified( Player player, bool modified )
	{
		LineupMenuRow row = RowForPlayer( player );

		if ( row != null )
		{
			row.IsModified = modified;
		}
	}

	// Sets color of all underlying rows
	public void SetColor( Color color )
	{
		rowColor = color;

		foreach ( LineupMenuRow row in rows )
		{
			row.Color = color;
		}
	}

	/* Select */

	// Visually selects row(s) for player(s) in specified stat
	public void Select( Stat stat )
	{
		DeselectAll();

		Mode = stat.IsBlock ? SelectMode.Multiple : SelectMode.Single;

		// Player 1
		RowForPlayer( stat.Player )?.Select();

		// Player 2/3
		if ( stat.IsBlock )
		{
			RowForPlayer( stat.Player2 )?.Select();
			RowForPlayer( stat.Player3 )?.Select();
		}
	}

	// Visually deselects all rows in menu
	public void DeselectAll()
	{
		foreach ( LineupMenuRow row in rows )
		{
			row.Deselect();
		}
	}

	// Returns currently selected player, if any (single select)
	public Player GetSelectedPlayer()
	{
		List<Player> selected = GetSelectedPlayers();

		return (selected.Count == 0) ? null : selected[0];
	}

	// Returns selected player(s) (multi select)
	public List<Player> GetSelectedPlayers()
	{
		List<Player> selected = new( MaxSelected );

		// Find selected players
		foreach ( LineupMenuRow row in rows )
		{
			if ( row.IsSelected )
			{
				selected.Add( row.Player );
			}
		}

		return selected;
	}

	// Returns number currently selected players (0-3)
	public int GetSelectedCount()
	{
		return GetSelectedPlayers().Count;
	}

	/* Show/Hide */

	// Shows lineup view
	public void Show()
	{
		scroll.IsVisible = true;
	}

	// Hides lineup view
	public void Hide()
	{
		scroll.IsVisible = false;
	}

	/* Event Callbacks */

	// Calls back registered listener when menu row selected
	private void OnPlayerSelected( Player player )
	{
		PlayerSelected?.Invoke( player, false );
	}

	// Calls back registered listener when menu row selected
	private void OnPlayerSelected( Player player, bool cancel )
	{
		PlayerSelected?.Invoke( player, cancel );
	}

	// Calls back registered listener when menu row selected
	private void OnEntrySelected( LineupEntry entry )
	{
		if ( entry != null )
		{
			EntrySelected?.Invoke( entry );
		}
	}
}

//
