/* 
 * 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.Control;
using DXLib.UI.Gestures;
using DXLib.UI.Container;

using DXLib.UI.Control.Image;
using DXLib.UI.Control.Button;

using DXLib.Data;
using DXLib.Utils;

namespace iStatVball3;

/*
 * Draws the libero area within the LegacyScore scoreboard. The libero area is used to swap the libero in/out of the
 * game. There can be 0-2 liberos defined. The user can toggle between multiple liberos.
 */
public class LegacyLibero : DXBorder
{
	/* Constants */
	private static readonly Color DividerColor = DXColors.Dark3;
	
	/* Properties */

	// Libero 1 active?
	private bool IsLibero1 => StateMachine.Libero1 == 1;

	// Active zone (LIB1, LIB2)
	private int Zone => IsLibero1 ? Lineup.LiberoZone1 : Lineup.LiberoZone2;
	private Color ZoneColor => liberoBtn.IsDisabled ? DXColors.Dark3 : DXColors.Action;

	// Convenience access to active entry
	private Player Player => GetActive().Player;
	private string Position => GetActive().Position;
	private bool IsAltNumber => GetActive().IsAltNumber;

	private bool IsEmpty => Player == null;
	private bool IsLibero => Position is Lineup.LiberoKey;

	private bool HasAltNumber => Player is { HasAltNumber: true };

	// External ref
	private LegacyState StateMachine => (LegacyState)parent.StateMachine;

	/* Fields */
	private readonly DXGridGestures layout;

	private readonly DXButton liberoBtn;
	private readonly DXImageArea avatarImg;

	private readonly DXLabel firstLbl;
	private readonly DXLabel lastLbl;

	private readonly DXNumberButton numberBtn;
	private readonly DXLabel positionLbl;

	private readonly DXButton swapBtn;

	// External refs
	private readonly LegacyScore parent;
	private Legacy engine;

	// Swap selection
	private readonly LineupMenu swapMenu;

	// Libero 1/2
	private LineupEntry libero1;
	private LineupEntry libero2;

	/* Methods */
	public LegacyLibero( LegacyScore parent )
	{
		this.parent = parent;
		
		// Simulate Material card
		Color = DXColors.Light1;
		CornerRadius = 5;

		// Underlying layout
		layout = new DXGridGestures
		{
			IsClippedToBounds = false,

			Padding = 0,
			Margin = 0,
			
			RowSpacing = 0,
			ColumnSpacing = 0,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill
		};

		// Libero 1/2
		liberoBtn = new DXButton
		{
			ButtonColor = DXColors.Light4,
			Font = DXFonts.Oswald,
			CornerRadius = 0,
			
			DisabledOpacity = 1.0,
			HasShadow = false,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill,
			
			ButtonTapped = OnSwitched
		};

		SetLiberoText( 1 );

		// Avatar
		avatarImg = new DXImageArea
		{
			IsVisible = false,

			DefaultIcon = "player",
			IconColor = DXColors.Light4
		};

		// First
		firstLbl = new DXLabel
		{
			TextColor = DXColors.Dark1,
			Font = DXFonts.RobotoBold,
			LineBreakMode = LineBreakMode.TailTruncation
		};

		// Last
		lastLbl = new DXLabel
		{
			TextColor = DXColors.Dark1,
			Font = DXFonts.RobotoBold,
			LineBreakMode = LineBreakMode.TailTruncation
		};

		// Number
		numberBtn = new DXNumberButton
		{
			IsVisible = false,
			
			IsCircle = true,
			IsSticky = true,

			NumberColor = DXColors.Light4,
			DisabledOpacity = 1.0,

			ButtonTapped = OnNumberTapped
		};

		numberBtn.Init();
		
		// Position
		positionLbl = new DXLabel
		{
			TextColor = DXColors.Dark3,
			Font = DXFonts.RobotoBold
		};

		// Swap
		swapBtn = new DXButton
		{
			IsSticky = true,
			ButtonTapped = OnSwapTapped
		};

		swapBtn.Init();
		
		Content = layout;

		// Swap menu
		swapMenu = new LineupMenu( Lineup.RowEntries, false, false )
		{
			EntrySelected = OnSwapSelected,
			Cancelled = OnSwapCancelled
		};

		// Register for events
		layout.Tapped += OnTapped;
		layout.Pressed = OnPressed;
	}

