/* 
 * 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.Container;

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

using DXLib.Utils;

namespace iStatVball3;

/*
 * Represents an individual cell within an action menu grid. The cell displays a lineup entry using the name, number,
 * or photo of the corresponding player. Alternatively, the cell can display error text.
 */
public class ActionCell : DXContent
{
	/* Constants */

	// Display modes
	public const string FirstMode = "first";
	public const string LastMode = "last";
	public const string NicknameMode = "nickname";
	public const string NumberMode = "number";
	public const string PhotoMode = "photo";
	public const string ErrorMode = "error";

	// Colors
	private static readonly Color TextColor = DXColors.Light4;
	private static readonly Color ErrorColor = DXColors.Negative;
	private static readonly Color LiberoColor = DXColors.Positive;
	private static readonly Color SetterColor = Colors.Yellow;

	private static readonly Color DisabledColor = DXColors.Light1;
	private static readonly Color HighlightColor = DXColors.Light4;

	private static readonly Color FrontRowColor = DXColors.Action;
	private static readonly Color BackRowColor = Color.FromArgb( "#176CAF" );

	// Pseudo-constants
	private static double BorderSize;
	private static string AnonymousStr;

	/* Events */
	public Action Changed { get; set; }
	public Action Selected { get; set; }

	public Action<string,bool> ErrorSelected { get; set; }

	/* Properties */

	// Underlying cell data
	public LineupEntry Entry { get; private set; }
	public Player Player => Entry?.Player;

	public bool IsAnonymous => (Player == null);
	public bool IsLibero => (!IsAnonymous && Entry.IsLibero);
	public bool IsSetter => (!IsAnonymous && Entry.IsSetter);

	// Court zone (1-6) represented by this cell
	public int Zone { get; private set; }

	// Name/number/photo
	public string Mode { get; set; }

	// Is error mode?
	private bool IsError => ((Mode == ErrorMode) || errorPlayer);

	// Cell sizing
	public ActionMenu.MenuSize Size { get; set; }

	private bool IsLarge => (Size == ActionMenu.MenuSize.Large);
	private bool IsMedium => (Size == ActionMenu.MenuSize.Medium);

	// Part of multi-select menu?
	public bool IsMultiSelect { get; set; }

	// Eligible for player errors? (long press events)
	public bool HasPlayerError { get; set; }

	// Mode specific dimensions
	public double CellWd { get; private set; }
	public double CellHt { get; private set; }

	// Visually disabled? (previous contacting player)
	public bool IsDisabled { get => disabled; set => SetDisabled( value ); }

	// Highlighted outline?
	public bool IsHighlighted { get => highlighted; set => SetHighlighted( value ); }

	// Currently selected by user?
	public bool IsSelected { get => selected; set => Select( value ); }
	
	/* Fields */

	// Mode specific controls (only one active)
	private DXTextBadge name;
	private DXTextBadge number;
	private DXImageArea photo;
	private DXTextBadge error;

	// Error handling
	private string errorKey;

	private bool errorTeam;
	private bool errorPlayer;

	// State control
	private bool disabled;
	private bool highlighted;
	private bool selected;
	
	private bool isZoned;

	/* Methods */
	public ActionCell()
	{
		// Pseudo-constants
		IsClippedToBounds = true;
		Padding = BorderSize;
	}

	/* Initialization */

	// Post-construction initialization
	public void Init()
	{
		BorderSize = IsLarge ? 4 : IsMedium ? 4 : 3;

		switch ( Mode )
		{
			// First/last name use text label
			case FirstMode:
			case LastMode:
			case NicknameMode:
			{
				InitName();
				break;
			}
			// Jersey number uses numeric label
			case NumberMode:
			{
				InitNumber();
				break;
			}
			// Player photo
			case PhotoMode:
			{
				InitPhoto();
				break;
			}
			// Error
			case ErrorMode:
			{
				InitError();
				break;
			}
		}

		// May support player error mode
		if ( HasPlayerError )
		{
			CreateError();

			error.ResourceUpper = "error.player";
		}

		SetContent();
	}

