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

using CommunityToolkit.Mvvm.Messaging;

using DXLib.UI;
using DXLib.UI.Alert;
using DXLib.UI.Layout;
using DXLib.UI.Control;
using DXLib.UI.Container;

using DXLib.UI.Form;
using DXLib.UI.Form.Control;
using DXLib.UI.Form.Message;

using DXLib.Utils;

namespace iStatVball3;

/*
 * Main content panel for the settings drawer. Includes a control list or link for each major group of settings. All
 * settings values will be updated when the drawer is closed. 
 */
public class SettingsContent : DXScroll
{
	/* Constants */
	public static readonly Thickness GroupPadding = new( 24, 24, 24, 0 );

	/* Fields */
	private readonly ToolBarDrawer drawer;
	private readonly Settings settings;

	// Layout
	private readonly DXVerticalLayout layout;

	// Recording
	private DXFormControlList recordGroup;

	private DXSelectorField recordEngine;
	private DXSelectorField recordPass;
	private DXSelectorField recordSwitch;

	// RallyFlow
	private DXFormControlList rallyGroup;

	private DXSelectorField rallyLevel;
	private DXCheckboxField rallyAuto;
	private DXCheckboxField rallyPreview;

	// Custom Options (RallyFlow)
	private DXFormControlList customGroup;

	private Dictionary<string,DXLinkField> customLinks;
	private SettingsContentCustom contentCustomOptions;
	private bool customHasChanges;

	// Player Grid (RallyFlow)
	private DXFormControlList gridGroup;

	private DXSelectorField gridDisplay;
	private DXSelectorField gridOrder;
	private DXCheckboxField gridTitle;

	// Legacy
	private DXFormControlList legacyGroup;

	private DXCheckboxField legacyFocus;
	private DXCheckboxField legacySmart;

	private DXSelectorField legacySet;
	private DXSelectorField legacyServe;

	private DXCheckboxField legacyPass0;
	private DXCheckboxField legacyRecv;

	// Smart Locations
	private DXFormControlList smartGroup;

	private DXSelectorField smartLibero;
	private DXCheckboxField smartAI;

	// Substitutions
	private DXFormControlList subsGroup;

	private DXSelectorField subsMax;
	private DXStepper subsWarn;

	// Timeouts
	private DXFormControlList timeoutGroup;

	private DXStepper timeoutMax;
	private DXSelectorField timeoutSec;

	// Analyze
	private DXFormControlList analyzeGroup;

	private DXSelectorField analyzePlayer;
	private DXStepper analyzeDigits;
	private DXCheckboxField analyzeScrimmage;

    // Export
    private DXFormControlList exportGroup;

    private DXSelectorField exportEmail;
    private DXSelectorField exportFormat;
    private DXSelectorField exportMaxPreps;

    // General
    private DXFormControlList generalGroup;

    private DXSelectorField generalSort;
    private DXSelectorField generalAdd;
    private DXCheckboxField generalFilter;
    private DXCheckboxField generalSample;

	/* Methods */
	public SettingsContent( ToolBarDrawer drawer )
	{
		this.drawer = drawer;
		
		// Access remote settings
		settings = Shell.Settings;

		BackgroundColor = DXColors.Light4;
		
		Padding = 0;
		Margin = 0;

		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;
		
		// Layout 
		layout = new DXVerticalLayout
		{
			BackgroundColor = DXColors.Light4,

			Padding = new Thickness( 0, 0, 0, DXDevice.IsIOS ? 10 : 30 ),
			Margin = 0,
			Spacing = 0,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill,
			
			IsClippedToBounds = false,
			IgnoreSafeArea = true
		};
		
		// Force full width on mobile landscape
		AdjustForSafeArea( layout );
		
		// Add base settings groups
		AddRecord();
		AddSmart();
		AddSubs();
		AddTimeout();
		AddAnalyze();
		AddExport();
		AddGeneral();

		bool rally = settings.IsRally;

		// RallyFlow/Legacy are dynamic
		AddRally( rally );
		AddLegacy( !rally );

		Content = layout;

        // Initial state
        customHasChanges = false;

        // Register listener
        WeakReferenceMessenger.Default.Register<DXFormChangedMessage>( this, OnControlChanged );
        drawer.Closed = OnClosed;
	}

