/* 
 * 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;
using DXLib.Utils;

namespace DXLib.UI.Gestures;

/*
 * Equivalent to DXGridLayout with the addition of support for up, down, tap, and long press gestures.
 */
public class DXGridGestures : MR.Gestures.Grid
{
	/* Properties */
	public LayoutOptions Horizontal { set => HorizontalOptions = value; }
	public LayoutOptions Vertical { set => VerticalOptions = value; }

	// Current grid height/width
	public int RowCount => RowDefinitions.Count;
	public int ColumnCount => ColumnDefinitions.Count;

	// Dynamic index of row/col just added
	public int RowIndex => RowCount - 1;
	public int ColumnIndex => ColumnCount - 1;

	/* Methods */
	
	// Shorthand for setting dimensions
	public void SetSize( double wd, double ht )
	{
		WidthRequest = wd;
		HeightRequest = ht;
	}
	
	// Shorthand for setting margins
	public void SetMargin( double x, double y, double wd, double ht )
	{
		Margin = new Thickness( x, y, wd, ht );
	}
	
	// Shorthand for setting layout options
	public void SetPosition( LayoutOptions horizontal, LayoutOptions vertical )
	{
		Horizontal = horizontal;
		Vertical = vertical;
	}
	
	// Shorthand for adding child at specified row/column
	public void Add( IView view, int col, int row )
	{
		Add( view );

		SetColumn( view, col );
		SetRow( view, row );
	}

	// Shorthand for adding child at specified row/column with spans
	public void Add( IView view, int col, int row, int colSpan, int rowSpan )
	{
		Add( view, col, row );
		
		SpanColumns( view, colSpan );
		SpanRows( view, rowSpan );
	}

	// Shorthand for removing specified child
	public new void Remove( IView view )
	{
		if ( (view != null) && Contains( view ) )
		{
			base.Remove( view );
		}
	}

	// Clears row/column definitions
	public new void Clear()
	{
		RowDefinitions.Clear();
		ColumnDefinitions.Clear();
	}

	// Clears row/column definitions AND all child views
	public void ClearAll()
	{
		base.Clear();

		Clear();
	}

	// Non-static method for specifying row span for a view
	public void SpanRows( IView view, int value )
	{
		SetRowSpan( view, value );
	}

	// Non-static method for specifying column span for a view
	public void SpanColumns( IView view, int value )
	{
		SetColumnSpan( view, value );
	}

	// Fills specified grid cell with solid color view
	public void Fill( Color color, int col, int row, int colSpan = 0, int rowSpan = 0 )
	{
		DXFill fill = null;

		// Might have existing fill
		foreach ( IView view in Children )
		{
			if ( (col == GetColumn( view )) && (row == GetRow( view )) )
			{
				fill = (view as DXFill);
			}
		}

		// Create new fill
		if ( fill == null )
		{
			fill = new DXFill();

			Add( fill, col, row );

			// Optional row/column spans
			if ( rowSpan > 0 )
			{
				SpanRows( fill, rowSpan );
			}

			if ( colSpan > 0 )
			{
				SpanColumns( fill, colSpan );
			}
		}

		// Change color
		fill.Color = color;
	}

	/* Row Utilities */

	// Adds fixed size row of specified dp height
	public void AddFixedRow( double value )
	{
		RowDefinitions.Add( new RowDefinition { Height = new GridLength( value ) } );
	}

	// Adds fixed size row of specified dp height at specified index
	public void InsertFixedRow( double value, int index )
	{
		RowDefinitions.Insert( index, new RowDefinition { Height = new GridLength( value ) } );
	}

	// Adds N number of fixed sized rows of specified dp height
	public void AddFixedRows( int count, double value )
	{
		for ( int i = 0; i < count; i++ )
		{
			AddFixedRow( value );
		}
	}

	// Adds proportionally sized row of equal height to other star rows
	public void AddStarRow()
	{
		AddStarRow( 1 );
	}

	// Adds proportionally sized row at specified percentage height
	public void AddStarRow( double value )
	{
		RowDefinitions.Add( new RowDefinition { Height = new GridLength( value, GridUnitType.Star ) } );
	}

	// Adds N number of equal height rows
	public void AddStarRows( int count )
	{
		for ( int i = 0; i < count; i++ )
		{
			AddStarRow();
		}
	}

