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

using System.Text;

using DXLib.UI;
using DXLib.UI.Alert;
using DXLib.UI.Gestures;

using DXLib.UI.Control;
using DXLib.UI.Control.List;
using DXLib.UI.Control.Badge;
using DXLib.UI.Control.Image;

using DXLib.Utils;

namespace iStatVball3;

/*
 * Displays an individual row in a lineup player/entry selection popup menu.
 */ 
public class LineupMenuRow : DXGridGestures
{
	/* Constants */

	// Possible row states
	public enum RowState
	{
		Normal,
		Reduced,
		Negative,
		Positive,
		Disabled,
		Selected
	};

	// Display color for each state
	private readonly Color[] StateColors =
	[
		DXColors.Light4,
		DXColors.Light4,
		DXColors.Accent3,
		DXColors.Positive,
		DXColors.Light4,
		DXColors.Action
	];

	// Label colors
	private static readonly Color NameColor = DXColors.Dark1;
	private static readonly Color PositionColor = DXColors.Dark3;

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

	/* Properties */
	public Player Player { get => rowPlayer; set => SetPlayer( value ); }

	// Customizable row height
	public double RowHt { get; set; }

	// Custom styling
	public Color Color { get => rowColor; set => SetColor( value ); }
	public double PositionSize { get; set; }

	// Modified checkmark badge
	public bool HasModified { get; set; } public bool IsModified { get => rowModified; set => SetModified( value ); }

	// Row currently selected?
	public bool IsSelected => rowState == RowState.Selected;

	/* Fields */
	private DXFill modifiedDot;
	private DXImageArea imageArea;
	private DXNumberBadge numberBadge;

	private DXLabel firstLbl;
	private DXLabel lastLbl;
	private DXLabel positionsLbl;

	// State control
	private RowState rowState;
	private bool rowModified;

	// Associated object
	private Player rowPlayer;
	private LineupEntry rowEntry;

	// Custom coloring
	private Color rowColor;

	// External ref
	private readonly LineupMenuView parent;

	/* Methods */
	public LineupMenuRow( LineupMenuView parent )
	{
		this.parent = parent;
	}

