/* 
 * 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.Form;
using DXLib.UI.Alert;
using DXLib.UI.Card;
using DXLib.UI.Layout;
using DXLib.UI.Container;

using DXLib.Utils;

namespace iStatVball3;

/*
 * Implements a form panel for editing raw tally stats for a set. The form is embedded in a parent SetEditForm. The form
 * contains a list of roster players, a header showing the currently selected player, and then a grid of controls for
 * entering/editing tally stats for that player.
 */
public class EditTallyForm : DXContent
{
	/* Properties */
	public Color DefaultColor { set => roster.Color = value; }

	/* Fields */
	private readonly DXGridLayout layout;
	private readonly DXFormButtons buttons;

	private readonly LineupMenuView roster;

	private readonly EditTallyHeader header;
	private readonly EditTallyView view;

	// Parent reference
	private Set tallySet;

	// State control
	private readonly bool editMode;
	private Player selectedPlayer;

	// Raw summary and metrics stats
	private StatSummary summary;
	private DataConfig config;

	/* Methods */
	public EditTallyForm( bool editMode )
	{
		this.editMode = editMode;

		Color = DXColors.Dark1;

		Padding = 0;
		Margin = 0;

		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;

		// Required for rounded corners 
		DXBorder frame = new()
		{
			Color = DXColors.Dark1,
			
			CornerRadius = DXCard.DefaultRadius,
			Elevation = DXCard.DefaultElevation,
			BorderWidth = 0,
			
			Padding = 0,
			Margin = 0,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill
		};
		
		// Internal form layout
		layout = new DXGridLayout
		{
			BackgroundColor = DXColors.Dark1,

			Padding = 0,
			RowSpacing = 0,
			ColumnSpacing = 0,

			// Required for rounded corners
			IsClippedToBounds = true,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill,

			// REQUIRED
			IgnoreSafeArea = true
		};

		// Roster
		roster = new LineupMenuView()
		{
			Embedded = true,
			HasTallRows = true,
			HasModified = true,

			Mode = LineupMenuView.SelectMode.Single,
			BackgroundColor = DXColors.Light1,

			PlayerSelected = OnPlayerSelected
		};

		// Header
		header = new EditTallyHeader();

		// Main editing view
		view = new EditTallyView
		{
			IsEditMode = editMode,

			PlayedChanged = OnPlayedChanged,
			TallyChanged = OnTallyChanged
		};

		// Save/cancel buttons
		buttons = new DXFormButtons( false, false )
		{
			SaveEnabled = false,

			SaveTapped = OnSaveTapped,
			CancelTapped = OnCancelTapped
		};

		frame.Content = layout;
		frame.Init();
		
		// Entire form scrolls on mobile
		if ( DXDevice.IsMobile )
		{
			DXScroll scroll = new()
			{
				Orientation = ScrollOrientation.Vertical,
				Content = frame
			};

			Content = scroll;
		}
		// Otherwise children scroll independently 
		else
		{
			Content = frame;
		}
	}

	// Post construction initialization
	public async Task Init( Set set, Color color )
	{
		tallySet = set;

		// May not have tally stats yet, otherwise deep copy
		summary = (set.StatSummary == null) ? new StatSummary() : new StatSummary( set.StatSummary );

		// Edit mode also needs metrics (accumulated into config)
		if ( editMode )
		{
			await AccumulateMetrics();
		}

		List<Player> players = set.Match.Season.GetPlayers();

		// Populate roster
		roster.Init( players.Count, false, false );
		roster.SetPlayers( players );

		// Init header
		header.Init( set );

		// Create controls
		view.Init( set );

		// Must be set AFTER players
		DefaultColor = color;
	}

