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

using DXLib.Data;
using DXLib.Utils;

namespace iStatVball3;

/*
 * Represents a vertical list of flyout buttons. Includes functionality for drawing all buttons in the bar. 
 */
public class FlyoutBar : DXGridLayout
{
	/* Constants */
	private const int TapDelay = 500;
	
	// Available bar types
	public enum FlyoutType
	{
		Rating,
		Modifier,
		Selector,

		Fault,
		FaultExt
	};
	
	// Colors
	private static readonly Color BorderColor = DXColors.Dark1;
	private static readonly Color TitleColor = DXColors.Dark2;
	private static readonly Color TextColor = DXColors.Light4;
	private static readonly Color ErrorColor = DXColors.Negative;
	
	// Layout3
	public static double BorderSize { get; private set; }
	public static double TitleHt { get; private set; }
	
	// Bars can have custom size
	public static double DefaultBarWd { get; private set; }
	
	/* Events */
	
	// Item selected from standalone flyout
	public Action<FlyoutBar,int> Selected { get; set; }
	
	// Item selected from fault flyout
	public Action<RecordData> FaultSelected { get; set; }
	
	/* Properties */
	
	// Rating/modifier/selector
	public FlyoutType Type { get; set; }
	
	// Individual button size
	public double ButtonWd { get; private set; }
	public double ButtonHt { get; private set; }
	
	// Overall bar size
	public double BarWd { get; private set; }
	public double BarHt { get; private set; }
	
	// Maximum number buttons on bar
	public int MaxCount { get; set; }
	
	// Current number active buttons on bar
	public int ButtonCount { get; private set; }
	
	// Does bar have title header?
	public bool HasTitle { get; set; }
	
	// Button color
	public Color Color { get; set; }
	
	// Used when swapping between two bars
	public Point AnchorPt { get; set; }
	
	// Special fault flyout?
	private bool IsFaultFlyout => (FaultSelected != null);

	/* Fields */
	private DXAbsoluteGestures barLayout;
	private DXTextBadge titleBadge;
	
	// Used for Material elevation
	private DXBorder border;
	
	// Underlying controls
	private List<FlyoutButton> buttons;
	
	// State
	private bool faultUp;
	
	// Default item
	private string defaultKey;
	
	// Parent
	private ActionMenuHandler menuHandler;
	
	/* Methods */
	public FlyoutBar()
	{
		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;

		IgnoreSafeArea = true;
		IsClippedToBounds = true;
	}
	
	// Post-construction initialization
	public void Init( DXAbsoluteGestures layout, ActionMenuHandler handler, ActionMenu.MenuSize size )
	{
		barLayout = layout;
		menuHandler = handler;
	
		double dim = DXDevice.GetScreenDim();
	
		bool large = (size == ActionMenu.MenuSize.Large);
		bool medium = (size == ActionMenu.MenuSize.Medium);
	
		// Set pseudo-constants
		double defaultButtonWd = large ? (dim * 0.063) : medium ? 57 : 50;
		double adjust = large ? (dim * 0.014) : medium ? 16 : 18;
	
		ButtonWd = HasTitle ? (defaultButtonWd - adjust) : defaultButtonWd;
		ButtonHt = large ? (dim * 0.035) : 36;
	
		BorderSize = large ? (dim * 0.002) : 2;
	
		DefaultBarWd = (BorderSize + defaultButtonWd + BorderSize);
		BarWd = (BorderSize + ButtonWd + BorderSize);
	
		// Layout
		BackgroundColor = BorderColor;
	
		Margin = 0;
		Padding = BorderSize;
		RowSpacing = BorderSize;
	
		// Optionally add title
		if ( HasTitle )
		{
			titleBadge = new DXTextBadge
			{
				Color = TitleColor,
				TextColor = TextColor,
	
				FontSize = large ? (dim * 0.014) : medium ? 13 : 10,
				Font = DXFonts.RobotoCondensedBold
			};
	
			TitleHt = large ? (dim * 0.020) : 20;
	
			AddFixedRow( TitleHt );
			Add( titleBadge, 0, 0 );
		}
	
		// Allocate memory
		buttons = [];
	
		// Add each button on bar
		for ( int i = 0; i < MaxCount; i++ )
		{
			FlyoutButton button = new()
			{
				Color = Color,
				TextSize = large ? (dim * 0.014) : medium ? 13 : 12,
			};
	
			button.Init();
	
			int index = HasTitle ? (buttons.Count + 1) : buttons.Count;
	
			AddFixedRow( ButtonHt );
			Add( button, 0, index );
	
			// Must be last
			buttons.Add( button );
		}
	
		UpdateHt();
	
		// Simulate Material elevation
		border = new DXBorder
		{
			CornerRadius = (float)(large ? (dim * 0.004) : 4),
			Elevation = 4,
			
			Content = this
		};
		
		border.Init();
		
		// Initially hidden
		Hide();
	}
	
