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

using DXLib.UI.Layout;
using DXLib.UI.Container;
using DXLib.UI.Control.Button;

using DXLib.Log;
using DXLib.Utils;

namespace DXLib.UI.Control;

/*
 * A control supporting numeric entry via a phone-style numeric keypad. The keypad is displayed in a modal popup.
 */ 
public class DXKeypad : DXGridLayout
{
	/* Constants */
	private const double ButtonSize = 45;
	
	// 99,999
	private const int MaxDigits = 5;

	/* Events */
	public Action Completed { get; set; }

	/* Properties */

	// Current numeric value
	public int? Number { get => number; set => UpdateNumber( value ); }
	public string NumberText { get => Number?.ToString(); set => Number = DXUtils.ConvertToInt( value ); }

	// Allowable range
	public int MinValue { get; set; }
	public int MaxValue { get; set; }

	/* Fields */
	private readonly DXLabel numberLbl;

	private readonly DXIconButton backBtn;
	private readonly DXIconButton doneBtn;

	private DXPopup popup;

	private readonly double popupWd;
	private readonly double popupHt;
	
	// State
	private int? number;
	private bool firstEntry = true;

	/* Methods */
	public DXKeypad()
	{
		BackgroundColor = DXColors.Dark2;

		// Layout
		const double spacing = 3;
	
		Padding = spacing;
		Margin = 0;
		
		RowSpacing = spacing;
		ColumnSpacing = spacing;

		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;

		AddFixedColumns( 3, ButtonSize );

		// Display row
		AddFixedRow( ButtonSize );

		numberLbl = new DXLabel
		{
			TextColor = DXColors.Light2,
			Font = DXFonts.Oswald,
			FontSize = 28,

			Horizontal = LayoutOptions.Center,
			Vertical = LayoutOptions.Center
		};

		Add( numberLbl, 0, 0, 3, 1 );

		// Keypad 3 rows (1,2,3 | 4,5,6 | 7,8,9)
		AddFixedRows( 3, ButtonSize );

		for ( int digit = 1; digit <= 9; digit++ )
		{
			DXButton btn = CreateButton( digit );

			int col = (digit - 1) % 3;
			int row = (digit - 1) / 3;

			Add( btn, col, (row + 1) );
		}

		popupWd = (3 * ButtonSize) + (spacing * 4);
		popupHt = (5 * ButtonSize) + (spacing * 6);

		WidthRequest = popupWd;
		HeightRequest = popupHt;
		
		// Keypad last row (back, 0, done)
		AddFixedRow( ButtonSize );

		// Backspace
		backBtn = new DXIconButton
		{
			IsDisabled = true,

			Resource = "backspace",
			Size = 35,
			
			IconColor = DXColors.Light1,
			IconScale = 0.75,

			Horizontal = LayoutOptions.Center,
			Vertical = LayoutOptions.Center,
			
			ButtonTapped = OnBackTapped
		};

		backBtn.Init();
		backBtn.SetSize( ButtonSize );
		
		Add( backBtn, 0, 4 );

		// Zero 
		DXButton zeroBtn = CreateButton( 0 );
		Add( zeroBtn, 1, 4 );

		// Done
		doneBtn = new DXIconButton
		{
			IsDisabled = true,

			Resource = "check_circle",
			Size = 35,
			
			IconColor = DXColors.Positive,
			IconScale = 0.80,
			
			Horizontal = LayoutOptions.Center,
			Vertical = LayoutOptions.Center,
			
			ButtonTapped = OnDoneTapped
		};

		doneBtn.Init();
		doneBtn.SetSize( ButtonSize );
		
		Add( doneBtn, 2, 4 );

		// Init
		Reset();
	}

	// Creates a digit button (0-9)
	private DXButton CreateButton( int digit )
	{
		DXButton button = new DXButton
		{
			Data = digit,
			
			Text = digit.ToString(),
			Type = DXButton.ButtonType.Action,
			
			Font = DXFonts.OswaldBold,
			FontSize = 22,

			Horizontal = LayoutOptions.Center,
			Vertical = LayoutOptions.Center,
			
			ButtonDown = OnDigitDown
		};

		button.SetSize( ButtonSize );
		button.Init();

		return button;
	}

	// Displays keypad in modal popup anchored to specified view
	public void Show( View view, double adjustX = 0, double adjustY = 0 )
	{
		popup = new DXPopup( null, this )
		{
			ViewWidth = popupWd,
			ViewHeight = popupHt,
				
			PopupClosed = OnPopupClosed
		};

		popup.ShowFromView( view, adjustX, adjustY );
	}

	// Clear current keypad entry
	public void Reset()
	{
		Number = null;
	}

	// Updates numeric display with specified value
	private void UpdateNumber( int? value )
	{
		number = value;

		numberLbl.Text = value?.ToString()!;
	}

	/* Event Callbacks */

	// Adds tapped digit to numeric value
	private void OnDigitDown( DXButton btn )
	{
		string numberStr = NumberText;
		bool isNull = string.IsNullOrEmpty( numberStr );

		int digit = (int)btn.Data;

		// First digit can't be 0
		if ( !isNull || (digit > 0) )
		{
			// Capped at 5 digits
			if ( isNull || (numberStr.Length < MaxDigits) )
			{
				string digitStr = digit.ToString();

				// First entry deletes previous value, otherwise append
				string newStr = firstEntry ? digitStr : (numberStr + digitStr);

				// Validate int
				try
				{
					int value = Convert.ToInt32( newStr );

					// Validate range
					if ( (value >= MinValue) && (value <= MaxValue) )
					{
						NumberText = newStr;

						firstEntry = false;

						backBtn.IsDisabled = false;
						doneBtn.IsDisabled = false;
					}
				}
				catch ( Exception ex )
				{
					DXLog.Debug( "keypad:{0}", ex.Message );
				}
			}
		}
	}

	// Removes last digit from current value
	private void OnBackTapped()
	{
		string numberStr = NumberText;

		// Must have at least 1 digit to delete
		if ( !string.IsNullOrEmpty( numberStr ) )
		{
			numberStr = numberStr[ ..^1 ];

			bool disabled = (numberStr.Length == 0);

			backBtn.IsDisabled = disabled;
			doneBtn.IsDisabled = disabled;

			// Convert back to int
			NumberText = numberStr;
		}
	}

	// User done with entry
	private void OnDoneTapped()
	{
		// Close popup
		popup.Hide();

		doneBtn.IsDisabled = true;
	}

	// Popup either cancelled or confirmed with done button
	private void OnPopupClosed()
	{
		firstEntry = true;

		// Callback listener
		Completed?.Invoke();
	}
}

//