	// Accumulates all skill metrics for editing existing set stats
	private async Task AccumulateMetrics()
	{
		// Make sure stats populated
		await tallySet.ReadCache( true );

		Match match = tallySet.Match;
		Organization org = match.Season.Team.Organization;

		// Configure query
		config = new DataConfig
		{
			Scope = "set",
			ScopeObject = tallySet,

			Organization = org,
			IsTeam1 = true,

			Team1 = match.Team1,
			Team2 = null,

			Action = null,
			OuterDim = "player",
			InnerDim = null,

			DataSet = "skills"
		};

		// Accumulate all set stats (metrics only)
		DataStats stats = await DataFilter.Filter( config );
		MetricsSkillAccumulator.AccumulateDimension( config, stats );

		// Must count sets played
		config.Dimension.CountSets( stats );
	}

	// Persists edited summary stats as delta between metrics and new total
	private void SaveChanges()
	{
		// May not have summary stats yet
		tallySet.StatSummary ??= new StatSummary();

		// Saving from edit form into set summary
		var srcStats = summary.PlayerStats;
		var destStats = tallySet.StatSummary.PlayerStats;

		// Save each player
		foreach ( string playerKey in srcStats.Keys )
		{
			// Player may not have entry yet
			if ( !destStats.TryGetValue( playerKey, out Dictionary<string,int> value ) )
			{
                value = new Dictionary<string,int>();
                destStats.Add( playerKey, value );
			}

			var srcPlayer = srcStats[ playerKey ];
			var destPlayer = value;

			// Copy each summary stat
			foreach ( string key in srcPlayer.Keys )
			{
				// Stat may not have summary entry yet
				destPlayer.TryAdd( key, 0 );

				// Edit form already calculated delta
				int srcValue = srcPlayer[ key ];

				// Persist					
				destPlayer[ key ] = srcValue;
			}
		}
	}

	/* Event Callbacks */

	// User selected player from roster
	private void OnPlayerSelected( Player player, bool cancel )
	{
		selectedPlayer = player;

		// Update header
		header.SetPlayer( player );

		string key = player.RootId;
		var summaryList = summary.PlayerStats;

		// Player may not have entry yet
		if ( !summaryList.TryGetValue(key, out Dictionary<string,int> value ) )
		{
            value = new Dictionary<string,int>();
            summaryList.Add( key, value );
		}

		var playerSummary = value;
		MetricsSkill playerMetrics = null;

		// Lookup skill metrics for editing
		if ( editMode )
		{
			var metricsList = config.Dimension.Metrics;

			// Player may not have entry yet
			if ( metricsList.TryGetValue( key, out DataMetrics metrics ) )
			{
				playerMetrics = metrics as MetricsSkill;

				// MUST calculate to include 'other' attack/assist metrics
				playerMetrics?.Calculate();
			}
		}

		// Populate view with player stats
		view.SetPlayer( player, playerSummary, playerMetrics );
	}

	// User toggled 'Played in set'
	private void OnPlayedChanged()
	{
		UpdateChange();
	}

	// User edited individual stat value
	private void OnTallyChanged()
	{
		UpdateChange();
	}

	// Updates UI to indicate changed data
	private void UpdateChange()
	{
		if ( selectedPlayer != null )
		{
			buttons.SaveEnabled = (!tallySet.IsSample || Shell.CurrentUser.IsAdmin);

			// Modified player row shows checkmark
			roster.SetModified( selectedPlayer, true );

			MetricsSkill metrics = null;

			// Edit mode requires metrics for player
			if ( editMode )
			{
				var metricsList = config.Dimension.Metrics;
				string key = selectedPlayer.RootId;

				// Player may not have entry if did not play
				if ( metricsList.TryGetValue( key, out DataMetrics value ) )
				{
					metrics = value as MetricsSkill;
				}
			}

			// Cache change, update state
			view.UpdateStats( selectedPlayer, summary, metrics );
		}
	}

	// User saving all changes on form
	private async void OnSaveTapped()
	{
		// Must only save changes when editing
		if ( editMode )
		{
			SaveChanges();
		}
		// Save everything when creating
		else
		{
			tallySet.StatSummary = summary;
		}

		// Persist
		await tallySet.UpdateSummary();

		Shell.Instance.HideForm();
	}