	// Initializes first/last name based cell
	private void InitName()
	{
		double dim = DXDevice.GetScreenDim();
		bool landscape = DXDevice.IsLandscape();

		AnonymousStr = DXString.Get( "action.anon" );

		// Text badge
		name = new DXTextBadge
		{
			Font = DXFonts.RobotoCondensedBold,
			FontSize = IsLarge ? (dim * 0.023) : IsMedium ? 22 : (landscape ? 20 : 15),

			Padding = new Thickness( (IsLarge ? (dim * 0.005) : IsMedium ? 4 : 2), 3 )
		};

		// Square shape
		CellWd = IsLarge ? (dim * 0.117) : IsMedium ? 108 : (landscape ? 95 : 72);
		CellHt = IsLarge ? (dim * 0.059) : IsMedium ? 53 : 45;
	}

	// Initializes jersey number based cell
	private void InitNumber()
	{
		bool ios = DXDevice.IsIOS;
		double dim = DXDevice.GetScreenDim();

		AnonymousStr = DXString.Get( "action.anon.num" );

		// Numeric badge
		number = new DXTextBadge
		{
			Font = DXFonts.OswaldBold,
			FontSize = IsLarge ? 44 : IsMedium ? 42 : 40,

			Padding = new Thickness( 5, -5 )
		};

		number.Adjust( 0, (ios ? 0 : -5) );

		double size = IsLarge ? (dim * 0.078) : IsMedium ? 75 : 70;

		// Square shape
		CellWd = size;
		CellHt = size;
	}

	// Initializes player photo based cell
	private void InitPhoto()
	{
		double dim = DXDevice.GetScreenDim();

		// Image area
		photo = new DXImageArea
		{
			DefaultIcon = "player"
		};

		double size = IsLarge ? (dim * 0.078) : IsMedium ? 75 : 70;

		// Square shape
		CellWd = size;
		CellHt = size;
	}

	// Initializes error message cell
	private void InitError()
	{
		double dim = DXDevice.GetScreenDim();

		BackgroundColor = ErrorColor;

		// Underlying control
		CreateError();

		// Rectangular shape
		CellWd = IsLarge ? (dim * 0.117) : IsMedium ? 108 : (DXDevice.IsLandscape() ? 95 : 72);
		CellHt = IsLarge ? (dim * 0.059) : IsMedium ? 53 : 45;
	}

	// Creates underlying control for error and player error modes
	private void CreateError()
	{
		double dim = DXDevice.GetScreenDim();

		error = new DXTextBadge
		{
			Color = ErrorColor,
			TextColor = TextColor,

			Font = DXFonts.RobotoCondensedBold,
			FontSize = IsLarge ? (dim * 0.023) : IsMedium ? 22 : 20
		};
	}

	/* Setters */

	// Sets lineup entry based on specified zone
	public void SetEntry( List<LineupEntry> entries, int zone )
	{
		Zone = zone;

		SetEntry( entries?[ zone - 1 ], true );
	}

	// Sets lineup entry for this cell
	public void SetEntry( LineupEntry entry, bool zoned = false )
	{
		isZoned = zoned;

		Entry = entry;

		SetDisabled( false );

		// May be anonymous
		bool anon = IsAnonymous;

		// Optional highlighted border
		highlighted = (anon || entry.IsDefault);

		// Entry data is mode specific
		switch ( Mode )
		{
			// First name
			case FirstMode:
			{
				name.Text = anon ? AnonymousStr : (Player.DuplicateFirst ? Player.GetAbbrevLast( 6 ) : Player.FirstName);
				break;
			}
			// Last name
			case LastMode:
			{
				name.Text = anon ? AnonymousStr : (Player.DuplicateLast ? Player.GetAbbrevFirst( 6 ) : Player.LastName);
				break;
			}
            // Nickname
            case NicknameMode:
            {
				name.Text = anon ? AnonymousStr : (string.IsNullOrEmpty( Player.Nickname ) ? Player.LastName : Player.Nickname);
                break;
            }
            // Jersey number
            case NumberMode:
			{
				number.Text = anon ? AnonymousStr : entry.Number;
				break;
			}
			// Player photo
			case PhotoMode:
			{
				photo.SetImage( anon ? null : entry.Player.ImageUrl );
				photo.SetSize( CellWd, CellHt, 1.0 );
				break;
			}
		}

		// Update state
		errorPlayer = false;
		Select( false );

		SetContent();
	}