	// Settings drawer closed
	private async void OnClosed()
	{
		// Ignore if no changes
		if ( HasChanges() )
		{
			DXSpinner.Start();

			// Persist
			await SaveAll();

			// Some changes dynamically update UI
			UpdateUI();

			DXSpinner.Stop();
		}

		// Unregister listener
		WeakReferenceMessenger.Default.UnregisterAll( this );
		
		drawer.Hide();
	}

	// Determines if any setting value has been changed
	private bool HasChanges()
	{
		bool record = recordGroup.HasChanges();
		bool rally = RallyHasChanges();
		bool legacy = LegacyHasChanges();
		bool smart = smartGroup.HasChanges();
		bool subs = subsGroup.HasChanges();
		bool timeout = timeoutGroup.HasChanges();
		bool analyze = analyzeGroup.HasChanges();
		bool export = exportGroup.HasChanges();
		bool general = generalGroup.HasChanges();

		return record || rally || legacy || smart || subs || timeout || analyze || export || general;
	}

	// Persists all setting group changes
	private async Task SaveAll()
	{
		SaveRecord();
		SaveRally();
		SaveLegacy();
		SaveSmart();
		SaveSubs();
		SaveTimeout();
		SaveAnalyze();
		SaveExport();
		SaveGeneral();

		// Persist
		await Shell.CurrentUser.UpdateSettings();
	}

	// Update UI to reflect changes
	private void UpdateUI()
	{
		Shell shell = Shell.Instance;

		// RallyFlow settings may have changed
		if ( RallyHasChanges() )
		{
			RecordForm record = shell.RecordForm;

			if ( record is { IsVisible: true } )
			{
				record.UpdateLayout();
			}
		}

		// Card order or sample org may have changed
		if ( generalGroup.HasChanges() )
		{
			PageStack pages = shell.PageStack;

			if ( pages.IsVisible )
			{
				pages.Refresh();
			}
		}
	}

	/* Event Callbacks */

	// User tapped back on drawer sub-screen
	private void OnReturned()
	{
		if ( contentCustomOptions != null )
		{
			SaveCustom();
			
			drawer.Previous();
		}
	}

	// Called back when any settings control changes value
	private void OnControlChanged( object sender, DXFormChangedMessage msg )
	{
		DXFormControl control = msg.Control;
		
		switch ( control.Key )
		{
			// Switching between RallyFlow/Legacy
			case "record.engine":
			{
				UpdateEngine( control.GetObject() as string );
				break;
			}
			// Changing pass rating scale
			case "record.pass":
			{
				UpdatePass();
				break;
			}
			// Custom RallyFlow level shows additional options
			case "rally.level":
			{
				UpdateLevel( control.GetObject() as string );
				break;
			}
			// Smart Locations toggled (RallyFlow)
			case "grid.order":
			{
				UpdateSmart( true, (control.GetObject() as string) == "smart" );
				break;
			}
			// Auto Focus toggled (Legacy)
			case "legacy.focus":
			{
				UpdateFocus( (bool)control.GetObject() );
				break;
			}
			// Smart Locations toggled (Legacy)
			case "legacy.smart":
			{
				UpdateSmart( false, (bool)control.GetObject() );
				break;
			}
			// Changing maximum substitutions
			case "subs.max":
			{
				UpdateSubs( control.GetObject() as string );
				break;
			}
			// Changing maximum timeouts
			case "timeout.max":
			{
				UpdateTimeout( ((DXStepper)control).Number );
				break;
			}
		}
	}

	/* Recording */

	// Adds all settings for Recording group
	private void AddRecord()
	{
		// Group header
		recordGroup = new DXFormControlList( false )
		{
			Padding = GroupPadding,
			Title = "settings.record",
			Color = DXColors.Light4
		};

		// Engine
		recordEngine = new DXSelectorField
		{
			Key = "record.engine",
			Title = "settings.record.engine",
			Items = "settings.record.engine",
			SelectedItem = settings.RecordEngine,
			Help = "settings.record.engine",
			HideClear = true
		};

		recordGroup.Add( recordEngine, false, false );

		// Passing
		recordPass = new DXSelectorField
		{
			Key = "record.pass",
			Title = "settings.record.pass",
			Items = "settings.record.pass",
			SelectedItem = settings.RecordPass,
			Help = "settings.record.pass",
			HideClear = true
		};

		recordGroup.Add( recordPass, false, false );

        // Switch sides
        recordSwitch = new DXSelectorField
        {
            Key = "record.switch",
            Title = "settings.record.switch",
            Items = "settings.record.switch",
            SelectedItem = settings.RecordSwitch,
            Help = "settings.record.switch",
            HideClear = true
        };

        recordGroup.Add( recordSwitch, false, false );

		// Init
		recordGroup.Init();

		layout.Add( recordGroup.View );
	}