	// Adds this flyout to parent layout
	public void Add()
	{
		barLayout.Add( border );
	}
	
	// Inserts this flyout at specified z-order index in parent layout
	public void Insert( int index )
	{
		barLayout.Insert( (index + 1), border );
	}
	
	// Removes this flyout from parent layout
	public void Remove()
	{
		barLayout.Remove( border );
	}
	
	// Dynamically calculates bar size based on number of items
	private void UpdateHt()
	{
		BarHt = (BorderSize + (HasTitle ? (TitleHt + BorderSize) : 0) + (ButtonHt * ButtonCount) + (BorderSize * (ButtonCount - 1)) + BorderSize);
	}
	
	// Sets optional title text
	public void SetTitle( string title )
	{
		titleBadge.TextUpper = title;
	}
	
	// Clears active button count
	public void ClearItems()
	{
		DeselectAll();
	
		ButtonCount = 0;
	}
	
	// Populates buttons on bar with specified items
	public void SetItems( List<DXItem> items )
	{
		ButtonCount = items.Count;
	
		// May have fewer than max items
		for ( int i = 0; i < ButtonCount; i++ )
		{
			FlyoutButton btn = buttons[i];
			DXItem item = items[i];
	
			btn.Index = i;
	
			btn.Key = item.Key;
			btn.Text = item.Value;
		}
	
		UpdateHt();
	}
	
	// Sets default bar selection
	public void SetDefault( string key )
	{
		// Find default in bar
		if ( ButtonCount > 0 )
		{
			foreach ( FlyoutButton btn in buttons )
			{
				bool selected = (key != null) && (btn.Key == key);
				btn.Select( selected );
			}
		}
	
		// Save default, even if bar not shown
		defaultKey = key;
	}
	
	// Puts button at specified index into special error mode
	public void SetError( int index )
	{
		// Must reset other buttons first
		for ( int i = 0; i < index; i++ )
		{
			FlyoutButton btn = buttons[i];
	
			if ( !btn.IsSelected )
			{
				btn.Color = Color;
			}
		}
	
		FlyoutButton button = buttons[ index ];
	
		// Set error mode
		button.Color = ErrorColor;
		button.Select( false );
	}
	
	/* Draw */
	
	// Draws flyout bar at specified location (not yet visible)
	public void Draw( double x, double y, bool middle = true )
	{
		double barHt = HasTitle ? (BarHt + TitleHt + BorderSize) : BarHt;
		double drawY = (y - (middle ? (barHt / 2) : 0));
	
		const double edge = ActionOverlay.Edge;
		Thickness safeArea = DXDevice.ExtraSafeArea();
	
		double screenHt = DXDevice.GetScreenHt() - ((DXDevice.IsAndroid && DXDevice.IsMobileLandscape()) ? DXDevice.GetSafeTop() : 0);
	
		// Restrict to screen top/bottom
		double minY = (safeArea.Top + edge);
		double maxY = (screenHt - safeArea.Bottom - edge - BarHt);
	
		drawY = Math.Max( drawY, minY );
		drawY = Math.Min( drawY, maxY );
	
		// Restrict to screen left/right
		double maxX = (DXDevice.GetScreenWd() - safeArea.Right - edge - BarWd);
		double drawX = Math.Min( x, maxX );
	
		// Draw
		barLayout.SetBounds( border, drawX, drawY, BarWd, BarHt );
	}
	
