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

using DXLib.UI.Gestures;
using DXLib.Utils;

using Color = Microsoft.Maui.Graphics.Color;
using Point = Microsoft.Maui.Graphics.Point;

namespace iStatVball3;

/*
 * Manages both the court and libero overlay areas used to select players and positions for lineup changes, substitutions,
 * and libero swaps.
 */
public class TeamOverlay
{
	/* Constants */

	// Court functionality modes
	public enum LineupMode
	{
		Closed,
		Blank,
		Replace,
		Sub,
		Swap,
		Preview
	};

	/* Events */
	public Action<int> Substituted { get; set; }
	public Action<int> Swapped { get; set; }

	/* Properties */
	public bool IsSideA => parent.IsSideA;

	// Libero currently swapped in or out?
	public bool IsLiberoIn => !IsLiberoOut;
	public bool IsLiberoOut => libero.IsLiberoOut();
	public bool IsLiberoEmpty => libero.IsEmpty;

	public bool HasTwoLiberos => (GetLiberoCount() == 2);

	// Preview, replace, sub, swap
	public LineupMode Mode { get; set; }

	public bool IsClosed => (Mode == LineupMode.Closed);
	public bool IsBlank => (Mode == LineupMode.Blank);
	public bool IsReplace => (Mode == LineupMode.Replace);
	public bool IsSub => (Mode == LineupMode.Sub);
	public bool IsSwap => (Mode == LineupMode.Swap);
	public bool IsPreview => (Mode == LineupMode.Preview);

	// Overlay currently visible?
	public bool IsOpen => !IsClosed;
	public bool IsModalOpen => (IsOpen && !IsPreview);

	// Subs remaining
	public int Subs => parent.Subs;

	// Custom color
	public Color Color => parent.Color;

	// Special embedded sync mode?
	public bool IsSyncMode => parent.IsSyncMode;

	// Landscape forced in Sync mode
	public bool IsLandscape => (IsSyncMode || DXDevice.IsLandscape());

	/* Fields */

	// External refs
	private readonly TeamBar parent;
	private IList<Player> overlayRoster;

	// Master layout
	private readonly DXAbsoluteGestures layout;

	// Underlying overlays
	private readonly TeamCourt court;
	private readonly TeamLibero libero;

	// Reusable menus
	private LineupMenu lineupMenu;
	private LineupMenu subMenu;

	/* Methods */
	public TeamOverlay( TeamBar parent, DXAbsoluteGestures layout )
	{
		this.parent = parent;
		this.layout = layout;

		// Created but not populated
		court = new TeamCourt( this, layout );
		libero = new TeamLibero( this, layout );
	}

	// Post-construction initialization, all static layout
	public void Init( IList<Player> roster )
	{
		overlayRoster = roster;

		// MUST reset here
		lineupMenu = null;
		subMenu = null;

		// Init all cells
		court.Init();
		libero.Init();
	}

	// Visually selects specified cell
	public void Select( int zone )
	{
		court.Select( zone );
	}

	// Find zone number (1-6) of libero on court, 0 if NA
	public int FindLibero()
	{
		return court.FindLibero();
	}

	// Swaps player in specified zone to given libero area
	public void SwapLibero( int courtZone, int liberoNum )
	{
		LineupEntry outEntry = court.GetEntry( courtZone );
		LineupEntry inEntry = libero.GetEntry( liberoNum );

		libero.SetEntry( outEntry, liberoNum );
		court.SetEntry( inEntry, courtZone );
	}

	/* WHP */

	// Returns current server (zone 1)
	public LineupEntry GetServer()
	{
		return court.GetServer();
	}

	// Returns current setter (backrow first)
	public LineupEntry GetSetter()
	{
		return court.GetSetter();
	}

	// Returns specified libero
	public LineupEntry GetLibero( int number )
	{
		return libero.GetEntry( number );
	}

	// Returns number of liberos currently in lineup
	public int GetLiberoCount()
	{
		return libero.GetCount();
	}

	// Returns list of all players currently on court (lineup order)
	public List<LineupEntry> GetTeam()
	{
		return court.GetTeam();
	}