	// Saves all settings for Recording group
	private void SaveRecord()
	{
		settings.RecordEngine = recordEngine.GetString();
		settings.RecordPass = recordPass.GetString();
        settings.RecordSwitch = recordSwitch.GetString();
	}

	// Switch between RallyFlow/Legacy engines
	private void UpdateEngine( string engine )
	{
		bool rally = (engine == Settings.RallyEngine);

		AddRally( rally );
		AddLegacy( !rally );

		// Machine Learning NA for Legacy
		if ( smartGroup.GetControl( "smart.ai" ) is DXCheckboxField field )
		{
			field.IsDisabled = !rally;
			field.Checked = rally;
		}

		// Legacy Pass0 affected by engine
		UpdatePass();
	}

	// Updates fields dependent on 0-3/4 pass rating option
	private void UpdatePass()
	{
		if ( recordGroup.GetControl( "record.engine" ) is DXSelectorField engine )
		{
			// 'Pass-0 ends rally' NA for 0-4 rating scale
			if ( engine.GetObject() is Settings.LegacyEngine )
			{
				DXSelectorField pass = recordGroup.GetControl( "record.pass" ) as DXSelectorField;
				DXCheckboxField pass0 = legacyGroup.GetControl( "legacy.pass0" ) as DXCheckboxField;

				if ( pass?.GetObject() is string passKey )
				{
					if ( pass0 != null )
					{
						pass0.Checked = true;
						pass0.IsDisabled = (passKey == Settings.Pass04Key);
					}
				}
			}
		}
	}

	/* RallyFlow */

	// Adds/removes all settings for RallyFlow group
	private void AddRally( bool add )
	{
		// Dynamically add
		if ( add )
		{
			// Lazily create
			if ( rallyGroup == null )
			{
				// Group header
				rallyGroup = new DXFormControlList( false )
				{
					Padding = GroupPadding,
					Title = "settings.rally",
					Color = DXColors.Light4
				};

				// Detail Level
				rallyLevel = new DXSelectorField
				{
					Key = "rally.level",
					Title = "settings.rally.level",
					Items = "settings.rally.level",
					SelectedItem = settings.RallyLevel,

					Help = "settings.rally.level",
					HelpWd = 500,
					HelpHt = 550,

					HideClear = true
				};

				rallyGroup.Add( rallyLevel, false, false );

				string level = settings.RallyLevel;

				// Auto serve/pass rating
				rallyAuto = new DXCheckboxField
				{
					IsDisabled = (level != Settings.LowKey) && (level != Settings.MediumKey),

					Key = "rally.auto",
					Text = "settings.rally.auto",
					Checked = settings.RallyAuto,
					Help = "settings.rally.auto"
				};

				rallyGroup.Add( rallyAuto, false, false );

				// Lineup preview
				rallyPreview = new DXCheckboxField
				{
					Key = "rally.preview",
					Text = "settings.rally.preview",
					Checked = settings.RallyPreview,
					Help = "settings.rally.preview"
				};

				rallyGroup.Add( rallyPreview, false, false );

				// Init
				rallyGroup.Init();

				// Player Grid
				AddGrid();
			}

			// Custom Options
			if ( customGroup == null )
			{
				AddCustom();
			}

			DXFormControl control = rallyGroup.GetControl( "rally.level" );
			bool custom = (control.GetObject() as string) == Settings.CustomKey;

			// Add to layout
			layout.Insert( 1, rallyGroup.View );
			
			if ( custom )
			{
				layout.Insert( 2, customGroup?.View );
			}
			
			layout.Insert( (custom ? 3 : 2), gridGroup.View );
		}
		// Remove
		else
		{
			if ( rallyGroup != null )
			{
				layout.Remove( rallyGroup.View );
				layout.Remove( customGroup.View );
				layout.Remove( gridGroup.View );
			}
		}
	}

	// Determines if any RallyFlow related setting has been changed
	private bool RallyHasChanges()
	{
		return (rallyGroup != null) && (rallyGroup.HasChanges() || gridGroup.HasChanges() || customHasChanges);
	}

	// Saves all top-level RallyFlow settings
	private void SaveRally()
	{
		if ( rallyGroup != null )
		{
			settings.RallyLevel = rallyLevel.GetObject() as string;
			settings.RallyAuto = (bool)rallyAuto.GetObject();
			settings.RallyPreview = (bool)rallyPreview.GetObject();

			// Player Grid (Custom already saved)
			SaveGrid();
		}
	}

