/*
 * 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.Control;
using DXLib.UI.Container;

using DXLib.Utils;

namespace DXLib.UI.Form;

/*
 * A vertical list of form input controls. The list can contain any combination of control types. An individual list
 * typically represents the group of required or optional controls on a form. The list is scrollable. The list can
 * optionally include a list of extended controls that can be added and removed independent of the base list. This is
 * typically used to support forms that dynamically add controls based on user input.
 */
public class DXFormControlList
{
	/* Constants */
	private const double BasePad = 24;
	
	/* Properties */
	public string Title { set => titleLbl.Resource = value; }
	public string TitleText { set => titleLbl.Text = value; }
	
	public Color Color { get => backgroundColor; set => SetColor( value ); }
	public bool IsReadOnly { set => titleLbl.TextColor = value ? DXColors.Dark4 : DXColors.Dark1; }

	// Disables list (title) but NOT underlying controls
	public bool IsDisabled { set => SetDisabled( value ); }

	// Shows/hides entire control list
	public bool IsVisible { set => View.IsVisible = value; }

	// Allows layout options to be overidden when used outside of forms
	public Thickness Padding { set { if ( scroll != null ) scroll.Padding = value; else layout.Padding = value; } }
	public LayoutOptions Vertical { set => layout.Vertical = value; }

	// Points to scroll view (tablet) or directly to layout (mobile)
	public View View { get; private set; }

	/* Fields */
	private readonly Dictionary<string,DXFormControl> controls;
	private readonly List<DXFormControl> extendedControls;

	private readonly DXScroll scroll;
	private readonly DXVerticalLayout layout;
	private readonly DXLabel titleLbl;

	// Custom config
	private Color backgroundColor;

	/* Methods */
	public DXFormControlList( bool scrollable = true )
	{
		bool ios = DXDevice.IsIOS;
		
		// Vertical layout
		layout = new DXVerticalLayout
		{
			BackgroundColor = DXColors.Light4,
			
			Padding = 0,
			Margin = 0,
			Spacing = 10,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill,

			// REQUIRED
			IgnoreSafeArea = true
		};

		// Control column scrolls independently on tablet
		if ( DXDevice.IsTablet && scrollable )
		{
			scroll = new DXScroll
			{
				Orientation = ScrollOrientation.Vertical,
				
				Padding = BasePad,
				Margin = 0,

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

			View = scroll;
		}
		// No scroll on mobile (or forced no scroll)
		else
		{
			layout.Padding = new Thickness( BasePad, (ios ? BasePad : 0 ), BasePad, BasePad );

			View = layout;
		}

		// Title
		titleLbl = new DXLabel
		{
			TextColor = DXColors.Dark1,
			Font = DXFonts.RobotoBold,
			FontSize = 17
		};

		layout.Add( titleLbl );

		// Allocate memory
		controls = new Dictionary<string,DXFormControl>();
		extendedControls = [];
	}

	// Post-construction control initialization
	public void Init()
	{
		foreach ( DXFormControl control in controls.Values )
		{
			control.Init();
		}
	}

	// Visually enables/disables control list title
	private void SetDisabled( bool disabled )
	{
		titleLbl.Opacity = disabled ? 0.3 : 1.0;
	}

	// Visually enables/disables title and all underlying controls
	public void DisableAll( bool disabled )
	{
		SetDisabled( disabled );

		foreach ( DXFormControl control in controls.Values )
		{
			control.IsDisabled = disabled;
		}
	}

	// Sets background color of underlying elements
	private void SetColor( Color value )
	{
		backgroundColor = value;

		layout.BackgroundColor = backgroundColor;
		scroll?.BackgroundColor = backgroundColor;
	}

	/* Add/Remove */
	
	// Adds control of any type to this list
	public void Add( DXFormControl control, bool required, bool readOnly )
	{
		int index = controls.Count;

		// Add to end of list
		Insert( control, index, required, readOnly );
	}

	// Inserts control of any type at specified position in list
	public void Insert( DXFormControl control, int index, bool required, bool readOnly )
	{
		string key = control.Key;

		if ( controls.TryAdd( key, control ) )
		{
			control.IsRequired = required;
			control.IsReadOnly = readOnly;

			control.SetColor( backgroundColor );
			control.SetState( DXFormControl.ControlState.Normal );

			// Account for title
			layout.Insert( (index + 1), control );
		}
	}

	// Removes specified control from list
	public void Remove( DXFormControl control )
	{
		if ( control != null )
		{
			string key = control.Key;

			if ( controls.ContainsKey( key ) )
			{
				layout.Remove( control );
				controls.Remove( key );
			}
		}
	}
	
	// Adds control to the extended list at end of stack
	public void AddExtended( DXFormControl control, bool required, bool readOnly )
	{
		extendedControls.Add( control );

		Add( control, required, readOnly );
	}
	
	// (Re)adds all controls to layout
	public void AddAll()
	{
		layout.Add( titleLbl );
		
		foreach ( DXFormControl control in controls.Values )
		{
			layout.Add( control );
		}
	}

	/* State */
	
	// Sets state for control matching specified key
	public void SetState( string key, DXFormControl.ControlState state )
	{
		controls[ key ].SetState( state );
	} 

	// Determines if any control in this list has changed its value
	public bool HasChanges()
	{
		return controls.Values.Any( control => control.HasChanges );
	}

	// Determines if all controls in list currently have valid values
	public bool IsValid()
	{
		return controls.Values.All( control => control.IsValid() );
	}

	// Un-focuses all controls in list except for specified focused control
	public void UnfocusAll( DXFormControl focused )
	{
		foreach ( DXFormControl control in controls.Values )
		{
			if ( (focused == null) || !control.Equals( focused ) )
			{
				control.LoseFocus();
			}
		}
	}

	/* Clear */

	// Clears layout but NOT controls
	public void Clear()
	{
		layout.Clear();
	}

	// Removes all controls from extended list
	public void ClearAllExtended()
	{
		foreach ( DXFormControl control in extendedControls )
		{
			layout.Remove( control );
			controls.Remove( control.Key );
		}

		extendedControls.Clear();
	}
	
	/* Get */

	// Returns control from list matching specified key
	public DXFormControl GetControl( string key )
	{
		return controls.GetValueOrDefault( key );
	}

	// Returns current input data for control matching specified key
	public object GetObject( string key )
	{
		return controls.TryGetValue( key, out var control ) ? control.GetObject() : null;
	}

	// Returns data value as string for control matching specified key
	public string GetString( string key )
	{
		string value = GetObject( key ) as string;

		return string.IsNullOrEmpty( value ) ? null : value;
	}

	// Returns data value as boolean for control matching specified key
	public bool GetBool( string key )
	{
		object obj = GetObject( key );

		return (obj != null) && (bool)obj;
	}

	// Returns data value as raw color for control matching specified key
	public long GetColor( string key )
	{
		object obj = GetObject( key );

		return (long)obj;
	}

	/* Layout */

	// Should ONLY be called if absolutely required
	public void UpdateLayout( LayoutType type )
	{
		foreach ( DXFormControl control in controls.Values )
		{
			control.UpdateLayout( type );
		}
	}
}

//
