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

using System.Collections.ObjectModel;

using Syncfusion.Maui.Core;
using Syncfusion.Maui.Inputs;

using DXLib.UI.Form;

using DXLib.Data;
using DXLib.Data.Model;

using DXLib.Utils;

namespace DXLib.UI.Control.Selector;

/*
 * ComboBox control that allows a single string item to be selected from a static list of items. The items can be either
 * from a resource string lookup table (LUT) or from a queried list of database objects.
 */
public class DXSelector : SfComboBox
{
	/* Constants */
	public const double DisabledOpacity = 0.40;

	/* Events */
	public Action<object> Selected { get; set; }
	
	// Required for some subclasses
	public Action AddTapped { get; set; }

	/* Properties */
	public LayoutOptions Horizontal { set => HorizontalOptions = value; }
	public LayoutOptions Vertical { set => VerticalOptions = value; }

	// Population mode
	public bool HasItems => items is { Count: > 0 };
	public bool HasObjects => objects is { Count: > 0 };

	// Currently selected key (item mode only)
	public string SelectedKey { get => GetSelectedKey(); set => SelectKey( value ); }

	// Has a value been selected?
	public bool HasSelection => (SelectedItem != null);

	// Required for some subclasses
	public virtual string ObjectLabel { get; set; }

	/* Fields */

	// LUT based
	private ObservableCollection<DXItem> items;

	// Query based
	protected ObservableCollection<DXModel> objects;

	// UI
	private readonly DXIcon icon;
	
	/* Methods */
	public DXSelector()
	{
		DisplayMemberPath = "Value";
		
		Padding = new Thickness( 0, 10, 0, 0 );
		Margin = 0;
		
		// Configure underlying control
		HeightRequest = DXFormControl.DefaultControlHt;
		
		HorizontalOptions = LayoutOptions.Fill;
		VerticalOptions = LayoutOptions.Fill;

		IsEditable = false;
		IsClearButtonVisible = false;

		ShowBorder = false;
		BackgroundColor = DXColors.Light4;
		ItemPadding = new Thickness( 10, 5 );

		TextColor = DXColors.Dark1;
		FontFamily = DXFonts.Roboto;
		FontAttributes = FontAttributes.None;
		FontSize = 17;

		FontFamily = DXFonts.Roboto;
		FontAttributes = FontAttributes.None;

		ShowBorder = false;
		PlaceholderColor = DXColors.Dark4;

		DropDownPlacement = DropDownPlacement.Auto;
		DropDownBackground = DXColors.Light4;
		SelectedDropDownItemBackground = DXColors.Accent1;
		
		DropDownStroke = DXColors.Dark2;
		DropDownStrokeThickness = 1;

		DropDownItemHeight = 36;
		DropDownItemFontSize = 17;
		DropDownItemTextColor = DXColors.Dark1;
		DropDownItemFontFamily = DXFonts.Roboto;
		DropDownItemFontAttributes = FontAttributes.None;

		MaxDropDownHeight = 350;

		// Custom dropdown icon to avoid right hand border glitch
		icon = new DXIcon
		{
			Margin = 0,

			Resource = "dropdown",
			Color = DXColors.Dark3,

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

		DropDownButtonSettings = new DropDownButtonSettings
		{
			View = icon
		};
		
		SelectionChanged += OnSelectionChanged;
		DropDownClosed += OnDropdownClosed;
	}

	// Visually enables/disables control
	public void SetDisabled( bool disabled )
	{
		IsEnabled = !disabled;
		
		Opacity = disabled ? DisabledOpacity : 1.0;
	}

	// Updates control background color  
	public void SetColor( Color color )
	{
		BackgroundColor = color;

		icon.BackgroundColor = color;
	}
	
	// Clears control display contents
	private new void Clear()
	{
		if ( !string.IsNullOrEmpty( Text ) )
		{
			Text = string.Empty;
		}
	}

	/* LUT Based */

	// Loads static lookup table for specified base key
	public void LoadItems( string baseKey )
	{
		if ( baseKey == null )
		{
			SetDisabled( false );
		}
		else
		{
			// Parse LUT
			List<DXItem> lookupTable = DXString.GetLookupList( baseKey );

			items = new ObservableCollection<DXItem>( lookupTable );

			// Populate control
			ItemsSource = items;
		}
	}

	// Loads static lookup table from specified list
	public void LoadItems( List<DXItem> list )
	{
		if ( list == null )
		{
			SetDisabled( false );
		}
		else
		{
			items = new ObservableCollection<DXItem>( list );

			SelectionChanged -= OnSelectionChanged;
			
			// Populate control
			ItemsSource = items;
			
			SelectionChanged += OnSelectionChanged;
		}
	}

	// Returns current selection in item mode
	public DXItem GetSelectedItem()
	{
		return HasItems ? (DXItem)SelectedItem : null;
	}

	// Returns currently selected item key
	private string GetSelectedKey()
	{
		return GetSelectedItem()?.Key;
	}

	// Selects specified lookup table value in list, null to clear
	public void SelectKey( string key )
	{
		SelectionChanged -= OnSelectionChanged;

		// Clear 
		if ( key == null )
		{
			if ( SelectedItem != null )
			{
				Clear();
				SelectedItem = null;
			}
		}
		// New value
		else
		{
			SelectedItem = GetItem( key );
		}
		
		SelectionChanged += OnSelectionChanged;
	}

	// Returns item matching specified key from preloaded list
	private DXItem GetItem( string key )
	{
		return items.FirstOrDefault( item => item.Key == key );
	}

	// Determines if preloaded list contains specified key
	public bool HasKey( string key )
	{
		return GetItem( key ) != null;
	}

	// Removes specified key from item list
	public void RemoveItem( string key )
    {
		DXItem item = GetItem( key );

		if ( (item != null) && items.Contains( item ) )
		{
			items.Remove( item );
		}
    }

	/* Object Based */

	// Loads list from specified query results (objects must have 'ObjectName' field)
	public virtual void LoadObjects( IList<DXModel> list )
	{
		if ( (list == null) || (list.Count == 0) )
		{
			SetDisabled( true );
		}
		else
		{
			// Sort alphabetically
			objects = new ObservableCollection<DXModel>( list.OrderBy( m => m.ObjectName ) );

			ItemsSource = objects;
			DisplayMemberPath = "ObjectName";
		}
	}

	// Returns current selection in object mode
	public DXModel GetSelectedObject()
	{
		return HasObjects ? (DXModel)SelectedItem : null;
	}

	// Selects specified query object in list
	public void SelectObject( object value )
	{
		SelectionChanged -= OnSelectionChanged;

		// Clear
		if ( value == null )
		{
			Clear();
			SelectedItem = null;
		}
		// New value
		else
		{
			SelectedItem = value;
		}
	}

	/* Event Callbacks */

	// Selection done with change made
	private void OnSelectionChanged( object sender, EventArgs args )
	{
		SelectionChanged -= OnSelectionChanged;

		// Callback listener
		Selected?.Invoke( SelectedItem ); 
	}

	// Required to prevent SelectionChanged callback on programmatic events
	private void OnDropdownClosed( object sender, EventArgs args )
	{
		SelectionChanged += OnSelectionChanged;
	}
}

//