	// Updates RallyFlow detail level
	private void UpdateLevel( string level )
	{
		RecordForm record = Shell.Instance.RecordForm;

		// Cannot change up from LOW during set without lineup
		if ( record != null )
		{
			if ( record.Set.IsInProgress && record.StateMachine.Lineup1.IsEmpty() && (level != Settings.LowKey) )
			{
				DXAlert.ShowError( "settings.rally.level.err", drawer.Hide );
				return;
			}
		}

		// Show custom options
		if ( level == Settings.CustomKey )
		{
			AddCustom();

			layout.Insert( 2, customGroup.View );
		}
		// Show level-specific options
		else
		{
			layout.Remove( customGroup.View );

			// (Un)check options based on level
			settings.SetLevel( level );
		}

		// Auto rate NA for some levels
		if ( rallyGroup.GetControl( "rally.auto" ) is DXCheckboxField rallyAutoField )
		{
			rallyAutoField.Checked = settings.RallyAuto;
			rallyAutoField.IsDisabled = level is Settings.HighKey or Settings.MaximumKey;
		}
	}

	/* Custom Options (RallyFlow) */

	// Adds all settings for Custom Options group
	private void AddCustom()
	{
		if ( customGroup == null )
		{
			// Group header
			customGroup = new DXFormControlList( false )
			{
				Padding = GroupPadding,
				Title = "settings.custom",
				Color = DXColors.Light4
			};

			// Custom option links
			customLinks = new Dictionary<string, DXLinkField>
			{
				[Settings.QuickKey] = CreateLink( Settings.QuickKey ),
				[Settings.RatingKey] = CreateLink( Settings.RatingKey ),
				[Settings.ModifierKey] = CreateLink( Settings.ModifierKey ),
				[Settings.FaultKey] = CreateLink( Settings.FaultKey )
			};

			// Add all to control list
			foreach ( DXLinkField link in customLinks.Values )
			{
				customGroup.Add( link, false, false );
			}

			// Init
			customGroup.Init();
		}
	}

	// Saves all settings for Custom Option sub-screens
	private void SaveCustom()
	{
		string key = contentCustomOptions.Key;

		List<string> selected = contentCustomOptions.GetSelectedKeys();
		List<string> selectedOpp = contentCustomOptions.GetSelectedKeys( true );

		// Temp save primary/opponent options from current action
		settings.RallyCustom.SetList( key, contentCustomOptions.GetSelectedKeys() );
		settings.RallyCustomOpp.SetList( key, contentCustomOptions.GetSelectedKeys( true ) );

		IList<string> quick = settings.RallyCustom.GetList( Settings.QuickKey );
		IList<string> quickOpp = settings.RallyCustomOpp.GetList( Settings.QuickKey );

		// Some options affect others
		switch ( key )
		{
			// Turning QuickSelect ON turns related Rating/Modifier OFF
			case Settings.QuickKey:
			{
				RemoveQuickKeys( selected );
				RemoveQuickKeys( selectedOpp, true );
				break;
			}
			// Turning Rating ON turns related QuickSelect OFF
			case Settings.RatingKey:
			{
				RemoveRateKeys( selected, quick );
				RemoveRateKeys( selectedOpp, quickOpp );
				break;
			}
			// Turning Modifier ON turns matching QuickSelect OFF
			case Settings.ModifierKey:
			{
				RemoveModKeys( selected, quick );
				RemoveModKeys( selectedOpp, quickOpp );
				break;
			}
		}

		contentCustomOptions = null;
		customHasChanges = true;
	}

	// Creates link field for a Custom Option
	private DXLinkField CreateLink( string key )
	{
		return new DXLinkField
		{
			Key = $"custom.{key}",
			Text = $"settings.custom.{key}",
			Help = $"settings.custom.{key}",

			LinkTapped = OnLinkTapped
		};
	}

	// Transitions to drawer sub-screen on link tap
	private void OnLinkTapped( object sender )
	{
		if ( sender is DXLinkField field )
		{
			string key = field.Key;
			string viewKey = key[ (key.LastIndexOf( '.' ) + 1).. ];

			string title = $"settings.{key}";

			// Create sub-screen
			contentCustomOptions = CreateCustomView( viewKey );
			
			// Transition
			drawer.Next( title, contentCustomOptions, OnReturned );
		}
	}

