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

using DXLib.UI.Alert;
using DXLib.Utils;

namespace iStatVball3;

/*
 * Represents a list of selectable players and positions in the recording interface. Used to populate the stat recording
 * engine. Note that RecordLineup is NOT the same as a normal Lineup. A record lineup can be either a pre-created or
 * empty anonymous lineup.
 */ 
public class RecordLineup
{
	/* Constants */
	public const int BaseEntries = Lineup.BaseEntries;
	public const int MaxEntries = Lineup.MaxEntries;

	// What is this lineup based on?
	public enum LineupType
	{
		Unknown,
		Anonymous,
		Lineup
	};

	/* Properties */

	// Anonymous or lineup
	public LineupType Type { get; private set; }

	public bool IsAnonymous => (Type == LineupType.Anonymous);
	public bool IsLineup => (Type == LineupType.Lineup);

	// Corresponding team name for UI display
	public string Name { get; set; }
	public string Abbreviation { get; set; }

	// Underlying players/positions
	public List<LineupEntry> Entries { get; private set; }
	public int EntryCount => IsAnonymous ? 1 : Entries.Count;

	// At least one libero defined?
	public bool HasLibero => EntryCount > Lineup.BaseEntries;

	/* Methods */
	public RecordLineup()
	{
		const int count = Lineup.MaxEntries;

		// Allocate containers
		Entries = new List<LineupEntry>( count );

		// Default to anonymous
		Type = LineupType.Anonymous;
	}

	// Creates new lineup deep copied from specified source
	public RecordLineup( RecordLineup src ) : this()
	{
		FromLineup( src );
	}

	// Determines if all entries from two lineups are identical
	public bool Equals( List<LineupEntry> entries )
	{
		// Must both be full lineups
		if ( Entries.Count != entries.Count )
		{
			return false;
		}

		// Every entry must match
		for ( int i = 0; i < Entries.Count; i++ )
		{
			if ( !Entries[i].Equals( entries[i] ) )
			{
				return false;
			}
		}

		return true;
	}

	// Determines if all lineup entries are currently empty
	public bool IsEmpty()
	{
		return Entries.All( entry => entry.IsEmpty );
	}

	// Determines if lineup currently valid (complete players and positions)
	public bool IsValid()
	{
		return ValidatePlayers( Entries );
	}

	// Determines if specified player is currently on court in this lineup
	public bool IsOnCourt( Player player )
	{
		// Only check court zones (1-6)
		for ( int i = 0; i < Lineup.BaseEntries; i++ )
		{
			if ( Equals( Entries[i].Player, player ) )
			{
				return true;
			}
		}

		return false;
	}
	
	// Returns rotated court location of specified player
	public int GetZone( Player player, int rotation )
	{
		LineupEntry entry = GetEntry( player );

		// Player must be on court
		if ( entry != null )
		{
			int zone = entry.Zone;

			// Libero zones not rotated
			return LineupEntry.ZoneIsLibero( zone ) ? zone : Lineup.GetRotationIndex( zone, rotation );
		}

		return Lineup.InvalidZone;
	}

	// Determines if specified player currently in backrow zone (5,6,1)
	public bool IsBackrow( Player player, int rotation )
	{
		int zone = GetZone( player, rotation );

		return LineupEntry.ZoneIsBackRow( zone );
	}

	// Determines if specified sort order is fixed or rotation based
	public static bool IsRotationOrder( string order )
	{
		return (order == "rot") || (order == "smart");
	}

	// Determines if this lineup has setter defined
	public bool HasSetter()
	{
		LineupEntry setter = GetSetter();

		return (setter != null);
	}

	/* Entries */

	// Returns specified libero from lineup, if any
	public LineupEntry GetLibero( int number )
	{
		int zone = (number == 1) ? Lineup.LiberoZone1 : Lineup.LiberoZone2;
		int index = (zone - 1);

		return (EntryCount > index) ? Entries[ index ] : null;
	}

	// Returns first setter found in lineup, if any
	public LineupEntry GetSetter()
	{
		return Entries.FirstOrDefault( entry => entry.IsSetter );
	}

	// Returns current lineup entry matching specified player
	public LineupEntry GetEntry( Player player )
	{
		return Entries.FirstOrDefault( entry => !entry.IsEmpty && entry.Player.Equals( player ) );
	}

	// Returns first X entries in lineup order
	public List<LineupEntry> GetEntries( int count = MaxEntries )
	{
		List<LineupEntry> list = new( count );

		// Deep copy X entries
		for ( int i = 0; i < count; i++ )
		{
			list.Add( new LineupEntry( Entries[i] ) );
		}

		return list;
	}

	// Sets entry at specified zone position
	public void SetEntry( LineupEntry entry, int zone )
	{
		// MUST update zone here to support auto sub/swap
		entry.Zone = zone;

		Entries[ zone - 1 ] = entry;
	}

	// Sorts internal lineup entry list given specified sort order setting
	public List<LineupEntry> SortEntries( string order )
	{
		// Deep copy zones 1-6
		List<LineupEntry> entries = GetEntries( Lineup.BaseEntries );

		// Sort
		return SortEntries( entries, order );
	}