	/* Show/Hide */
	
	// Renders flyout (but not yet shown)
	public void PreShow()
	{
		border.Opacity = ActionMenu.PreOpacity;
		border.IsVisible = true;
	}
	
	// Makes flyout fully visible
	public void Show()
	{
		border.Opacity = 1.0;
		border.IsVisible = true;
	}
	
	// Hides flyout bar
	public void Hide()
	{
		// Special handling for fault flyout
		if ( IsFaultFlyout )
		{
			faultUp = false;
			DeselectAll();
		}
	
		border.IsVisible = false;
		border.Opacity = 0.0;
	
		ClearItems();
	}
	
	/* Select */
	
	// Returns key of currently selected button, or default
	public string SelectedKey()
	{
		int index = SelectedIndex();
	
		return (index >= 0) ? buttons[ index ].Key : defaultKey;
	}
	
	// Returns currently selected button index
	public int SelectedIndex()
	{
		// Find current selection
		for ( int index = 0; index < buttons.Count; index++ )
		{
			if ( buttons[ index ].IsSelected )
			{
				return index;
			}
		}
	
		// Nothing selected
		return -1;
	}
	
	// Selects button at specified index
	public void Select( int index )
	{
		if ( index != SelectedIndex() )
		{
			if ( (index >= 0) && (index < buttons.Count) )
			{
				// Deselect all others
				DeselectAll();
	
				// Select new button
				buttons[ index ].Select( true );
	
				// Normal flyout
				if ( !IsFaultFlyout || (Type == FlyoutType.FaultExt) )
				{
					Selected?.Invoke( this, index );
				}
			}
		}
	}
	
	// Deselects all buttons on this bar
	private void DeselectAll()
	{
		foreach ( FlyoutButton button in buttons )
		{
			button.Select( false );
		}
	}
	
	/* Event Callbacks */
	
	// Determines if touch location is anywhere on this flyout bar
	public bool Contains( Point pt )
	{
		return border.IsVisible && border.Bounds.Contains( pt );
	}
	
	// Selects tapped button (if any)
	public void OnDown( Point pt )
	{
		// Only applicable for normal flyouts
		if ( !IsFaultFlyout )
		{
			Point origin = border.Bounds.Location;
	
			// Find matching button
			foreach ( FlyoutButton button in buttons )
			{
				if ( button.Contains( origin, pt ) )
				{
					Select( button.Index );
					return;
				}
			}
		}
	}
	
	// Handles taps on special fault flyout (or auto-select)
	public void OnUp( Point pt )
	{
		// Can auto-select player if only 1 player/flyout
		if ( (menuHandler != null) && menuHandler.CanAutoSelect() )
		{
			menuHandler.HandleSelected( true );
		}
		// Ignore first up event
		else if ( IsFaultFlyout && faultUp )
		{
			Point origin = border.Bounds.Location;
	
			// Find matching button
			foreach ( FlyoutButton button in buttons )
			{
				if ( button.Contains( origin, pt ) )
				{
					Select( button.Index );
					break;
				}
			}
	
			// Show tap state, then callback listener
			DXTimer.Delay( TapDelay, () =>
			{
				FaultSelected.Invoke( new RecordData
				{
					X = Stats.FaultX,		// Faults start/end same point
					Y = Stats.FaultY,
	
					Result = Stats.ErrorKey,
					Error = Stats.FaultKey,
					Fault = SelectedKey(),
	
					IsError = true,
					IsTeamError = false
				});
	
				faultUp = false;
			});
		}
	
		faultUp = true;
	}
}

//