	// Creates link sub-screen for a Custom Option
	private SettingsContentCustom CreateCustomView( string key )
	{
		return new SettingsContentCustom( key )
		{
			Selected = settings.RallyCustom.GetList( key ),
			SelectedOpp = settings.RallyCustomOpp.GetList( key ),

			SelectionChanged = OnLinkChanged
		};
	}

	// Marks link field as having changes
	private void OnLinkChanged( string key )
	{
		customLinks[ key ].HasChanges = true;
	}
	
	/* Remove */
	
	// Turns OFF related Rating/Modifier for selected QuickSelect actions
	private void RemoveQuickKeys( IList<string> selected, bool opponent = false )
	{
		IList<string> rate = opponent ? settings.RallyCustomOpp.GetList( Settings.RatingKey ) : settings.RallyCustom.GetList( Settings.RatingKey );
		IList<string> mod = opponent ? settings.RallyCustomOpp.GetList( Settings.ModifierKey ) : settings.RallyCustom.GetList( Settings.ModifierKey );

		// QuickSelect 2nd ON turns ALL Pass Ratings and Modifier 2nd OFF
		if ( selected.Contains( Stats.SecondKey ) )
		{
			rate.Remove( Stats.ReceiveKey );
			rate.Remove( Stats.FirstKey );
			rate.Remove( Stats.DefenseKey );
			rate.Remove( Stats.FreeballKey );
			rate.Remove( Stats.PutbackKey );

			mod.Remove( Stats.SecondKey );
		}

		// QuickSelect 3rd ON turns Rating 2nd (set) and Modifier 3rd OFF
		if ( selected.Contains( Stats.ThirdKey ) )
		{
			rate.Remove( Stats.SecondKey );
			mod.Remove( Stats.ThirdKey );
		}
	}

	// Turns OFF related QuickSelect for each selected Rating action
	private static void RemoveRateKeys( IList<string> selected, IList<string> list )
	{
		foreach ( string key in selected )
		{
			// Set rating requires Third Ball
			if ( key == Stats.SecondKey )
			{
				list.Remove( Stats.ThirdKey );
			}
			// ALL Pass ratings require Second Ball
			else
			{
				list.Remove( Stats.SecondKey );
			}
		}
	}

	// Turns OFF QuickSelect for each selected Modifier action
	private static void RemoveModKeys( IList<string> selected, IList<string> list )
	{
		foreach ( string key in selected )
		{
			list.Remove( key );
		}
	}

	/* Player Grid (RallyFlow) */

	// Adds all settings for Player Grid group
	private void AddGrid()
	{
		// Group header
		gridGroup = new DXFormControlList( false )
		{
			Padding = GroupPadding,
			Title = "settings.grid",
			Color = DXColors.Light4
		};

		// Display type
		gridDisplay = new DXSelectorField
		{
			Key = "grid.display",
			Title = "settings.grid.display",
			Items = "settings.grid.display",
			SelectedItem = settings.GridDisplay,
			Help = "settings.grid.display",
			HideClear = true
		};

		gridGroup.Add( gridDisplay, false, false );

		// Ordering
		gridOrder = new DXSelectorField
		{
			Key = "grid.order",
			Title = "settings.grid.order",
			Items = "settings.grid.order",
			SelectedItem = settings.GridOrder,
			Hint = DXFormControl.HintType.None,
			Help = "settings.grid.order",
			HideClear = true
		};

		gridGroup.Add( gridOrder, false, false );

		// Show action title?
		gridTitle = new DXCheckboxField
		{
			Key = "grid.title",
			Text = "settings.grid.title",
			Checked = settings.GridTitle,
			Help = "settings.grid.title"
		};

		gridGroup.Add( gridTitle, false, false );

		// Init
		gridGroup.Init();
	}

	// Saves all settings for Player Grid group
	private void SaveGrid()
	{
		settings.GridDisplay = gridDisplay.GetObject() as string;
		settings.GridOrder = gridOrder.GetObject() as string;
		settings.GridTitle = (bool) gridTitle.GetObject();
	}

	/* Legacy */

