/* 
 * 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 FFImageLoading.Maui;

using Syncfusion.Maui.Buttons;
using Syncfusion.Maui.TreeView;
using Syncfusion.TreeView.Engine;

using DXLib.UI.Layout;
using DXLib.UI.Control.Image;
using DXLib.UI.Control.Checkbox;

using DXLib.Data.Model;

namespace DXLib.UI.Control.Tree;

/*
 * An SfTreeView used to display a hierarchical tree of tri-state checkbox nodes. The tree is populated using
 * DXModel based data.
 */
public class DXTree : SfTreeView
{
	/* Constants */
	public const double ExpanderSize = 30;

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

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

	// Auto expand root nodes?
	public bool AutoExpand { set => AutoExpandMode = value ? TreeViewAutoExpandMode.RootNodesExpanded : TreeViewAutoExpandMode.None; }

	/* Fields */
	private readonly ObservableCollection<DXTreeDatum> data;

	/* Methods */
	public DXTree()
	{
		BackgroundColor = DXColors.Light4;

		// Parent node checks/unchecks child nodes
		CheckBoxMode = TreeNodeCheckBoxMode.Recursive;

		// Populate on demand
		NotificationSubscriptionMode = TreeViewNotificationSubscriptionMode.CollectionChange;
		NodePopulationMode = TreeNodePopulationMode.Instant;

		// Custom expand/collapse icons
		ExpanderWidth = 0;
		ExpanderPosition = TreeViewExpanderPosition.Start;
		ExpandActionTarget = TreeViewExpandActionTarget.Node;

		// Animated
		AutoExpandMode = TreeViewAutoExpandMode.None;
		IsAnimationEnabled = true;

		// No selection
		SelectionMode = TreeViewSelectionMode.None;
		SelectionBackground = DXColors.Light2;

		// Custom sizing
		ItemHeight = 35;
		Indentation = 20;

		// Custom data
		ItemTemplateContextType = ItemTemplateContextType.Node;
		ItemTemplate = CreateTemplate();

		ChildPropertyName = "ChildData";

		// Register for events
		NodeChecked += OnNodeChecked;

		// Allocate data container
		data = new ObservableCollection<DXTreeDatum>();
		ItemsSource = data;
	}

	// Returns custom data template used for each item in tree
	private static DataTemplate CreateTemplate()
	{
		DataTemplate dataTemplate = new( () =>
		{
			// Layout
			DXGridLayout layout = new()
			{
				Padding = 0,
				RowSpacing = 0,
				ColumnSpacing = 0,
				
				Horizontal = LayoutOptions.Fill,
				Vertical = LayoutOptions.Fill
			};
			
			// Custom expander icon
			DXImage icon = new()
			{
				Margin = 0,
				
				Vertical = LayoutOptions.Fill,
				Horizontal = LayoutOptions.Fill
			};
		
			icon.SetSize( ExpanderSize, ExpanderSize );

			Binding source = new( "IsExpanded" ) { Converter = new DXTreeConverter() };
			Binding visibility = new( "HasChildNodes" ) { Converter = new DXTreeVisibilityConverter() };

			icon.SetBinding( CachedImage.SourceProperty, source );
			icon.SetBinding( IsVisibleProperty, visibility );

			// Checkbox (MUST set disabled color)
			DXCheckbox checkbox = new()
			{
				IsTriState = true,
				DisabledColor = DXCheckbox.DefaultDisabledColor,
				
				Padding = new Thickness( 5, 0, 0, 0 ),			// REQUIRED workaround
				Margin = 0,
				
				Horizontal = LayoutOptions.Fill,
				Vertical = LayoutOptions.Fill
			};

			checkbox.SetBinding( ToggleButton.TextProperty, "Content.Text" );
			checkbox.SetBinding( ToggleButton.IsEnabledProperty, new Binding( "Content.IsEnabled" ) );
			checkbox.SetBinding( SfCheckBox.IsCheckedProperty, new Binding( "IsChecked", BindingMode.TwoWay ) );

			// 2 columns
			layout.AddFixedColumn( ExpanderSize - 5 );	// 0: icon
			layout.AddStarColumn();						// 1: checkbox

			layout.Add( icon, 0, 0 );
			layout.Add( checkbox, 1, 0 );

			return layout;
		});

		return dataTemplate;
	}

	// Adds a top-level (root) node to the tree
	public DXTreeDatum AddRoot( string key, string text, bool enabled = true )
	{
		// Create node
		DXTreeDatum root = new()
		{
			Parent = null,
			Key = key,
			Text = text,
			IsEnabled = enabled,
			ChildData = []
		};

		// Add to tree
		data.Add( root );

		return root;
	}

	// Adds child node to specified node, optionally also adding leaf nodes
	public static DXTreeDatum AddNode( DXTreeDatum node, string key, string text, bool enabled = true, List<DXModel> models = null )
	{
		// Create child node
		DXTreeDatum child = new()
		{
			Parent = node.Key,
			Key = key,
			Text = text,
			IsEnabled = enabled,
			Model = null,
			ChildData = []
		};

		node.ChildData.Add( child );

		// Optionally add children within new node
		if ( models != null )
		{
			AddChildren( child, models );
		}

		return child;
	}

	// Adds child nodes to specified node
	public static void AddChildren( DXTreeDatum node, List<DXModel> models )
	{
		// Create child node for each model
		foreach ( DXModel model in models )
		{
			DXTreeDatum child = new()
			{
				Parent = node.Key,
				Key = model.UniqueId,
				Text = model.ObjectName,
				IsEnabled = true,
				Model = model
			};

			node.ChildData.Add( child );
		}
	}

	// Clears all tree nodes
	public void Clear()
	{
		data.Clear();
	}

	/* Select */

	// Returns count of all currently selected nodes in tree
	public int GetSelectedCount()
	{
		return CheckedItems.Count;
	}

	// Returns list of all selected datums within specified root key
	public List<DXTreeDatum> GetSelected( string key )
	{
		List<DXTreeDatum> selected = [];

		// Include items from any hierarchical node
		foreach ( DXTreeDatum item in CheckedItems )
		{
			if ( (item.Parent != null) && item.Parent.Equals( key ) )
			{
				selected.Add( item );
			}
		}

		return selected;
	}

	// Returns list of all selected objects with specified key
	public List<DXModel> GetSelectedModels( string key )
	{
		List<DXModel> selected = [];

		// Include items from any hierarchical node
		foreach ( DXTreeDatum item in CheckedItems )
		{
			if ( (item.Parent != null) && item.Parent.Equals( key ) )
			{
				selected.Add( item.Model );
			}
		}

		return selected;
	}

	/* Event Callbacks */

	// User checked/unchecked a tree node
	private void OnNodeChecked( object sender, NodeCheckedEventArgs args )
	{
		Checked?.Invoke();
	}
}

//