	// Post construction initialization
	public void Init( Legacy legacy )
	{
		engine = legacy;

		// Default libero 1
		StateMachine.ChangeLibero( true, 1 );
	}

	/* Libero State */

	// Returns currently active libero
	private LineupEntry GetActive()
	{
		return IsLibero1 ? libero1 : libero2;
	}

	// Returns number liberos defined in lineup
	private int GetCount()
	{
		int count = 0;

		if ( !libero1.IsEmpty )
		{
			count++;
		}
		if ( !libero2.IsEmpty )
		{
			count++;
		}

		return count;
	}

	/* Active */

	// Sets active libero as either 1 or 2
	private void SetActive( int number )
	{
		StateMachine.ChangeLibero( true, number );

		// 'LIBX'
		SetLiberoText( number );

		// Populate UI
		SetEntry( number, (IsLibero1 ? libero1 : libero2) );
	}

	// Updates libero zone text as 'LIB 1/2'
	private void SetLiberoText( int number )
	{
		liberoBtn.Text = (number == 0) ? string.Empty : $"{DXString.GetUpper( "teambar.lib" )} {number}";
	}

	// Switches between libero 1/2
	private void ToggleActive()
	{
		SetActive( (IsLibero1 ? 2 : 1) );
	}

	/* Get/Set */

	// Returns libero player(s)
	public List<Player> GetPlayers()
	{
		// Include empty
        List<Player> players =
        [
	        libero1.IsEmpty ? null : libero1.Player,
	        libero2.IsEmpty ? null : libero2.Player
        ];

        return players;
	}

	// Returns specified libero entry
	public LineupEntry GetEntry( int number )
	{
		// MUST deep copy
		return new LineupEntry( (number == 1) ? libero1 : libero2 );
	}

	// Sets entry for specified libero number
	public void SetEntry( int number, LineupEntry entry )
	{
		if ( number == 1 )
		{
			libero1 = entry;
		}
		else
		{
			libero2 = entry;
		}

		// Update UI
		Populate( entry?.Player, entry?.Position, entry is { IsAltNumber: true } );
	}

	// Sets entries for both liberos
	public void SetEntries( List<LineupEntry> entries )
	{
		libero1 = entries[ Lineup.LiberoZone1 - 1 ];
		libero2 = entries[ Lineup.LiberoZone2 - 1 ];

		Update();
	}

	// Populates from menu selection
	private void SetPlayer( Player player )
	{
		SetEntry( StateMachine.Libero1, new LineupEntry
		{
			Zone = Zone,

			Position = Lineup.LiberoKey,
			IsAltNumber = false,

			PlayerId = player?.UniqueId,
			Player = player,
		});
	}

	/* Populate */

	// Populates fields with specified player info
	private void Populate( Player player, string position, bool altNumber )
	{
		// Darker color for libero
		Color = IsLibero ? DXColors.Dark4 : DXColors.Light1;

		Color color = (player == null) ? Organization.DefaultColor : player.Season.Color;

		// Avatar
		avatarImg.IsVisible = !IsEmpty;
		avatarImg.Color = color;
		avatarImg.SetImage( player?.ImageUrl );

		// Name
		firstLbl.Text = player?.FirstName!;
		lastLbl.Text = player?.LastName!;

		// Number
		numberBtn.IsVisible = !IsEmpty;
		numberBtn.IsDisabled = engine.IsEditOff || !HasAltNumber;
		numberBtn.Text = player?.GetNumber( altNumber );
		numberBtn.Color = color;

		// Position
		positionLbl.IsVisible = !IsEmpty;
		positionLbl.Text = (IsLibero ? DXString.Get( "lineup.libero" ) : position)?.ToUpper()!;

		// Menu
		swapMenu.MenuColor = color;

		// Update background/button
		UpdateLayout();
		UpdateSwap();
	}