	// Post construction initialization
	public void Init()
	{
		bool ios = DXDevice.IsIOS;

		HeightRequest = RowHt;
		
		Padding = 0;
		RowSpacing = 0;
		ColumnSpacing = 0;

		double imageSize = RowHt;
		double badgeSize = (RowHt * 0.65);
		double posSize = (RowHt * 0.70);
		double modSize = (RowHt * 0.23);

		double fontSize = (RowHt * 0.30);
		double numSize = (RowHt * 0.35);

		double spacing = (RowHt * 0.28);

		// 2 rows
		AddStarRow();       // 0: first
		AddStarRow();       // 1: last

		// Modified
		if ( HasModified )
		{
			modifiedDot = new DXFill
			{
				Circle = modSize,
				Color = DXColors.Warn,

				Margin = DXUtils.Left( -1 ),

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

			// Android: Must use opacity instead of visible
			if ( ios )
			{
				modifiedDot.IsVisible = false;
			}
			else
			{
				modifiedDot.IsVisible = true;
				modifiedDot.Opacity = 0.0;
			}

			Fill( DXColors.Dark3, 0, 0, 1, 2 );
			Add( modifiedDot, 0, 0, 1, 2 );

			AddFixedColumn( modSize + spacing );
		}

		// Icon/Image
		imageArea = new DXImageArea
		{
			DefaultIcon = "player"
		};

		imageArea.SetImage( (ImageSource)null );
		imageArea.SetSize( imageSize, imageSize, 0.80 );

		Add( imageArea, ColumnCount, 0, 1, 2 );
		AddFixedColumn( imageSize );

		// Jersey Number 
		numberBadge = new DXNumberBadge
		{
			Size = badgeSize,
			AdjustY = (ios ? -2 : -3),

			TextColor = DXColors.Light4,
			FontSize = numSize,

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

		numberBadge.Init();

		Add( numberBadge, ColumnCount, 0, 1, 2 );
		AddFixedColumn( badgeSize + spacing );

		// First Name
		firstLbl = new DXLabel
		{
			TextColor = NameColor,
			Font = DXFonts.RobotoBold,
			FontSize = fontSize,

			LineBreakMode = LineBreakMode.TailTruncation,
			Vertical = LayoutOptions.End
		};

		Add( firstLbl, ColumnCount, 0 );

		// Last Name
		lastLbl = new DXLabel
		{
			TextColor = NameColor,
			Font = DXFonts.RobotoBold,
			FontSize = fontSize,

			LineBreakMode = LineBreakMode.TailTruncation,
			Vertical = LayoutOptions.Start
		};

		Add( lastLbl, ColumnCount, 1 );
		AddStarColumn();

		// Positions
		positionsLbl = new DXLabel
		{
			TextColor = PositionColor,

			Font = DXFonts.RobotoBold,
			FontSize = PositionSize,

			LineBreakMode = LineBreakMode.WordWrap,
			HorizontalTextAlignment = TextAlignment.Center,

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

		Add( positionsLbl, ColumnCount, 0, 1, 2 );
		AddFixedColumn( posSize );

		// Defaults
		SetState( RowState.Normal );

		// Register for row tap
		Tapped += OnTapped;
	}

	// Populates this row given a roster player
	public void SetPlayer( Player player )
	{
		Populate( player, player.Number, player.Positions );
	}

	// Populates this row given a lineup entry
	public void SetEntry( LineupEntry entry )
	{
		rowEntry = entry;

		Populate( entry.Player, entry.Number, new List<string> { entry.Position } );
	}

	// Used internally to populate row with player info
	private void Populate( Player player, string number, IList<string> positions )
	{
		rowPlayer = player;

		// Image
		imageArea.SetImage( player.ImageUrl );

		// Number
		numberBadge.Text = number;

		// First/Last
		firstLbl.Text = player.FirstName;
		lastLbl.Text = player.LastName;

		// Positions (vertical list)
		StringBuilder posStr = new();
		int count = positions.Count;

		for ( int i = 0; i < count; i++ )
		{
			string pos = positions[i];

			if ( pos != null )
			{
				posStr.Append( pos.ToUpper() );

				if ( i < (count - 1) )
				{
					posStr.AppendLine();
				}
			}
		}

		positionsLbl.Text = posStr.ToString();
	}

	// Updates state for this row
	public void SetState( RowState state )
	{
		rowState = state;

		// Selected state has special handling
		if ( !IsSelected )
		{
			bool disabled = (state == RowState.Disabled);
			bool reduced = (state == RowState.Reduced);

			// Disabled indicated by opacity
			IsEnabled = !disabled;
			Opacity = disabled ? 0.3 : (reduced ? 0.6 : 1.0);
		}

		// All states indicated by color
		BackgroundColor = StateColors[ (int)state ];

		// Possibly inverse colors
		UpdateColorState( IsSelected );
	}

	// Marks this row as modified
	private void SetModified( bool modified )
	{
		rowModified = modified;

		// Indicate with green checkmark
		UpdateColorState( IsSelected );

		modifiedDot.IsVisible = modified;

		// Android: Must use opacity
		if ( DXDevice.IsAndroid )
		{
			modifiedDot.Opacity = modified ? 1.0 : 0.0;
		}
	}

	/* Select */

	// Visually marks this row as selected (no mode check)
	public void Select()
	{
		SetState( RowState.Selected );
	}

	// Visually marks this row as selected (handles single/multi select)
	private void InternalSelect()
	{
		// Multi-select
		if ( parent.IsMultiSelect )
		{
			int count = parent.GetSelectedCount();

			// Toggle already selected
			if ( IsSelected )
			{
				if ( count > 1 )
				{
					Deselect();
					NotifySelected( null );
				}
			}
			// Select if not already max (3)
			else
			{
				Opacity = 1.0;

				if ( count < LineupMenuView.MaxSelected )
				{
					SetState( RowState.Selected );

					// Callback listener
					NotifySelected( rowPlayer );
				}
			}
		}
		// Single select
		else
		{
			parent.DeselectAll();

			SetState( RowState.Selected );

			// Callback listener
			NotifySelected( rowPlayer );
		}
	}

	// Removes visual selection
	public void Deselect()
	{
		SetState( RowState.Normal );
	}

	// Callback registered listener when player selected
	public void NotifySelected( Player player, bool cancel = false )
	{
		PlayerSelected?.Invoke( player, cancel );
	}

	/* Colors */

	// Sets custom avatar/badge color
	private void SetColor( Color color )
	{
		rowColor = color;

		// Custom color
		imageArea.Color = color;
		numberBadge.Color = color;
	}

	// Updates with inverted or normal color scheme
	private void UpdateColorState( bool inverted )
	{
		Color light = DXColors.Light4;

		// Avatar
		imageArea.Color = inverted ? light : rowColor;
		imageArea.IconColor = inverted ? rowColor : light;

		// Badge
		numberBadge.Color = inverted ? light : rowColor;
		numberBadge.TextColor = inverted ? rowColor : light;

		Color name = inverted ? light : NameColor;

		// Name
		firstLbl.TextColor = name;
		lastLbl.TextColor = name;

		// Position
		positionsLbl.TextColor = inverted ? light : PositionColor;
	}

	/* Event Callbacks */

	// User completed tap anywhere within row
	private void OnTapped( object sender, MR.Gestures.TapEventArgs args )
	{
		if ( rowState != RowState.Disabled )
		{
			// Indicate tap with opacity change
			Opacity = IsSelected ? 1.0 : 0.4;

			// Invalid selection, warn user
			if ( rowState == RowState.Negative )
			{
				DXAlert.ShowOkCancel( "teambar.sub.title", "teambar.sub.invalid", OnInvalidConfirmed, OnInvalidCancelled );
			}
			// Valid
			else
			{
				HandleTap( true );
			}
		}
	}

	// User continuing invalid selection
	private void OnInvalidConfirmed()
	{
		HandleTap( true );
	}

	// User cancelling invalid selection
	private void OnInvalidCancelled()
	{
		HandleTap( false, true );
	}

	// Calls back registered listener following tap
	private void HandleTap( bool valid, bool cancel = false )
	{
		// Normal tap
		if ( parent.Mode == LineupMenuView.SelectMode.None )
		{
			// Deselect after brief delay
			DXTimer.Delay( DXListMenu.TapDelay, () =>
			{
				Opacity = 1.0;

				// Callback listener(s)
				NotifySelected( (valid ? rowPlayer : null), cancel );
				EntrySelected?.Invoke( valid ? rowEntry : null );
			});
		}
		// Selected state
		else
		{
			InternalSelect();
		}
	}
}

//