	// User cancelling form
	private void OnCancelTapped()
	{
		// Warn if changes will be lost
		if ( view.HasChanges() )
		{
			DXAlert.ShowNegativeCancel( "form.cancel.title", "form.cancel.msg", "form.discard", OnCancelConfirmed );
		}
		else
		{
			OnCancelConfirmed();
		}
	}

	// User confirmed cancel, exit form
	private static void OnCancelConfirmed()
	{
		Shell.Instance.HideForm();
	}

	/* Layout */

	// Redraws entire tally form
	public override void UpdateLayout( LayoutType type )
	{
		layout.ClearAll();

		base.UpdateLayout( type );

		// Update children
		header.UpdateLayout( type );
		view.UpdateLayout( type );
	}

	// Landscape (4:3)
	protected override void Landscape()
	{
		Padding = 15;

		// 3 rows
		layout.AddFixedRow( 50 );		// 0: header
		layout.AddStarRow();			// 1: view
		layout.AddFixedRow( 68 );		// 2: buttons

		// 3 columns
		layout.AddStarColumn( 25 );		// 0: roster
		layout.AddFixedColumn( 1 );		// 1: divider
		layout.AddStarColumn( 75 );     // 2: header, view

		// Divider
		layout.Fill( DXColors.Dark1, 1, 0, 1, 3 );

		// Add components
		layout.Add( roster, 0, 0, 1, 3 );
		layout.Add( header, 2, 0 );
		layout.Add( view, 2, 1 );
		layout.Add( buttons, 2, 2 );
	}

	// Portrait (3:4)
	protected override void Portrait()
	{
		bool wide = DXDevice.IsTabletWide;

		Padding = 15;

		// 3 rows
		layout.AddFixedRow( 50 );					// 0: header
		layout.AddStarRow();						// 1: view
		layout.AddFixedRow( 68 );					// 2: buttons

		// 3 columns
		layout.AddStarColumn( wide ? 36 : 32 );		// 0: roster
		layout.AddFixedColumn( 1 );					// 1: divider
		layout.AddStarColumn( wide ? 64 : 68 );     // 2: header, view

		// Divider
		layout.Fill( DXColors.Dark1, 1, 0, 1, 3 );

		// Add components
		layout.Add( roster, 0, 0, 1, 3 );
		layout.Add( header, 2, 0 );
		layout.Add( view, 2, 1 );
		layout.Add( buttons, 2, 2 );
	}

	// Mobile Landscape
	protected override void MobileLandscape()
	{
		Padding = DXUtils.AddPadding( 15, DXDevice.SafeAreaLR() );
		
		// 3 rows
		layout.AddFixedRow( 50 );       // 1: header
		layout.AddAutoRow();            // 2: view
		layout.AddFixedRow( 68 );       // 3: buttons

		// 3 columns
		layout.AddStarColumn( 40 );     // 0: roster
		layout.AddFixedColumn( 1 );     // 1: divider
		layout.AddStarColumn( 60 );     // 2: header, view, buttons

		// Divider
		layout.Fill( DXColors.Dark1, 1, 0, 1, 2 );

		// Add components
		layout.Add( roster, 0, 0, 1, 3 );
		layout.Add( header, 2, 0 );
		layout.Add( view, 2, 1 );
		layout.Add( buttons, 2, 2 );
	}

	// Mobile Portrait
	protected override void MobilePortrait()
	{
		Padding = DXUtils.AddPadding( 15, DXDevice.SafeAreaLR() );

		// 4 rows
		layout.AddAutoRow();			// 0: roster
		layout.AddFixedRow( 50 );       // 1: header
		layout.AddAutoRow();			// 2: view
		layout.AddFixedRow( 68 );       // 3: buttons

		// 1 column

		// Add components
		layout.Add( roster, 0, 0 );
		layout.Add( header, 0, 1 );
		layout.Add( view, 0, 2 );
		layout.Add( buttons, 0, 3 );
	}
}

//
