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

using DXLib.UI;
using DXLib.UI.Layout;
using DXLib.UI.Container;

using DXLib.Utils;

namespace iStatVball3;

/*
 * Interface for configuring data for all reports on a report form. The builder includes a report type selector, team
 * selector, and dimension/filter selectors. The builder is a fixed height at the top of the report form.
 */
public class Builder : DXBorder
{
	/* Constants */

	// Row heights
	private const double SelectorHt = 70;
	private const double DimensionHt = 57;
	private const double AdjustHt = 10;

	// Refresh control
	private const double UpdateInterval = 500;

	/* Properties */

	// Grid/Bar/Shot/Heat/Trend
	public string SelectedType { get => typeBar.SelectedType; set => typeBar.SelectedType = value; }

	// Data refresh control
	public bool IsUpdating { get; set; }

	/* Fields */
	private readonly DXGridLayout layout;

	private readonly BuilderType typeBar;
	private readonly BuilderTeam teamBar;

	private readonly BuilderDimension outerBar;
	private readonly BuilderDimension innerBar;

	// Setup
	private ReportConfig reportConfig;
	private List<string> dimensions;

	// Refresh control
	private DateTimeOffset lastUpdate;

	// External ref
	private readonly ReportForm parent;

	/* Methods */
	public Builder( ReportForm parent )
	{
		this.parent = parent;

		BackgroundColor = DXColors.Dark1;

		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;
		
		bool tablet = DXDevice.IsTablet;

		// Underlying layout
		layout = new DXGridLayout
		{
			BackgroundColor = DXColors.Dark1,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill,
			
			IgnoreSafeArea = true
		};

		// Type selector
		typeBar = new BuilderType
		{
			TypeChanged = OnTypeChanged
		};

		// Team selector
		teamBar = new BuilderTeam
		{
			TeamChanged = OnTeamChanged
		};

		// Dimension 1
		outerBar = new BuilderDimension( DataDimension.Level.Outer )
		{
			DimensionChanged = OnOuterChanged,
			FilterChanged = OnFilterChanged
		};

		// Dimension 2
		innerBar = new BuilderDimension( DataDimension.Level.Inner )
		{
			DimensionChanged = OnInnerChanged
		};

		// Static layout on tablet
		if ( tablet )
		{
			Layout();
		}

		Content = layout;

		// Refresh control
		lastUpdate = DateTimeOffset.MinValue;
	}

	// Post-construction initialization
	public void Init( ReportConfig config )
	{
		reportConfig = config;

		IsUpdating = true;

		JsonDashboard json = config.Dashboard;

		// Team (default 1)
		teamBar.Init( json, config );

		// Type (default grid)
		typeBar.Init( json );

		// Save for later use
		dimensions = KeyDimension.GetDimensions( config, teamBar.IsTeam1 );

		IsUpdating = false;
	}

	// Returns dynamic height based on orientation
	public static double GetViewHt()
	{
		double pad = GetPad();

		const double ht = (SelectorHt + DimensionHt);
		double fullPad = (pad * 3) + (DXDevice.IsIOS ? 0 : 1);

		// Mobile portrait stacked 4 rows, all others 2
		return DXDevice.IsMobilePortrait() ? ((ht * 2) - AdjustHt + fullPad) : (ht + fullPad);
	}

	// Updates all reports (via parent) with current query configuration
	private async Task ForceUpdate( bool filter = false )
	{
		double elapsed = DXUtils.Now().Subtract( lastUpdate ).TotalMilliseconds;

		// Protect against redundant updates
		if ( !IsUpdating && (elapsed > UpdateInterval) )
		{
			IsUpdating = true;

			// Configure query
			DataConfig dataConfig = CreateConfig( filter );

			// Refresh data display
			await parent.UpdateData( dataConfig );

			IsUpdating = false;
			lastUpdate = DXUtils.Now();
		}
	}

	// Creates data configuration based on current builder settings
	private DataConfig CreateConfig( bool filter = false )
	{
		string selected = typeBar.SelectedType;

		// Trend segment also Momentum Chart depending on scope/data
		bool momentum = (selected == AnalyzeKeys.TrendKey) && (reportConfig.Scope == DataConfig.SetScope);

		return new DataConfig
		{
			Type = momentum ? AnalyzeKeys.MomentumKey : selected,
			Key = reportConfig.Key,

			DataSet = reportConfig.GetDataSet(),
			Action = reportConfig.GetAction(),

			Scope = reportConfig.Scope,
			ScopeObject = reportConfig.ScopeObject,

			Organization = reportConfig.Organization,
			IsTeam1 = teamBar.IsTeam1,

			Team1 = reportConfig.Team1,
			Team2 = reportConfig.Team2,

			OuterDim = outerBar.SelectedKey,
			InnerDim = innerBar.SelectedKey,

			OuterFilter = filter ? outerBar.Filter : null,
			InnerFilter = filter ? innerBar.Filter : null,

			PlayerAccessId = Shell.CurrentUser.GetPermission(),
		};
	}

