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

using DXLib.UI.Control.Picker;
using DXLib.Utils;

namespace DXLib.UI.Form.Control;

/*
 * A form control used to select from a static list of options. The options are displayed in a popup with a native
 * picker control. The selected value is displayed in string format in the control.
 */
public class DXPicker : DXFormControl
{
	/* Constants */

	// Available picker format options
	public enum PickerType
	{
		Date,
		Time,
		Duration
	};

	// Undefined date/time values
	public static readonly DateTime EmptyDate = DateTime.UnixEpoch;
	public static readonly TimeSpan EmptyTime = TimeSpan.Zero;

	/* Properties */
	public PickerType Type { set => SetPickerType( value ); }

	public bool IsDuration => (pickerType == PickerType.Duration);
	public bool IsEmpty { set => SetEmpty( value ); }

	// Type specific values
	public DateTime Date { get; set; }
	public TimeSpan Time { get; set; }

	public TimeSpan? Duration
	{
		get => durationPicker.Duration;
		set { durationPicker.Duration = value; IsValidValue = (value != null); }
	}

	// Type specific min/max
	public DateTime MinDate { set => datePicker.MinimumDate = value; }
	public DateTime MaxDate { set => datePicker.MaximumDate = value; }

	// Allows external validity control
	public bool IsValidValue { get; set; }

	/* Fields */
	private PickerType pickerType;

	// Underlying controls
	private DXDatePicker datePicker;
	private DXTimePicker timePicker;

	private DXDurationPicker durationPicker;

	/* Methods */
	public DXPicker()
	{
		// Defaults
		Date = EmptyDate;
		Time = EmptyTime;
	}

	// Create underlying control
	private void SetPickerType( PickerType type )
	{
		pickerType = type;

		bool ios = DXDevice.IsIOS;

		switch ( type )
		{
			// Date
			case PickerType.Date:
			{
				datePicker = new DXDatePicker
				{
					Date = Date,

					Margin = new Thickness( 0, (ios ? 0 : 10), 0, 0 )
				};
				
				// Register for events
				datePicker.Focused += OnFocused;
				datePicker.Unfocused += OnUnfocused;
				datePicker.DateSelected += OnDateSelected;

				// Valid by default
				IsValidValue = true;
				break;
			}
			// Time
			case PickerType.Time:
			{
				timePicker = new DXTimePicker
				{
					Time = Time
				};

				// Register for events
				timePicker.Focused += OnFocused;
				timePicker.Unfocused += OnUnfocused;
				timePicker.PropertyChanged += ( sender, e ) =>
				{
					if ( e.PropertyName == TimePicker.TimeProperty.PropertyName )
					{
						OnTimeSelected();
					}
				};

				// Valid by default
				IsValidValue = true;
				break;
			}
			// Duration
			case PickerType.Duration:
			{
				durationPicker = new DXDurationPicker
				{
					Selected = OnDurationSelected
				};

				// Can be invalid
				IsValidValue = false;
				break;
			}
			
			default: break;
		}
	}

	// Gets value from underlying picker control (Date only)
	public DateTime GetDate()
    {
		return datePicker.Date;
    }

	// Forces update of underlying picker value (Date only)
	public void SetDate( DateTime date )
    {
		if ( datePicker != null )
        {
			datePicker.DateSelected -= OnDateSelected;

			// Do NOT trigger event
			datePicker.Date = date;

			datePicker.DateSelected += OnDateSelected;
        }
    }

	// Shows empty (null) value for non-nullable picker types
	public void SetEmpty( bool empty )
	{
		double opacity = empty ? 0.1 : 1.0;

		switch ( pickerType )
		{
			case PickerType.Date: datePicker.Opacity = opacity; break;
			case PickerType.Time: timePicker.Opacity = opacity; break;
			default: break;
		}
	}

	// Configures picker specific error hint text
	protected override void SetHintType( HintType type )
	{
		base.SetHintType( type );

		// Parent did not set
		if ( !string.IsNullOrEmpty( hintLbl.Text ) )
		{
			string resource = type switch
			{
				HintType.Date => "form.hint.date",
				HintType.Duration => "form.hint.duration",
				
				_ => null
			};

			hintLbl.Text = DXString.Get( resource );
		}
	}

	// Disables extended controls
	public override void SetDisabled( bool disabled )
	{
		base.SetDisabled( disabled );

		double opacity = disabled ? (DisabledOpacity - 0.20) : 1.0;

		// Disable picker specific controls
		switch ( pickerType )
		{
			case PickerType.Date:
			{
				if ( datePicker.Opacity > 0 )
				{
					datePicker.Opacity = opacity;
				} 
				
				break;
			}
			case PickerType.Time:
			{
				if ( timePicker.Opacity > 0 )
				{
					timePicker.Opacity = opacity;
				} 
				
				break;
			}
			case PickerType.Duration:
			{
				if ( durationPicker.Opacity > 0 )
				{
					durationPicker.Opacity = opacity; 
				}
				
				break;
			}
			
			default: break;
		}
	}

	/* Abstracts */

	// Insert in parent layout
	public override void Init()
	{
		switch ( pickerType )
		{
			case PickerType.Date: AddControl( datePicker ); break;
			case PickerType.Time: AddControl( timePicker ); break;
			case PickerType.Duration: AddControl( durationPicker ); break;
			default: break;
		}
	}

	// Returns underlying picker value
	public override object GetObject()
	{
		return pickerType switch
		{
			PickerType.Date => Date,
			PickerType.Time => Time,
			PickerType.Duration => Duration,

			_ => null
		};
	}

	// Sets control placeholder text (if applicable)
	public override void SetPlaceholder( bool focused )
	{
		if ( pickerType == PickerType.Duration )
		{
			durationPicker.SetPlaceholder();
		}
	}

	// Has value if picker value has been selected
	public override bool HasValue()
	{
		return pickerType switch
		{
			PickerType.Date => (Date != EmptyDate),
			PickerType.Time => (Time != EmptyTime),
			PickerType.Duration => Duration.HasValue,

			_ => false
		};
	}

	// Always valid by default, can be overidden externally
	public override bool IsValid()
	{
		return IsValidValue;
	}

	/* Event Callbacks */

	// Date picker value selected
	private void OnDateSelected( object sender, DateChangedEventArgs args )
	{
		Date = args.NewDate;

		OnSelected();
	}

	// Time picker value selected
	private void OnTimeSelected()
	{
		Time = timePicker.Time;

		OnSelected();
	}

	// Duration picker value selected
	private void OnDurationSelected()
	{
		IsValidValue = true;

		OnSelected();
	}
	
	// Control gained focus
	private void OnFocused( object sender, FocusEventArgs args )
	{
		if ( !IsDuration )
		{
			OnFocus();
		}
	}

	// Control lost focus
	private static void OnUnfocused( object sender, FocusEventArgs args )
	{}

	// Updates UI following user selection
	private void OnSelected()
	{
		OnChange( !IsDuration );
	}
}

//