	// Displays menu for selecting replacement player
	private void ShowLineupMenu()
	{
		LineupMenu menu = engine.GetLineupMenu();

		// Update popup menu
		menu.Header = DXString.GetUpper( "lineup.libero" );
		menu.HasClear = !IsEmpty;

		menu.SetReduced( engine.GetPlayers() );
		menu.SetDisabled( [ Player ] );

		menu.PlayerSelected = OnLineupSelected;

		menu.ShowFromView( this );
	}

	/* UI Control */

	// Disables buttons while rally in-progress
	public void Disable( bool disable )
	{
		swapBtn.IsVisible = (!disable && engine.IsEditOff && !IsEmpty);
	}

	// Updated state based on current edit mode
	public void UpdateEditMode()
	{
		bool off = engine.IsEditOff;

		numberBtn.IsDisabled = (off || !HasAltNumber);
		swapBtn.IsVisible = (off && !IsEmpty);

		Update();
		UpdateLayout();
	}

	// Updates visual in/out state of swap button
	private void UpdateSwap()
	{
		swapBtn.Reset();
		swapBtn.IsVisible = (engine.IsEditOff && !IsEmpty);

		// Button hidden when empty
		if ( !IsEmpty )
		{
			swapBtn.Resource = IsLibero ? "teambar.swapin" : "teambar.swapout";
			swapBtn.Type = IsLibero ? DXButton.ButtonType.Positive : DXButton.ButtonType.Negative;
		}
	}

	// Updates libero area UI based on current state
	public void Update()
	{
		int count = GetCount();
		bool hasLibero = (count > 0);

		// Update button states
		liberoBtn.IsDisabled = ((count < 2) && !engine.IsEditOn);
		liberoBtn.ButtonColor = ZoneColor;

		swapBtn.IsDisabled = !hasLibero;

		// Re-populate if at least 1 libero
		if ( hasLibero )
		{
			int number = StateMachine.Libero1;

			SetActive( number );
			LineupEntry libero = GetEntry( number );

			// Default to libero 1
			if ( (libero == null) || libero.IsEmpty )
			{
				number = 1;

				SetActive( number );
				libero = GetEntry( number );
			}

			SetEntry( number, libero );
		}
	}

	// Handles player replacement request
	private void HandleReplace( Point point )
	{
		if ( IsEmpty || IsLibero )
		{
			// Exclude internal buttons
			if ( !liberoBtn.Contains( point ) && !numberBtn.Contains( point ) )
			{
				ShowLineupMenu();
			}
		}
	}

	/* Event Callbacks */

	// LINEUP

	// User long pressed in libero area
	private void OnTapped( object sender, MR.Gestures.TapEventArgs args )
	{
		// Single tap only during blank lineup edit
		if ( engine.IsEditLineup )
		{
			HandleReplace( args.Touches[0] );
		}
	}

	// User long pressed in libero area
	private void OnPressed( Point point )
	{
		// Long press always works when editing
		if ( engine.IsEditOn )
		{
			HandleReplace( point );
		}
	}

	// User chose replacement player
	private void OnLineupSelected( Player player, bool cancel )
	{
		SetPlayer( player );
	}

	// SWAP

	// User tapped swap in/out
	private void OnSwapTapped( object sender )
	{
		// Swap-in, show menu
		if ( IsLibero )
		{
			swapMenu.SetEntries( StateMachine.GetBackrow() );
			swapMenu.ShowFromView( swapBtn );
		}
		// Swap-out
		else
		{
			StateMachine.SwapLiberoOut( false );

			Update();
			UpdateSwap();
		}
	}

	// User selected swap-in destination
	private void OnSwapSelected( LineupEntry entry )
	{
		// Swap for selected player
		StateMachine.SwapLiberoIn( entry );

		Update();
		UpdateSwap();
	}

	// User cancelled swap
	private void OnSwapCancelled()
	{
		swapBtn.Reset();
	}