	// Used for passing new data down into builder components
	public void UpdateData( DataConfig dataConfig, DataStats stats )
	{
		typeBar.UpdateData( dataConfig, stats );
		outerBar.UpdateData( dataConfig, stats );
		teamBar.UpdateData( stats );
	}

	// Configures builder for specified report type
	public void SelectType( string type )
	{
		switch ( type )
		{
			// Grid
			case AnalyzeKeys.GridKey:
			{
				EnableBoth();
				break;
			}
			// Shot/Heat/Bar/Trend
			case AnalyzeKeys.ShotKey:
			case AnalyzeKeys.HeatKey:
			{
				EnableOuter();
				break;
			}
			// Momentum
			case AnalyzeKeys.MomentumKey:
			{
				EnableNone();
				break;
			}
		}

		// Report can be exported unless excluded in config
		if ( parent.HasExport() )
		{
			Shell.Instance.ToolBar.AddCustom( 1, "export", "export.item" );
		}
	}

	// Enables both outer and inner dimension bars
	private void EnableBoth()
	{
		// Always outer dimension, inner filter TBD
		outerBar.Disable( false );

		outerBar.HasFilter = true;
		outerBar.AllowEmpty = true;

		bool isTeam1 = teamBar.IsTeam1;

		// Outer dimension defaults based on team
		outerBar.Load( isTeam1, dimensions );
		outerBar.SelectDefault( isTeam1 );

		bool hasInner = reportConfig.HasInner;

		// Inner defaults to NA
		innerBar.Disable( !hasInner );

		if ( hasInner )
		{
			innerBar.HasFilter = false;

			innerBar.Load( isTeam1, dimensions, outerBar.SelectedKey );
			innerBar.SelectDefault( isTeam1 );
		}
	}

	// Enables outer dimension bar only
	private void EnableOuter()
	{
		// Outer dimension only
		outerBar.Disable( false );
		innerBar.Disable( true );

		outerBar.HasFilter = true;
		innerBar.HasFilter = false;

		outerBar.AllowEmpty = true;

		bool isTeam1 = teamBar.IsTeam1;

		// Outer dimension defaults based on team
		outerBar.Load( isTeam1, dimensions );
		outerBar.SelectDefault( isTeam1 );

		// Inner NA
		innerBar.SelectedKey = null;
	}

	// Disables both outer and inner dimension bars
	private void EnableNone()
	{
		outerBar.SelectedKey = null;

		// No dimensions
		outerBar.Disable( true );
		innerBar.Disable( true );

		// No filters
		outerBar.HasFilter = false;
		innerBar.HasFilter = false;
	}

	/* Event Callbacks */

	// User changed report type
	private async void OnTypeChanged( string type )
	{
		SelectType( type );

		// Refresh
		await ForceUpdate();
	}

	// User changed team
	private async void OnTeamChanged( bool isTeam1 )
	{
		switch ( typeBar.SelectedType )
		{
			// Outer depends on team
			case AnalyzeKeys.GridKey:
			{
				outerBar.Load( isTeam1, dimensions );
				outerBar.SelectDefault( isTeam1 );

				// Inner always NA to start (may be disabled)
				if ( reportConfig.HasInner )
				{
					innerBar.Load( isTeam1, dimensions, outerBar.SelectedKey );
					innerBar.SelectDefault( isTeam1 );
				}

				break;
			}
			// Outer depends on team, inner always disabled
			case AnalyzeKeys.BarKey:
			case AnalyzeKeys.ShotKey:
			case AnalyzeKeys.HeatKey:
			case AnalyzeKeys.TrendKey:
			{
				outerBar.Load( isTeam1, dimensions );
				outerBar.SelectDefault( isTeam1 );

				innerBar.Disable( true );
				break;
			}
			// Nothing to do here
			case AnalyzeKeys.MomentumKey:
			{
				break;
			}
		}

		// Refresh
		await ForceUpdate();
	}