	// Adds all settings for Legacy group
	private void AddLegacy( bool add )
	{
		if ( add )
		{
			if ( legacyGroup == null )
			{
				// Group header
				legacyGroup = new DXFormControlList( false )
				{
					Padding = GroupPadding,
					Title = "settings.legacy",
					Color = DXColors.Light4
				};

				// Auto Focus
				legacyFocus = new DXCheckboxField
				{
					Key = "legacy.focus",
					Text = "settings.legacy.focus",
					Checked = settings.LegacyFocus,
					Help = "settings.legacy.focus"
				};

				legacyGroup.Add( legacyFocus, false, false );

				// Smart Locations
				legacySmart = new DXCheckboxField
				{
					Key = "legacy.smart",
					Text = "settings.legacy.smart",
					Checked = settings.LegacySmart,
					Help = "settings.legacy.smart"
				};

				legacyGroup.Add( legacySmart, false, false );

				// Auto Set
				legacySet = new DXSelectorField
				{
					Key = "legacy.set",
					Title = "settings.legacy.set",
					Items = "settings.legacy.set",
					SelectedItem = settings.LegacySet,
					Help = "settings.legacy.set",
					HideClear = true
				};

				legacyGroup.Add( legacySet, false, false );

				// Serve Format
				legacyServe = new DXSelectorField
				{
					Key = "legacy.serve",
					Title = "settings.legacy.serve",
					Items = "settings.legacy.serve",
					SelectedItem = settings.LegacyServe,
					Help = "settings.legacy.serve",
					HideClear = true
				};

				legacyGroup.Add( legacyServe, false, false );

				// Pass-0 Ends Rally
				legacyPass0 = new DXCheckboxField
				{
					Key = "legacy.pass0",
					Text = "settings.legacy.pass0",
					Checked = settings.LegacyPass0,
					Help = "settings.legacy.pass0",

					IsDisabled = settings.IsPass04
				};

				legacyGroup.Add( legacyPass0, false, false );

				// Pass Recv Only
				legacyRecv = new DXCheckboxField
				{
					Key = "legacy.recv",
					Text = "settings.legacy.recv",
					Checked = settings.LegacyRecv,
					Help = "settings.legacy.recv"
				};

				legacyGroup.Add( legacyRecv, false, false );

				// Init
				legacyGroup.Init();
			}

			layout.Insert( 1, legacyGroup.View );
		}
		else
		{
			if ( legacyGroup != null )
			{
				layout.Remove( legacyGroup.View );
			}
		}
	}

	// Determines if any Legacy related setting has been changed
	private bool LegacyHasChanges()
	{
		return (legacyGroup != null) && legacyGroup.HasChanges();
	}

	// Saves all settings for Legacy group
	private void SaveLegacy()
	{
		if ( legacyGroup != null )
		{
			settings.LegacyFocus = (bool) legacyFocus.GetObject();
			settings.LegacySmart = (bool) legacySmart.GetObject();

			settings.LegacySet = legacySet.GetObject() as string;
			settings.LegacyServe = legacyServe.GetObject() as string;

			settings.LegacyPass0 = (bool)legacyPass0.GetObject();
			settings.LegacyRecv = (bool)legacyRecv.GetObject();
		}
	}

	// User toggled Auto Focus setting
	private void UpdateFocus( bool on )
	{
		// 'Pass on recv only' requires Auto Focus
		if ( legacyGroup.GetControl( "legacy.recv" ) is DXCheckboxField recv )
		{
			recv.IsDisabled = !on;
			recv.Checked = false;
		}
	}

	/* Smart Locations */

	// Adds all settings for Smart Locations group
	private void AddSmart()
	{
		// Group header
		smartGroup = new DXFormControlList( false )
		{
			Padding = GroupPadding,
			Title = "settings.smart",
			Color = DXColors.Light4
		};

		// Libero
		smartLibero = new DXSelectorField
		{
			Key = "smart.libero",
			Title = "settings.smart.libero",
			Items = "settings.smart.libero",
			SelectedItem = settings.SmartLibero,
			Hint = DXFormControl.HintType.None,
			Help = "settings.smart.libero",
			HideClear = true
		};

		smartGroup.Add( smartLibero, false, false );

		bool valid = settings.IsRally && (settings.GridOrder == "smart");

		// AI
		smartAI = new DXCheckboxField
		{
			IsDisabled = !valid,

			Key = "smart.ai",
			Text = "settings.smart.ai",
			Checked = settings.SmartAI,
			Help = "settings.smart.ai"
		};

		smartGroup.Add( smartAI, false, false );

		// Init
		smartGroup.Init();

		layout.Add( smartGroup.View );
	}

	// Saves all settings for Smart Locations group
	private void SaveSmart()
	{
		settings.SmartLibero = smartLibero.GetString();
		settings.SmartAI = (bool) smartAI.GetObject();
	}