	// NUMBER

	// User tapped jersey number
	private void OnNumberTapped( object sender )
	{
		// Only valid if multiple numbers
		if ( HasAltNumber )
		{
			string num = Player.Number;
			string alt = Player.AltNumber;

			// Build number list
			List<DXItem> list =
			[
				new( LineupEntry.NumberKey, num ),
				new( LineupEntry.AltNumberKey, alt )
			];

			// Show menu (highlight current value)
			DXMenu numMenu = new()
			{
				Title = "lineup.number",
				MenuWd = 100,

				ItemSelected = OnNumberSelected,
				Cancelled = OnNumberCancelled
			};

			string selected = Player.GetNumber( IsAltNumber );

			numMenu.SetItems( list, selected );
			numMenu.ShowFromView( numberBtn );
		}
	}

	// User selected number from popup menu
	private void OnNumberSelected( DXItem item )
	{
		// User may have cancelled
		if ( item != null )
		{
			GetActive().IsAltNumber = (item.Key == LineupEntry.AltNumberKey);

			numberBtn.Text = item.Value;
		}

		numberBtn.Reset();
	}

	// User cancelled number selection
	private void OnNumberCancelled()
	{
		numberBtn.Reset();
	}

	// SWITCH

	// User toggled between libero 1/2
	private void OnSwitched( object sender )
	{
		ToggleActive();
	}

	/* Layout */

	// Redraws Legacy libero area
	public override void UpdateLayout( LayoutType type )
	{
		layout.ClearAll();

		base.UpdateLayout( type );
	}

	// Landscape (4:3) [dynamic]
	protected override void Landscape()
	{
		bool ios = DXDevice.IsIOS;
		bool wide = DXDevice.IsTabletWide;

		double dim = DXDevice.GetScreenDim();

		// Sizing constants
		double pad = (dim * 0.008);

		double avatarSize = (dim * 0.050);
		double badgeSize = (wide ? 30 : (dim * 0.028));
		double fontSize = (wide ? 16 : (dim * 0.015));
		double numberSize = (dim * 0.018);

		double swapWd = (dim * (wide ? 0.075 : 0.082));
		double swapHt = (dim * 0.025);

		double row1Ht = wide ? 26 : (dim * 0.023);
		double row2Ht = wide ? 23 : (dim * 0.021);

		double col1Wd = avatarSize + (pad * 2);

		// 4 rows
		layout.AddFixedRow( row1Ht );			// 0: zone, first
		layout.AddFixedRow( row2Ht );			// 1: last
		layout.AddFixedRow( badgeSize );		// 2: number, pos
		layout.AddStarRow();					// 3: swap

		// 3 columns
		layout.AddFixedColumn( col1Wd );		// 0: zone, avatar
		layout.AddFixedColumn( 2 );				// 1: divider
		layout.AddStarColumn();					// 2: others

		// Libero 1/2
		liberoBtn.FontSize = numberSize;

		liberoBtn.ButtonWd = col1Wd;
		liberoBtn.ButtonHt = row1Ht;

		layout.Fill( ZoneColor, 0, 0 );
		layout.Add( liberoBtn, 0, 0 );

		// Avatar
		avatarImg.SetMargin( pad, (wide ? 0 : pad), pad, 0 );
		avatarImg.SetPosition( LayoutOptions.Center, (wide ? LayoutOptions.Center : LayoutOptions.Start) );
		avatarImg.SetSize( avatarSize, avatarSize, 0.80 );

		layout.Add( avatarImg, 0, 1, 1, 3 );

		// Divider
		layout.Fill( DividerColor, 1, 0, 1, 4 );

		Color rightBgnd = IsEmpty ? DXColors.Light2 : DXColors.Light4;

		// Right background
		layout.Fill( rightBgnd, 2, 0, 1, 4 );

		// First
		firstLbl.FontSize = fontSize;

		firstLbl.SetMargin( pad, (wide ? -5 : 0), pad, 0 );
		firstLbl.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( firstLbl, 2, 0 );

		// Last
		lastLbl.FontSize = fontSize;

		lastLbl.SetMargin( pad, (ios ? 0 : (wide ? -7 : -2)), pad, 0 );
		lastLbl.SetPosition( LayoutOptions.Start, LayoutOptions.Start );

		layout.Add( lastLbl, 2, 1 );

		// Number
		numberBtn.NumberSize = (fontSize - 2);

		numberBtn.SetSize( badgeSize, true );
		numberBtn.SetMargin( pad, (wide ? -10 : -2), 0, 0 );
		numberBtn.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( numberBtn, 2, 2 );

		// Position
		positionLbl.FontSize = (fontSize - 3);

		positionLbl.SetMargin( (pad + badgeSize + pad - 5), (wide ? -12 : -4), 0, 0 );
		positionLbl.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( positionLbl, 2, 2 );

		// Swap
		swapBtn.FontSize = (dim * (wide ? 0.012 : 0.014));

		swapBtn.SetSize( swapWd, swapHt );
		swapBtn.SetMargin( 0, 0, (pad - 2), (pad - 2) );
		swapBtn.SetPosition( LayoutOptions.End, LayoutOptions.End );

		layout.Add( swapBtn, 2, 3 );
	}