	// Adds a row that auto sizes to its content
	public void AddAutoRow()
	{
		RowDefinitions.Add( new RowDefinition { Height = GridLength.Auto } );
	}

	// Inserts an auto row into layout at specified index
	public void InsertAutoRow( int index )
	{
		RowDefinitions.Insert( index, new RowDefinition { Height = GridLength.Auto } );
	}
	
	// Adds N number of auto sized rows
	public void AddAutoRows( int count )
	{
		for ( int i = 0; i < count; i++ )
		{
			AddAutoRow();
		}
	}

	// Removes row at specified index
	public void RemoveRow( int index )
	{
		RowDefinitions.RemoveAt( index );
	}

	/* Column Utilities */

	// Adds fixed size column of specified dp width
	public void AddFixedColumn( double value )
	{
		ColumnDefinitions.Add( new ColumnDefinition { Width = new GridLength( value ) } );
	}

	// Adds fixed size column of specified dp width at specified index
	public void InsertFixedColumn( double value, int index )
	{
		ColumnDefinitions.Insert( index, new ColumnDefinition { Width = new GridLength( value ) } );
	}

	// Adds N number of fixed sized columns of specified dp width
	public void AddFixedColumns( int count, double value )
	{
		for ( int i = 0; i < count; i++ )
		{
			AddFixedColumn( value );
		}
	}

	// Adds proportionally sized column of equal width to other star columns
	public void AddStarColumn()
	{
		AddStarColumn( 1 );
	}

	// Adds proportionally sized column at specified percentage width
	public void AddStarColumn( double value )
	{
		ColumnDefinitions.Add( new ColumnDefinition { Width = new GridLength( value, GridUnitType.Star ) } );
	}

	// Adds N number of equal height columns
	public void AddStarColumns( int count )
	{
		for ( int i = 0; i < count; i++ )
		{
			AddStarColumn();
		}
	}

	// Adds a columns that auto sizes to its content
	public void AddAutoColumn()
	{
		ColumnDefinitions.Add( new ColumnDefinition { Width = GridLength.Auto } );
	}

	/* 
	 * Long Press
	 * 
	 * Used to provide improved long press tracking for any DXGridLayout.
	 * Rather than waiting for the press to be released, the callback will
	 * be invoked as soon as the delay time has elapsed, while the finger 
	 * is still down.
	 */

	// Minimum trigger time for long press
	private const int LongPressDelay = 500;

	// Callback when long press triggered
	public Action<Point> Pressed { set { callback = value; AddLongPress(); } }

	// Tracks duration
	private DXTimer timer;

	// Callback handler
	private Action<Point> callback;

	// Register for long press event handling
	private void AddLongPress()
	{
		Down += OnDown;
		Up += OnUp;
	}

	// User started press
	private void OnDown( object sender, MR.Gestures.DownUpEventArgs args )
	{
		// If press continues longer than trigger time, callback listener
		timer = DXTimer.Delay( LongPressDelay, () =>
		{
			callback?.Invoke( args.Touches[0] );
		});
	}

	// User released finger, ends long press tracking
	private void OnUp( object sender, MR.Gestures.DownUpEventArgs args )
	{
		timer?.Stop();
	}

	/* Layout */

	// Forces layout for current device type/orientation
	public virtual void UpdateLayout( bool wide = false )
	{
		UpdateLayout( DXDevice.GetLayoutType( wide ) );
	}

	// Gives subclasses opportunity to make type/orientation specific changes
	public virtual void UpdateLayout( LayoutType type )
	{
		switch ( type )
		{
			case LayoutType.Landscape: Landscape(); break;
			case LayoutType.Portrait: Portrait(); break;

			case LayoutType.WideLandscape: WideLandscape(); break;
			case LayoutType.WidePortrait: WidePortrait(); break;

			case LayoutType.MobileLandscape: MobileLandscape(); break;
			case LayoutType.MobilePortrait: MobilePortrait(); break;
			
			default: break;
		}
	}

	// Layout changes specific to each possible size
	protected virtual void Landscape() {}
	protected virtual void Portrait() {}

	protected virtual void WideLandscape() {}
	protected virtual void WidePortrait() {}

	protected virtual void MobileLandscape() {}
	protected virtual void MobilePortrait() {}
}

//