	// Updates Smart Locations states based on engine
	private void UpdateSmart( bool rally, bool smart )
	{
		smartGroup.IsDisabled = !smart;

		// Libero
		if ( smartGroup.GetControl( "smart.libero" ) is DXSelectorField liberoField )
		{
			liberoField.IsDisabled = !smart;
		}

		bool valid = (rally && smart);

		// AI
		if ( smartGroup.GetControl( "smart.ai" ) is DXCheckboxField aiField )
		{
			aiField.IsDisabled = !valid;
			aiField.Checked = valid;
		}
	}

	/* Substitutions */

	// Adds all settings for Substitutions group
	private void AddSubs()
	{
		// Group header
		subsGroup = new DXFormControlList( false )
		{
			Padding = GroupPadding,
			Title = "settings.subs",
			Color = DXColors.Light4
		};

		int max = settings.SubsMax;

		// Maximum
		subsMax = new DXSelectorField
		{
			Key = "subs.max",
			Title = "settings.subs.max",
			Items = "settings.subs.max",
			SelectedItem = max.ToString(),
			Help = "settings.subs.max",
			HideClear = true
		};

		subsGroup.Add( subsMax, false, false );

		// Warning
		subsWarn = new DXStepper
		{
			IsDisabled = (max == 0),

			Key = "subs.warn",
			Title = "settings.subs.warn",
			Number = settings.SubsWarn,
			MinValue = 0,
			MaxValue = 5,
			Step = 1,
			IsLooping = true,
			Help = "settings.subs.warn"
		};

		subsGroup.Add( subsWarn, false, false );

		// Init
		subsGroup.Init();

		layout.Add( subsGroup.View );
	}

	// Saves all settings for Substitutions group
	private void SaveSubs()
	{
		settings.SubsMax = (int) DXUtils.ConvertToInt( subsMax.GetString() )!;
		settings.SubsWarn = subsWarn.Number;
	}

	// Disables subs warning if unlimited subs selected
	private void UpdateSubs( string value )
	{
		subsWarn.IsDisabled = (value == "0");
	}

	/* Timeout */

	// Adds all settings for Timeout group
	private void AddTimeout()
	{
		// Group header
		timeoutGroup = new DXFormControlList( false )
		{
			Padding = GroupPadding,
			Title = "settings.timeout",
			Color = DXColors.Light4
		};

		int max = settings.TimeoutMax;

		// Maximum
		timeoutMax = new DXStepper
		{
			Key = "timeout.max",
			Title = "settings.timeout.max",
			Number = max,
			MinValue = 0,
			MaxValue = 4,
			Step = 1,
			IsLooping = true,
			Help = "settings.timeout.max"
		};

		timeoutGroup.Add( timeoutMax, false, false );

		// Seconds
		timeoutSec = new DXSelectorField
		{
			Key = "timeout.sec",
			Title = "settings.timeout.sec",
			Items = "settings.timeout.sec",
			SelectedItem = settings.TimeoutSec.ToString(),
			Help = "settings.timeout.sec",
			HideClear = true,

			IsDisabled = (max == 0)
		};

		timeoutGroup.Add( timeoutSec, false, false );

		// Init
		timeoutGroup.Init();

		layout.Add( timeoutGroup.View );
	}

	// Saves all settings for Timeout group
	private void SaveTimeout()
	{
		settings.TimeoutMax = timeoutMax.Number;
		settings.TimeoutSec = (int) DXUtils.ConvertToInt( timeoutSec.GetObject() as string )!;
	}

	// Disables timeout duration if 0 timeouts selected
	private void UpdateTimeout( int max )
	{
		timeoutSec.IsDisabled = (max == 0);
	}

	/* Analyze */

	// Adds all settings for Analyze group
	private void AddAnalyze()
	{
		// Group header
		analyzeGroup = new DXFormControlList( false )
		{
			Padding = GroupPadding,
			Title = "settings.analyze",
			Color = DXColors.Light4
		};

        // Player display
        analyzePlayer = new DXSelectorField
		{
			Key = "analyze.player",
			Title = "settings.analyze.player",
			Items = "settings.analyze.player",
			SelectedItem = settings.AnalyzePlayer,
			Help = "settings.analyze.player",
			HideClear = true
		};

		analyzeGroup.Add( analyzePlayer, false, false );

		// Decimal digits
		analyzeDigits = new DXStepper
		{
			Key = "analyze.digits",
			Title = "settings.analyze.digits",
			Number = settings.AnalyzeDigits,
			MinValue = 0,
			MaxValue = 3,
			Step = 1,
			IsLooping = true,
			Help = "settings.analyze.digits"
		};

		analyzeGroup.Add( analyzeDigits, false, false );

		// Include scrimmages?
		analyzeScrimmage = new DXCheckboxField
		{
			Key = "analyze.scrimmage",
			Text = "settings.analyze.scrimmage",
			Checked = settings.AnalyzeScrimmage,
			Help = "settings.analyze.scrimmage"
		};

		analyzeGroup.Add( analyzeScrimmage, false, false );

		// Init
		analyzeGroup.Init();

		layout.Add( analyzeGroup.View );
	}