	// Widescreen Landscape (16:10)
	protected override void WideLandscape()
	{
		Landscape();
	}

	// Portrait (4:3)
	protected override void Portrait()
	{
		Landscape();

		double dim = DXDevice.GetScreenDim();
		
		double swapWd = (dim * 0.072);
		double swapHt = (dim * 0.020);

		// Sizing constants
		double badgeSize = (dim * 0.026);
		
		// Number
		numberBtn.SetSize( badgeSize, true );

		// Swap
		swapBtn.SetSize( swapWd, swapHt );
		swapBtn.SetMargin( 0, 0, 7, 5 );
	}

	// Widescreen Portrait (10:16)
	protected override void WidePortrait()
	{
		Portrait();

		double pad = 8;
		double badgeSize = 34;

		numberBtn.SetMargin( pad, -6, 0, 0 );
		positionLbl.SetMargin( (pad + badgeSize + pad - 5), -9, 0, 0 );
	}

	// Mobile Landscape
	protected override void MobileLandscape()
	{
		bool ios = DXDevice.IsIOS;
		
		// Sizing constants
		double ht = RecordEngine.ScoreboardHt;
		double pad = (ht * 0.020);
		
		double avatarSize = (ht * 0.32);
		double fontSize = (ht * 0.12);
		double badgeSize = (ht * 0.21);

		const double swapHt = 23;

		// 6 rows
		layout.AddStarRow( 16 );        // 0: zone
		layout.AddStarRow( 24 );        // 1: avatar, number
		layout.AddStarRow( 13 );        // 2: avatar, pos
		layout.AddStarRow( 19 );        // 3: first/last
		layout.AddFixedRow( 1 );        // 4: divider
		layout.AddStarRow( 28 );        // 5: swap

		// 2 columns
		layout.AddStarColumn( 45 );     // 0: avatar
		layout.AddStarColumn( 55 );     // 1: number, pos

		// Libero 1/2
		liberoBtn.FontSize = (ht * 0.11);
		liberoBtn.TextAdjustY = ios ? 0 : -7;
		
		liberoBtn.SetSize( 75, 16 );

		layout.Fill( ZoneColor, 0, 0, 2, 1 );
		layout.Add( liberoBtn, 0, 0, 2, 1 );

		// Avatar
		avatarImg.SetMargin( pad, pad, 0, 0 );
		avatarImg.SetPosition( LayoutOptions.Start, LayoutOptions.Center );
		avatarImg.SetSize( avatarSize, avatarSize, 0.80 );

		layout.Add( avatarImg, 0, 1, 1, 2 );

		// First/Last
		firstLbl.Text = Player?.FullName!;
		firstLbl.FontSize = fontSize;

		firstLbl.SetMargin( pad, (ios ? pad : 0), pad, pad );
		firstLbl.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( firstLbl, 0, 3, 2, 1 );

		// Number
		numberBtn.NumberSize = fontSize;
		numberBtn.Size = badgeSize;
		
		numberBtn.SetMargin( pad, pad, 0, 0 );
		numberBtn.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( numberBtn, 1, 1 );

		// Position
		positionLbl.FontSize = (fontSize - 4);

		positionLbl.SetMargin( pad, 0, 0, 0 );
		positionLbl.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( positionLbl, 1, 2 );

		// Divider
		layout.Fill( DividerColor, 0, 4, 2, 1 );

		// Swap
		swapBtn.FontSize = 12;

		swapBtn.SetSize( 70, swapHt );
		swapBtn.SetMargin( 0, 0, 0, 0 );
		swapBtn.SetPosition( LayoutOptions.Center, LayoutOptions.Center );

		layout.Fill( DXColors.Light4, 0, 5, 2, 1 );
		layout.Add( swapBtn, 0, 5, 2, 1 );
	}