	// User changed selection of outer dimension combobox
	private async void OnOuterChanged( string filterKey )
	{
		switch ( typeBar.SelectedType )
		{
			// Grid
			case AnalyzeKeys.GridKey:
			{
				// Outer key removed from inner (default to NA, may be disabled)
				if ( reportConfig.HasInner )
				{
					bool isTeam1 = teamBar.IsTeam1;

					innerBar.Load( isTeam1, dimensions, filterKey );
					innerBar.SelectDefault( isTeam1 );
				}

				break;
			}
			// Bar/Shot/Heat
			case AnalyzeKeys.BarKey:
			case AnalyzeKeys.ShotKey:
			case AnalyzeKeys.HeatKey:
			{
				innerBar.Disable( true );
				break;
			}
		}

		// Refresh
		await ForceUpdate();
	}

	// User changed selection of inner dimension combobox
	private async void OnInnerChanged( string key )
	{
		// Refresh
		await ForceUpdate();
	}

	// User changed outer or inner dimension filter
	private async void OnFilterChanged()
	{
		await ForceUpdate( true );
	}

	/* Layout */

	// Device/orientation specific layout
	public override void UpdateLayout( LayoutType type )
	{
		// Mobile must completely re-layout based on orientation
		if ( DXDevice.IsMobile )
		{
			layout.ClearAll();

			switch ( type )
			{
				case LayoutType.MobileLandscape: MobileLandscape(); break;
				case LayoutType.MobilePortrait: MobilePortrait(); break;
			}
		}

		// Update children
		typeBar.UpdateLayout( type );
		teamBar.UpdateLayout( type );

		outerBar.UpdateLayout( type );
		innerBar.UpdateLayout( type );
	}
	
	// Mobile Landscape
	protected override void MobileLandscape()
	{
		double pad = GetPad();

		Padding = 0;

		layout.RowSpacing = pad;
		layout.ColumnSpacing = pad;

		// 2 rows		
		layout.AddFixedRow( SelectorHt );       // 0: type/team
		layout.AddFixedRow( DimensionHt );      // 1: dimensions

		// 3 columns
		layout.AddStarColumn( 50 );             // 0: type/dim1
		layout.AddStarColumn( 10 );             // 1: dim2
		layout.AddStarColumn( 40 );             // 2: team

		// Type
		layout.Add( typeBar, 0, 0, 2, 1 );

		// Team
		layout.Fill( DXColors.Dark3, 0, 2 );
		layout.Add( teamBar, 2, 0 );

		// Dimensions
		layout.Fill( DXColors.Light2, 0, 1 );
		layout.Add( outerBar, 0, 1 );
		layout.Add( innerBar, 1, 1, 2, 1 );
	}

	// Mobile Portrait
	protected override void MobilePortrait()
	{
		double pad = GetPad();
		Padding = new Thickness( 0, pad );

		layout.RowSpacing = pad;
		layout.ColumnSpacing = DXDevice.IsIOS ? pad : 2;

		// 5 rows		
		layout.AddFixedRow( SelectorHt );				// 0: type
		layout.AddFixedRow( SelectorHt - AdjustHt );    // 1: team
		layout.AddFixedRow( DimensionHt );				// 2: outer dim
		layout.AddFixedRow( DimensionHt );              // 3: inner dim

		// 1 column
		layout.AddStarColumn();

		// Type
		layout.Add( typeBar, 0, 0 );

		// Team
		layout.Fill( DXColors.Dark3, 0, 1 );
		layout.Add( teamBar, 0, 1 );
		
		// Dimensions
		layout.Add( outerBar, 0, 2 );
		layout.Add( innerBar, 0, 3 );
	}
	
	// All others
	private void Layout()
	{
		bool ios = DXDevice.IsIOS;

		double pad = GetPad();
		Padding = new Thickness( 0, pad );

		layout.RowSpacing = pad;
		layout.ColumnSpacing = ios ? pad : 2;

		// 2 rows		
		layout.AddFixedRow( SelectorHt );       // 0: type/team
		layout.AddFixedRow( DimensionHt );      // 1: dimensions

		// 3 columns
		layout.AddStarColumn( 50 );             // 0: type/dim1
		layout.AddStarColumn( 10 );             // 1: dim2
		layout.AddStarColumn( 40 );             // 2: team

		// Type
		layout.Add( typeBar, 0, 0, 2, 1 );

		// Team
		layout.Fill( DXColors.Dark3, 0, 2 );
		layout.Add( teamBar, 2, 0 );

		// Dimensions
		layout.Add( outerBar, 0, 1 );
		layout.Add( innerBar, 1, 1, 2, 1 );
	}

	// Returns platform/orientation specific padding
	private static double GetPad()
	{
		return DXDevice.IsMobilePortrait() ? 1 : (DXDevice.IsIOS ? 1 : 0);
	}
}

//