	// Sets error text (only applicable for error mode)
	public void SetError( string key, string text, bool team )
	{
		errorKey = key;
		errorTeam = team;

		error.TextUpper = text;
	}

	// Visually disables this cell via opacity change
	private void SetDisabled( bool value )
	{
		disabled = value;

		Opacity = disabled ? 0.3 : 1.0;

		UpdateState( selected );
	}

	// Visually highlights this cell with outline color
	private void SetHighlighted( bool value )
	{
		highlighted = value;

		UpdateState( selected );
	}

	// Updates cell content based on display mode
	private void SetContent()
	{
		Content = Mode switch
		{
			FirstMode or 
			LastMode or 
			NicknameMode => name,
			
			NumberMode => number,
			PhotoMode => photo,
			ErrorMode => error,
			
			_ => Content
		};
	}

	/* Select */

	// Visually (de)selects this cell
	private void Select( bool select )
	{
		selected = select;

		UpdateState( selected );
	}

	// Sets cell foreground/background colors
	private void UpdateState( bool inverse )
	{
		// Error mode
		if ( IsError )
		{
			Color light = TextColor;
			Color dark = ErrorColor;

			BackgroundColor = inverse ? light : dark;

			// Normal or inverse colors
			error.Color = inverse ? light : dark;
			error.TextColor = inverse ? dark : light;

			Content = error;
		}
		// Name/number/photo
		else
		{
			Color light = (IsSetter && !inverse) ? SetterColor : TextColor;
			Color dark = IsDisabled ? DisabledColor : (IsLibero ? LiberoColor : ColorForZone());

			Color color = inverse ? light : dark;
			Color textColor = inverse ? dark : light;

			// Normal or inverse colors
			BackgroundColor = IsHighlighted ? HighlightColor : color;

			switch ( Mode )
			{
				// Name
				case FirstMode:
				case LastMode:
				case NicknameMode:
				{
					name.Color = color;
					name.TextColor = textColor;
					break;
				}
				// Jersey number
				case NumberMode:
				{
					number.Color = color;
					number.TextColor = textColor;
					break;
				}
				// Photo
				case PhotoMode:
				{
					photo.Color = color;
					photo.IconColor = textColor;
					break;
				}
			}
		}
	}

	// Returns fill color based on front/back row
	private Color ColorForZone()
	{
		return Zone switch
		{
			// Back row (5-6-1)
			5 or 6 or 1 => isZoned ? BackRowColor : FrontRowColor,
			
			// Front row (4-3-2)
			_ => FrontRowColor
		};
	}

	/* Event Handling */

	// Determines if specified point is within this cell
	public bool Contains( Point origin, Point pt )
	{
		Rect bounds = Bounds.Offset( origin );

		return bounds.Contains( pt );
	}

	// Visually selects this cell on touch down (multiselect toggles)
	public void OnDown()
	{
		if ( !disabled )
		{
			Select( !IsMultiSelect || !selected );
		}
	}

	// Called following tap (touch up)
	public void OnUp()
	{
		if ( !disabled )
		{
			// Briefly show selection, then callback listener
			DXTimer.Delay( 250, HandleUp );
		}
	}

	// User long pressed on player cell, change colors, wait for tap
	public void OnLongPress()
	{
		if ( HasPlayerError )
		{
			errorPlayer = true;
			UpdateState( false );
		}
	}

	// Calls back registered listener after touch up delay
	private void HandleUp()
	{
		// Multiselect only notifies parent
		if ( IsMultiSelect )
		{
			Changed?.Invoke();
		}
		// Otherwise callback listener
		else
		{
			if ( IsError )
			{
				ErrorSelected.Invoke( errorKey, errorTeam );

				// Reset state
				errorPlayer = false;
				Select( false );
			}
			else
			{
				Selected.Invoke();
			}
		}
	}
}

//
