/* 
 * 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.UI.Control.Checkbox;

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

using DXLib.Utils;

namespace DXLib.UI.Form.Control;

/*
 * A form control used to select multiple text items from a single node treeview control. This is similar to a multiselect
 * field except that all checkboxes can be turned on/off by a parent checkbox. The parent checkbox is tri-state, indicating
 * whether all, some, or no options are selected.
 */
public class DXTreeSelect : DXFormControl
{
	/* Constants */
	private const double DefaultPopupWd = 260;
	private const double DefaultPopupHt = 370;
		
	/* Events */
	public Action Selected { get; set; }

	/* Properties */

	// Text label for item type being selected
	public string Label { get; set; }

	// Pluralize label for display?
	public bool IsPlural { get; set; }

	// Max number selected items to display in form field
	public int MaxDisplay { get; set; }

	// List of currently selected items keys
	public List<string> SelectedKeys => GetSelectedKeys();

	// All/none items currently selected?
	public bool AllSelected => node.AllSelected;
	public bool NoneSelected => node.NoneSelected;

	/* Fields */
	private readonly DXTextButton fieldBtn;

	private readonly DXVerticalLayout layout;
	private readonly DXCheckboxNode node;
	private readonly DXButton okBtn;

	private DXPopup popup;

	// State control
	private List<string> initialKeys;

	/* Methods */
	public DXTreeSelect()
	{
		// Form field
		fieldBtn = new DXTextButton
		{
			TextColor = DXColors.Dark1,
			Font = DXFonts.Roboto,
			FontSize = 17,

			HeightRequest = 36,
			ButtonTapped = OnTapped
		};

		// Popup view
		layout = new DXVerticalLayout
		{
			BackgroundColor = DXColors.Light2,

			Padding = 0,
			Spacing = 0,

			WidthRequest = DefaultPopupWd,
			HeightRequest = DefaultPopupHt,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill,
			
			IgnoreSafeArea = true
		};

		// Underlying controls
		node = new DXCheckboxNode
		{
			Padding = new Thickness( 15, 10, 15, 0 ),
			HeightRequest = 314,

			SelectionChanged = OnSelectionChanged
		};

		layout.Add( node );

		// Button layout
		DXHorizontalLayout btnLayout = new()
		{
			Padding = new Thickness( 0, 10, 0, 0 ),
			Spacing = 10,

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

		// Cancel
		DXButton cancelBtn = new()
		{
			Resource = "alert.cancel",
			Type = DXButton.ButtonType.Neutral,

			Margin = 0,
			ButtonWd = 75,

			ButtonTapped = OnCancelled
		};

		cancelBtn.Init();
		
		// OK
		okBtn = new DXButton
		{
			Resource = "alert.ok",
			Type = DXButton.ButtonType.Action,

			Margin = 0,

			ButtonTapped = OnCompleted
		};

		okBtn.Init();
		
		btnLayout.Add( cancelBtn );
		btnLayout.Add( okBtn );

		layout.Add( btnLayout );
	}

	/* Load */

	// Sets root checkbox value
	public void SetRoot( string key, string text )
	{
		node.SetRoot( key, text );
	}

	// Sets child checkboxes from LUT item list
	public void SetChildItems( string key )
	{
		node.SetChildItems( key );
	}

	// Sets child checkboxes from list of key,value items
	public void SetChildItems( List<DXItem> items )
	{
		node.SetChildItems( items );
	}

	// Sets child checkboxes from data model list
	public void SetChildModels( List<DXModel> models, bool sort = true )
	{
		List<DXModel> list = sort ? models.OrderBy( m => m.ObjectName ).ToList() : models;

		node.SetChildModels( list );
	}

	/* Select */

	// Returns list of currently selected item keys
	public List<string> GetSelectedKeys()
	{
		return IsDisabled ? null : (GetObject() as List<string>);
	}

	// (De)selects item for specified key
	public void SelectItem( string key, bool selected )
	{
		node.SelectItem( key, selected );
	}

	// Selects root and all child nodes
	public void SelectAll()
	{
		node.SelectAll( true );

		UpdateField();
	}

	/* Internal */

	// Updates control field with selected item(s)
	public void UpdateField()
	{
		List<string> items = node.GetSelectedItems();

		int count = node.ChildCount;
		int selectedCount = items.Count;

		string text;
		string label = IsPlural ? DXUtils.Pluralize( Label ) : Label;

		// 'All Foos' 
		if ( selectedCount == count )
		{
			text = $"{DXString.Get( "form.tree.all" )} {label}";
		}
		// 'X Foos'
		else if ( selectedCount > MaxDisplay )
		{
			text = $"{selectedCount} {label}";
		}
		// 'Foo1, Foo2'
		else
		{
			text = string.Join( ", ", items );
		}

		fieldBtn.Text = text;
	}

	// Displays checkbox node in modal popup anchored to form control
	private void Show()
	{
		initialKeys = SelectedKeys;

		// Show modal
		popup = new DXPopup( layout )
		{
			IsModal = true,
			
			ViewWidth = DefaultPopupWd,
			ViewHeight = DefaultPopupHt
		};

		popup.ShowFromView( this, (Width * 0.25), 0 );
	}

	/* Abstracts */

	// Insert in parent layout
	public override void Init()
	{
		AddControl( fieldBtn );
	}

	// Returns underlying object as array of selected keys
	public override object GetObject()
	{
		return node.GetSelectedKeys();
	}

	// Placeholder only visible when not focused with no value
	public override void SetPlaceholder( bool focused )
	{
		bool hasValue = HasValue();

		// Focused never shows placeholder
		if ( focused )
		{
			fieldBtn.Text = (HasValue() ? fieldBtn.Text : null)!;
		}
		// Unfocused shows placeholder if no value
		else if ( !hasValue )
		{
			fieldBtn.Text = Title;
		}
	}

	// Has value if at least one checkbox selected
	public override bool HasValue()
	{
		return (node.SelectedCount > 0);
	}

	// Valid if optional or at least one selection
	public override bool IsValid()
	{
		return !IsRequired || (node.SelectedCount > 0);
	}

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

		fieldBtn.Opacity = disabled ? DisabledOpacity : 1.0;

		SetState( ControlState.Normal );
	}

	/* Event Callbacks */

	// User tapped anywhere on entry field
	private void OnTapped( object data )
	{
		if ( !IsDisabled )
		{
			OnFocus();

			// Display in popup
			Show();
		}
	}

	// User un/checked box in node list
	private void OnSelectionChanged()
	{
		okBtn.IsDisabled = !IsValid();
	}

	// Editing done
	private void OnCompleted( object sender )
	{
		OnChange();

		// Update text
		UpdateField();

		// Must occur last
		OnDone();

		popup.Hide();

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

	// User cancelled, restore previous selection
	private void OnCancelled( object sender )
	{
		node.SelectItems( initialKeys );

		popup.Hide();

		SetState( ControlState.Normal );
	}
}

//