	// Returns list of players currently in frontrow (zones 2-4)
	public List<LineupEntry> GetFrontrow()
	{
		return court.GetFrontrow();
	}

	/* Get/Set */

	// Returns list of current players in both overlays
	public List<Player> GetPlayers()
	{
		// Court
		List<Player> players = court.GetPlayers();

		// Liberos
		players.AddRange( libero.GetPlayers() );

		return players;
	}

	// Returns list of lineup entries from both overlays (rotation order)
	public List<LineupEntry> GetEntries( int rotation )
	{
		// Court
		List<LineupEntry> entries = court.GetEntries( rotation );

		// Liberos
		entries.AddRange( libero.GetEntries() );

		return entries;
	}

	// Updates lineup entry for specified court zone
	public void SetEntry( LineupEntry entry, int zone )
	{
		court.SetEntry( entry, zone );
	}

	// Updates player list for both court and libero overlays
	public void SetEntries( List<LineupEntry> entries, int rotation )
	{
		court.SetEntries( entries, rotation );
		libero.SetEntries( entries );
	}

	/* Show/Hide */

	// Displays both court and libero overlays
	public void Show( Rect bounds, double teambarSize )
	{
		// MUST set preview mode before Update
		court.SetPreview( IsPreview );

		UpdateLayout();

		bool sideA = parent.IsSideA;

		// Zones 1-6 always shown
		court.Show( sideA, bounds, teambarSize );

		// Libero zone not needed for sub/swap
		switch ( Mode )
		{
			case LineupMode.Blank:
			case LineupMode.Replace:
			{
				libero.Show( sideA, bounds, teambarSize );
				break;
			}
		}
	}

	// Hides both overlays
	public void Hide()
	{
		Mode = LineupMode.Closed;

		court.Hide();
		libero.Hide();
	}

	// Determines if specified point within team overlay
	public bool Contains( Point pt )
	{
		return court.Bounds.Contains( pt ) || libero.Bounds.Contains( pt );
	}

	/* Menus */

	// Used to access lineup menu
	public LineupMenu GetLineupMenu()
	{
		// Lazily create
		if ( lineupMenu == null )
		{
			lineupMenu = new LineupMenu( overlayRoster.Count, true )
			{
				Color = Color
			};

			lineupMenu.SetPlayers( overlayRoster );
		}

		return lineupMenu;
	}

	// Used to access substitution menu
	public LineupMenu GetSubMenu( int zone )
	{
		// Get all players on court plus libero(s)
		List<Player> activePlayers = GetPlayers();

		int count = (overlayRoster.Count - activePlayers.Count);

		// MUST have available players to sub
		if ( count < 1 )
		{
			return null;
		}

		// Lazily create menu
		subMenu ??= new LineupMenu( count, false )
		{
			Color = Color
		};

		List<Player> subPlayers = new( count );
		List<LineupMenuRow.RowState> subStates = new( count );

		RecordState sm = parent.StateMachine;

		// Sub menu only includes players not already on court
		foreach ( Player player in overlayRoster )
		{
			if ( !activePlayers.Contains( player ) )
			{
				subPlayers.Add( player );

				// Highlight valid/invalid subs
				subStates.Add( sm.GetSubState( zone, player ) );
			}
		}

		subMenu.SetPlayers( subPlayers, subStates );

		return subMenu;
	}

	/* UI */

	// Allows child court to be inserted at proper level in parent layout
	public void AddCourt()
	{
		layout.Add( court );
	}

	// Allows child libero area to be inserted at proper level in parent layout
	public void AddLibero()
	{
		layout.Add( libero );
	}

	/* Layout */

	// Returns RallyFlow specific layout orientation
	public LayoutType GetLayoutType()
	{
		return parent.GetLayoutType();
	}

	// Redraws for RallyFlow specific orientation
	public void UpdateLayout()
	{
		UpdateLayout( GetLayoutType() );
	}

	// Updates all overlay children
	public void UpdateLayout( LayoutType type )
	{
		court.UpdateLayout( type );
		libero.UpdateLayout( type );
	}
}

//