	// Saves all settings for Analyze group
	private void SaveAnalyze()
	{
        settings.AnalyzePlayer = analyzePlayer.GetString();
		settings.AnalyzeDigits = (byte)analyzeDigits.Number;
		settings.AnalyzeScrimmage = (bool) analyzeScrimmage.GetObject();
	}

    /* Export */

	// Adds all settings for Export group
	private void AddExport()
	{
        // Group header
        exportGroup = new DXFormControlList( false )
        {
            Padding = GroupPadding,
            Title = "settings.export",
            Color = DXColors.Light4
        };

        // Email mode
        exportEmail = new DXSelectorField
        {
            Key = "export.email",
            Title = "settings.export.email",
            Items = "settings.export.email",
            SelectedItem = settings.ExportEmail,
            Help = "settings.export.email",
            HideClear = true
        };

        exportGroup.Add( exportEmail, false, false );

        // File format
        exportFormat = new DXSelectorField
        {
            Key = "export.format",
            Title = "settings.export.format",
            Items = "settings.export.format",
            SelectedItem = settings.ExportFormat,
            Help = "settings.export.format",
            HideClear = true
        };

        exportGroup.Add( exportFormat, false, false );

        // MaxPreps
        exportMaxPreps = new DXSelectorField
        {
            Key = "export.maxpreps",
            Title = "settings.export.maxpreps",
            Items = "settings.export.maxpreps",
            SelectedItem = settings.ExportMaxPreps,
            Help = "settings.export.maxpreps",
            HideClear = true
        };

        exportGroup.Add( exportMaxPreps, false, false );

        // Init
        exportGroup.Init();

        layout.Add( exportGroup.View );
    }

    // Saves all settings for Export group
    private void SaveExport()
    {
        settings.ExportEmail = exportEmail.GetString();
        settings.ExportFormat = exportFormat.GetString();
		settings.ExportMaxPreps = exportMaxPreps.GetString();
    }

    /* General */

    // Adds all settings for General group
    private void AddGeneral()
	{
		// Group header
		generalGroup = new DXFormControlList( false )
		{
			Padding = GroupPadding,
			Title = "settings.general",
			Color = DXColors.Light4
		};

		// Roster sort order
		generalSort = new DXSelectorField
		{
			Key = "general.sort",
			Title = "settings.general.sort",
			Items = "settings.general.sort",
			SelectedItem = settings.GeneralSort,
			Help = "settings.general.sort",
			HideClear = true
		};

		generalGroup.Add( generalSort, false, false );

		// 'Add New' location
		generalAdd = new DXSelectorField
		{
			Key = "general.add",
			Title = "settings.general.add",
			Items = "settings.general.add",
			SelectedItem = settings.GeneralAdd,
			Help = "settings.general.add",
			HideClear = true
		};

		generalGroup.Add( generalAdd, false, false );

		// Filter Tournaments
		generalFilter = new DXCheckboxField
		{
			Key = "general.filter",
			Text = "settings.general.filter",
			Checked = settings.GeneralFilter,
			Help = "settings.general.filter"
		};

		generalGroup.Add( generalFilter, false, false );

		// Show Sample
		generalSample = new DXCheckboxField
		{
			Key = "general.sample",
			Text = "settings.general.sample",
			Checked = settings.GeneralSample,
			Help = "settings.general.sample"
		};

		generalGroup.Add( generalSample, false, false );

		// Init
		generalGroup.Init();

		layout.Add( generalGroup.View );
	}

	// Saves all settings for General group
	private void SaveGeneral()
	{
		settings.GeneralSort = generalSort.GetString();
		settings.GeneralAdd = generalAdd.GetString();

		settings.GeneralFilter = (bool) generalFilter.GetObject();
		settings.GeneralSample = (bool) generalSample.GetObject();
	}
}

//