	// Sorts a lineup entry list given specified sort order setting
	public static List<LineupEntry> SortEntries( List<LineupEntry> entries, string order )
	{
		bool empty = LineupEntry.IsListEmpty( entries );

		if ( !empty )
		{
			switch ( order )
			{
				// First, Last
				case "first":
				case "photo":
				{
					return entries.OrderBy( e => e.Player.FirstName ).ThenBy( e => e.Player.LastName ).ToList();
				}
				// Last, First
				case "last":
				{
					return entries.OrderBy( e => e.Player.LastName ).ThenBy( e => e.Player.FirstName ).ToList();
				}
				// Jersey number
				case "number":
				{
					return entries.OrderBy( e => DXUtils.ConvertToInt( e.Player.Number ) ).ToList();
				}
			}
		}

		return entries;
	}

	/* Validate */

	// Validates all entries populated with no duplicates
	public static bool Validate( IList<LineupEntry> entries, bool record = true, Action callback = null )
	{
		string titleStr = record ? "lineup.singular" : "lineup.starter";

		// All cells 1-6 must have player selected
		if ( ValidatePlayers( entries ) )
		{
			// Must have none, both, or only Libero 1 defined
			if ( ValidateLiberos( entries ) )
			{
				// Must also have 6 positions
				if ( ValidatePositions( entries ) )
				{
					Player duplicate = GetDuplicatePlayer( entries );

					// Cannot have duplicates
					if ( duplicate == null )
					{
						return true;
					}

					DXAlert.ShowError( titleStr, "lineup.err.dup", duplicate.FullName, callback );
				}
				else
				{
					DXAlert.ShowError( titleStr, "lineup.err.pos", callback );
				}
			}
			else
			{
				DXAlert.ShowError( titleStr, "lineup.err.lib", callback );
			}
		}
		else
		{
			DXAlert.ShowError( titleStr, "lineup.err.player", callback );
		}

		return false;
	}

	// Validates all zones (1-6) have player selected
	public static bool ValidatePlayers( IList<LineupEntry> entries )
	{
		// Might be entirely empty
		if ( (entries == null) || (entries.Count == 0) )
		{
			return false;
		}

		// Check all zones
		for ( int zone = 1; zone <= Lineup.BaseEntries; zone++ )
		{
			int index = (zone - 1);
			LineupEntry entry = entries[ index ];

			// Missing player
			if ( (entry == null) || entry.IsEmpty )
			{
				return false;
			}
		}

		// Valid
		return true;
	}

	// Validates libero entries
	public static bool ValidateLiberos( IList<LineupEntry> entries )
	{
		bool lib1 = !entries[ Lineup.LiberoZone1 - 1 ].IsEmpty;
		bool lib2 = !entries[ Lineup.LiberoZone2 - 1 ].IsEmpty;

		// Valid if both empty, both defined, or only Libero 1 defined
		return (!lib1 && !lib2) || (lib1 && lib2) || lib1;
	}

	// Validates all zones (1-6) have position selected
	public static bool ValidatePositions( IList<LineupEntry> entries )
	{
		for ( int zone = 1; zone <= Lineup.BaseEntries; zone++ )
		{
			int index = (zone - 1);
			string position = entries[ index ].Position;

			// Missing position
			if ( (position == null) || (position == Lineup.EmptyPosition) )
			{
				return false;
			}
		}

		return true;
	}

	// Searches for duplicate players in specified entry list
	public static Player GetDuplicatePlayer( IList<LineupEntry> entries )
	{
		foreach ( LineupEntry entry in entries )
		{
			// Found duplicate
			if ( CountOccurrences( entry?.Player, entries ) > 1 )
			{
				return entry?.Player;
			}
		}

		// No dups
		return null;
	}

	// Counts number of times specified player exists in entry list
	private static int CountOccurrences( Player player, IList<LineupEntry> entries )
	{
		int count = 0;

		if ( player != null )
		{
			foreach ( LineupEntry entry in entries )
			{
				if ( entry is { Player: not null } )
				{
					// Found
					if ( entry.Player.Equals( player ) )
					{
						count++;
					}
				}
			}
		}

		// Total occurrences
		return count;
	}

	/* Deep Copy */

	// Populates record lineup from a pre-created lineup
	public void FromLineup( Lineup lineup )
	{
		Type = LineupType.Lineup;

		// Blank lineup creates empty entries
		if ( lineup == null )
		{
			for ( int zone = 1; zone <= Lineup.MaxEntries; zone++ )
			{
				Entries.Add( new LineupEntry
				{
					Zone = zone
				});
			}
		}
		// Add each lineup entry
		else
		{
			foreach ( LineupEntry entry in lineup.Entries )
			{
				// Protect against v2 import bug
				if ( Lineup.IsLiberoZone( entry.Zone ) )
				{
					entry.Position = Lineup.LiberoKey;
				}

				Entries.Add( entry );
			}
		}
	}

	// Populates record lineup from existing record lineup
	public void FromLineup( RecordLineup lineup )
	{
		Type = lineup.Type;

		if ( IsLineup )
		{
			FromList( lineup.Entries );
		}
	}

	// Populates record lineup from full list of in-memory entries
	public static RecordLineup Create( IList<LineupEntry> entries )
	{
		RecordLineup lineup = new()
		{
			Type = LineupType.Lineup
		};

		lineup.FromList( entries, entries.Count );

		return lineup;
	}

	// Populates record lineup from in-memory entries
	public void FromList( IList<LineupEntry> entries )
	{
		FromList( entries, entries.Count );
	}

	// Populates record lineup from subset of in-memory entries
	public void FromList( IList<LineupEntry> entries, int count )
	{
		Entries.Clear();

		for ( int i = 0; i < count; i++ )
		{
			Entries.Add( new LineupEntry( entries[i] ) );
		}
	}
}

//