	// Mobile Portrait
	protected override void MobilePortrait()
	{
		bool ios = DXDevice.IsIOS;
		
		Thickness safeArea = DXDevice.SafeArea();

		// Sizing constants
		double wd = (DXDevice.GetScreenWd() - safeArea.HorizontalThickness);
		double pad = (wd * 0.0067);

		double avatarSize = (RecordEngine.ScoreboardHt * 0.15);
		double fontSize = (wd * 0.038);
		double badgeSize = (wd * 0.065);

		const double swapHt = 28;

		// 6 rows
		layout.AddStarRow( 16 );		// 0: zone
		layout.AddStarRow( 22 );        // 1: avatar, number
		layout.AddStarRow( 14 );        // 2: avatar, pos
		layout.AddStarRow( 18 );		// 3: first/last
		layout.AddFixedRow( 1 );		// 4: divider
		layout.AddStarRow( 29 );		// 5: swap

		// 2 columns
		layout.AddStarColumn( 50 );		// 0: avatar
		layout.AddStarColumn( 50 );		// 1: number, pos

		// Libero 1/2
		liberoBtn.FontSize = (wd * 0.040);
		liberoBtn.TextAdjustY = ios ? 0 : -8;
		
		liberoBtn.SetSize( 75, 16 );

		layout.Fill( ZoneColor, 0, 0, 2, 1 );
		layout.Add( liberoBtn, 0, 0, 2, 1 );

		// Avatar
		avatarImg.SetMargin( 0, 0, 0, 0 );
		avatarImg.SetPosition( LayoutOptions.Start, LayoutOptions.Center );
		avatarImg.SetSize( avatarSize, avatarSize, 0.80 );

		layout.Add( avatarImg, 0, 1, 1, 2 );

		// First
		firstLbl.Text = Player?.FullName!;
		firstLbl.FontSize = fontSize;

		firstLbl.SetMargin( pad, (ios ? pad : 0), pad, pad );
		firstLbl.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( firstLbl, 0, 3, 2, 1 );

		// Number
		numberBtn.NumberSize = (fontSize - 1);
		numberBtn.Size = badgeSize;
		
		numberBtn.SetMargin( pad, pad, 0, 0 );
		numberBtn.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( numberBtn, 1, 1 );

		// Position
		positionLbl.FontSize = (fontSize - 3);

		positionLbl.SetMargin( pad, 0, 0, 0 );
		positionLbl.SetPosition( LayoutOptions.Start, LayoutOptions.Center );

		layout.Add( positionLbl, 1, 2 );

		// Divider
		layout.Fill( DividerColor, 0, 4, 2, 1 );

		// Swap
		swapBtn.FontSize = 12;

		swapBtn.SetSize( 80, swapHt );
		swapBtn.SetMargin( 0, 0, 0, 0 );
		swapBtn.SetPosition( LayoutOptions.Center, LayoutOptions.Center );

		layout.Fill( DXColors.Light4, 0, 5, 2, 1 );
		layout.Add( swapBtn, 0, 5, 2, 1 );
	}
}

//
