/* 
 * 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.Log;
using DXLib.Utils;

namespace DXLib.UI.Control.Button;

/*
 * Implements a Material style button control with down, tap, and press button states. Also provides an optional sticky
 * state. Allows application specific objects to be stored with the button.
 */
public class DXButton : DXBorderGestures
{
	/* Constants */
	
	// Rounded rect buttons
	protected const int DefaultRadius = 4;
	
	// Material style drop shadow
	private const int DefaultElevation = 1;
	
	// Default sizing
	public const double DefaultWd = 64;
	public const double DefaultHt = 36;

	private const double DefaultFontSize = 15;
	
	// Default style options
	public enum ButtonType
	{
		Undefined = 0,
		
		Neutral,	
		Positive,
		Negative,
		Action
	};

	// Type specific button colors
	private static readonly Color[] DefaultColors =
	[
		DXColors.Transparent,
		
		DXColors.Neutral,
		DXColors.Positive,
		DXColors.Negative,
		DXColors.Action
	];

	// Default colors
	private static readonly Color DefaultTextColor = DXColors.Light4;
	
	// Opacities
	public const double DefaultDisabledOpacity = 0.20;
	private const double DefaultTapOpacity = 0.30;

	/* Events */
	public Action<DXButton> ButtonDown { get; set; }
	public Action<DXButton> ButtonTapped { get; set; }
	public Action<DXButton> ButtonPressed { get; set; }

	/* Properties */

	// Text
	public string Text { set => label.Text = value?.ToUpper()!; }
	public string Resource { set => label.Text = DXString.GetUpper( value ); }

	// Dynamic colors
	public Color ButtonColor { set { buttonColor = value; Color = value; } }
	public Color TextColor { set => label.TextColor = value; }
	public Color TapColor { get; set; }
	
	// Button config
	public ButtonType Type { get => buttonType; set => SetType( value ); }

	public string Font { set => label.Font = value; }
	public double FontSize { set => label.FontSize = value; }
	
	// Sizing
	public double ButtonWd { set => WidthRequest = value; }
	public double ButtonHt { set => HeightRequest = value; }

	// Adjust text alignment within button
	public double TextAdjustX { set => label.Margin = new Thickness( value, 0, 0, 0 ); }
	public double TextAdjustY { set => label.Margin = new Thickness( 0, value, 0, 0 ); }
	
	// Requires manual reset after tap?
	public bool IsSticky { get; set; }
	
	// Shows tap state?
	public bool IsTapShown { get; set; }
	
	// Material drop shadow?
	public bool HasShadow { get; set; }
	
	// Handling tap or press commands?
	public bool HasTap => (ButtonDown != null) || (ButtonTapped != null);
	public bool HasPress => (ButtonPressed != null);

	// Opacities
	public double DisabledOpacity { get; set; }
	public double TapOpacity { get; set; }
	
	// Currently enabled?
	public bool IsDisabled { get => !IsEnabled; set => SetDisabled( value ); }

	// Associated object
	public object Data { get; set; }

	/* Inherited */

	// CornerRadius
	// BorderWidth
	// BorderColor

	/* Fields */
	private Color buttonColor;
	private ButtonType buttonType;
	
	// Text component
	protected readonly DXLabel label;
	
	/* Methods */
	public DXButton( bool defaultSize = true )
	{
		bool ios = DXDevice.IsIOS;
		
		// Layout options
		Padding = new Thickness( 0, 0, 0, 0 );

		Horizontal = LayoutOptions.Center;
		Vertical = LayoutOptions.Center;

		// Underlying text
		label = new DXLabel
		{
			Padding = 0,
			Margin = new Thickness( 0, (ios ? 0 : -2), 0, 0 ),
			
			Font = DXFonts.RobotoBold,
			FontSize = DefaultFontSize,
			TextColor = DefaultTextColor,
			
			Horizontal = LayoutOptions.Center,
			Vertical = LayoutOptions.Center
		};

		Content = label;
		
		// Defaults
		Elevation = DefaultElevation;
		CornerRadius = DefaultRadius;
		BorderWidth = 0;

		DisabledOpacity = DefaultDisabledOpacity;
		TapOpacity = DefaultTapOpacity;
		
		IsSticky = false;
		IsTapShown = true;
		HasShadow = true;

		InputTransparent = false;
		
		// Do NOT automatically set size for all buttons
		if ( defaultSize  ) 
		{
			SetSize( DefaultWd, DefaultHt );
		}
		
		// Start in normal state
		Reset();
		
		// Register for events
		Down += OnDown;
		Tapped += OnTapped;
		LongPressing += OnLongPressed;			// Do NOT wait for press release
	}

    // Determines if specified point within bounds of this button
    public bool Contains( Point point )
	{
		return Bounds.Contains( point );
	}

	// Forces redraw of corners/border/shadow
	public override void Redraw()
	{
		Elevation = HasShadow ? Elevation : 0;
		
		// MUST be last
		base.Redraw();
	}
	
	// Updates button visual disabled state 
	protected virtual void SetDisabled( bool disabled )
	{
		IsEnabled = !disabled;

		// Optionally dim opacity
		Opacity = disabled ? DisabledOpacity : 1.0;

		// No shadow if disabled
		Elevation = (disabled || !HasShadow) ? 0 : DefaultElevation;
		
		// MUST call base only
		base.Redraw();
	}
	
	// Sets button color based on specified type
	private void SetType( ButtonType type )
	{
		buttonType = type;

		// Update color
		ButtonColor = DefaultColors[ (int)type ];
	}
	
	// Draws button in tapped state
	public void ShowTap()
	{
		// Optional custom tap color
		Color = TapColor ?? buttonColor;

		// Dim opacity
		Opacity = TapOpacity;

		// ALWAYS no shadow on tap
		Shadow = EmptyShadow;
	}

	// Draws button in normal state, resets sticky button
	public void Reset()
	{
		// Always return to normal color
		Color = buttonColor;
		
		// Normal opacity
		Opacity = 1.0;

		// Normal shadow
		Shadow = HasShadow ? DefaultShadow : EmptyShadow;
	}
	
	/* Event Callbacks */

	// User started tap
	protected void OnDown( object sender, MR.Gestures.DownUpEventArgs args )
	{
		if ( HasTap && !IsDisabled )
		{
			// Show tap state
			if ( IsTapShown )
			{
				ShowTap();
			}

			DXLog.Debug( "DOWN" );
			
			// Optionally immediately callback listener
			ButtonDown?.Invoke( this );
		}
	}

	// User fully completed tap
	protected virtual void OnTapped( object sender, MR.Gestures.TapEventArgs args )
	{
		if ( HasTap && !IsDisabled )
		{
			// Return to normal state unless sticky
			if ( IsTapShown && !IsSticky )
			{
				Reset();
			}

			DXLog.Debug( "TAPPED" );
			
			// Callback listener after full tap
			ButtonTapped?.Invoke( this );
		}
	}

	// User long pressed button, callback listener
	private void OnLongPressed( object sender, MR.Gestures.LongPressEventArgs args )
	{
		if ( HasPress && !IsDisabled )
		{
			ButtonPressed?.Invoke( this );
		}
	}
}

//
