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

using Plugin.Firebase.Firestore;

using DXLib.UI;

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

using DXLib.Log;
using DXLib.Data;
using DXLib.Utils;

namespace iStatVball3;

/*
 * Implements a form panel for editing play-by-play stats for a set. The form is embedded in a parent SetEditForm. The
 * form contains the full list of stat events for the set, as well as a header showing the currently selected team/action/result,
 * a scrollable list of roster players, and then flyouts for selecting rating/modifier/selector/fault.
 */
public class EditLogForm : DXContent, EditLogController
{
	/* Fields */
	private readonly DXGridLayout layout;
	private readonly DXFormButtons buttons;

	// Components
	private readonly LogHeader header;
	private readonly LogView log;

	private readonly EditLogView view;
	private readonly FilterView filter;

	private DXPopup filterPopup;

	// External ref
	private Set logSet;

	// State control
	private LogEvent currentEvent;
	private readonly List<LogEvent> changes;

	/* Methods */
	public EditLogForm()
	{
		// Allocate container
		changes = [];

		bool tablet = DXDevice.IsTablet;

		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
		};

		// Log header
		header = new LogHeader
		{
			ShowClose = false,
			ShowCount = true,
			ShowFilter = true,

			FilterTapped = OnFilterTapped
		};

		header.Init();

		// Filter
		filter = new FilterView
		{
			Filtered = OnFiltered,
			Cleared = OnCleared
		};

		// Log
		log = new LogView( false )
		{
			Mode = LogView.LogMode.Edit,
			FontSize = tablet ? 16 : 14,

			EventSelected = OnEventSelected
		};

		// Header/roster/flyouts
		view = new EditLogView( this );

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

			SaveTapped = OnSaveTapped,
			CancelTapped = OnCancelTapped
		};

		frame.Content = layout;
		frame.Init();
		
		Content = frame;
	}

	// Post construction initialization
	public async Task Init( Set set )
	{
		logSet = set;

		Match match = set.Match;

		// Cache remote stats
		await set.ReadCache( true );

		// Init log
		log.Init( match );
		log.Load( set.StatCache );

		header.SetCount( set.StatCache.Count, false );

		// Init filter
		filter.Init( set );
		filter.Update( 2, 2 );

		// Init header/roster/flyouts
		view.Init( set );
	}

	// Updates UI, caches data, following any roster/flyout change
	private void HandleChange( bool next )
	{
		Stat stat = currentEvent.Stat;

		// Update display
		log.Modify( stat );
		log.Refresh( currentEvent );

		LogEvent nextEvent = log.GetNextEvent( currentEvent );

		// Next event may have also changed
		if ( next && !stat.IsError )
		{
			log.Modify( nextEvent.Stat );
			log.Refresh( nextEvent );
		}

		// Cache modification
		if ( !changes.Contains( currentEvent ) )
		{
			changes.Add( currentEvent );
		}

		buttons.SaveEnabled = (!logSet.IsSample || Shell.CurrentUser.IsAdmin);
	}

	// Persists any pending changes
	private async Task UpdateData()
	{
		try
		{
			IWriteBatch batch = DXData.StartBatch();

			// Update Db
			foreach ( LogEvent change in changes )
			{
				change.Stat.Update( batch );
			}

			await DXData.CommitBatch( batch );
		}
		// Older sets may not have stats in Db
		catch
		{
			DXLog.Error( "editlogform.updatedata" );
		}

		// Cache both locally and to cloud
		await logSet.WriteCaches( true );
	}

	/* Event Callbacks */

	// User selected event from log list
	private void OnEventSelected( LogEvent evt )
	{
		currentEvent = evt;

		// Populate header/roster/flyouts
		view.SetStat( evt.Stat );
	}

	// User selected player, update editing view
	public void OnPlayerSelected( Player player )
	{
		Stat stat = currentEvent.Stat;

		// Update player(s)
		if ( stat.IsBlock )
		{
			stat.SetPlayers( view.Players, logSet.Lineup1 );

			// Multi-block not eligible for modifiers
			view.SetModifiers( stat, stat.IsMultiBlock );

			if ( stat.IsMultiBlock )
			{
				stat.Modifier = null;
			}

			// May need to update block solo/assist
			EditLogView.UpdateBlockResult( stat );
		}
		else
		{
			stat.SetPlayer( player, logSet.Lineup1 );
		}

		// Update UI
		HandleChange( false );
	}

	// User selected item from rating/modifier/selector flyout
	public void OnFlyoutSelected( FlyoutBar.FlyoutType type, int index )
	{
		Stat stat = currentEvent.Stat;
		
		// Update data
		stat.Rating = view.Rating;
		stat.Modifier = view.Modifier;
		
		bool selector = (type == FlyoutBar.FlyoutType.Selector);
		
		// Selector change updates next stat
		if ( selector )
		{
			stat.Selector = view.Selector;
			LogEvent nextEvent = log.GetNextEvent( currentEvent );
		
			EditLogView.UpdateNextEvent( currentEvent, nextEvent );
		}
		
		// Update UI
		HandleChange( selector );
	}

	// User selected fault from fault flyout
	public void OnFaultSelected()
	{
		Stat stat = currentEvent.Stat;

		// Update data
		stat.Fault = view.Fault;

		// Update UI
		HandleChange( false );
	}

	// User saving all event modifications
	private async void OnSaveTapped()
	{
		DXSpinner.Start();

		// Update db and caches
		await UpdateData();

		Shell.Instance.HideForm();
	}

	// User cancelling editing, confirm if discarding changes
	private void OnCancelTapped()
	{
		// Warn if changes will be lost
		if ( changes.Count > 0 )
		{
			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();
	}

	// User tapped filter button in log header
	private void OnFilterTapped()
	{
		// Create popup
		filterPopup = new DXPopup( filter )
		{
			IsModal = false,
			
			ViewWidth = 500,
			ViewHeight = 560,

			PopupClosed = OnClosed
		};

		// Show
		filterPopup.ShowFromView( header.FilterBtn );
	}

	// User cleared filter
	private void OnCleared()
	{
		filterPopup.Hide();

		DXSpinner.Start();

		// Reload log
		log.Load( logSet.StatCache );

		// Update header
		header.SetCount( logSet.StatCache.Count, false );
		header.FilterBtn.Reset();

		// Clear view
		view.ClearStat();

		DXSpinner.Stop();
	}

	// User tapped log filter button
	private void OnFiltered()
	{
		filterPopup.Hide();

		DXSpinner.Start();

		// Filter stats against current selections
		Filter logFilter = filter.GetFilter();
		List<Stat> stats = logFilter.FilterStats( logSet.StatCache );

		// Update log
		log.Load( stats );

		bool filtered = (stats.Count < logSet.StatCache.Count);

		// Update event count (indicate filtered)
		header.SetCount( stats.Count, filtered );
		header.FilterBtn.Reset();

		// Clear view
		view.ClearStat();

		DXSpinner.Stop();
	}

	// User closed (cancelled) popup
	private void OnClosed()
	{
		header.FilterBtn.Reset();
	}

	/* Layout */

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

		base.UpdateLayout( type );

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

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

		const double headerHt = LogHeader.HeaderHt;

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

		// 3 columns
		layout.AddStarColumn( 45 );			// 0: log
		layout.AddFixedColumn( 1 );			// 1: divider
		layout.AddStarColumn( 55 );			// 2: view

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

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

		const double headerHt = LogHeader.HeaderHt;

		// 5 rows
		layout.AddFixedRow( headerHt );		// 0: header
		layout.AddStarRow( 55 );			// 1: log
		layout.AddFixedRow( 1 );			// 2: divider
		layout.AddStarRow( 45 );			// 3: view
		layout.AddFixedRow( 68 );			// 4: buttons

		// 1 column
		layout.AddStarColumn();

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

	// Mobile Landscape
	protected override void MobileLandscape()
	{
		Padding = DXUtils.AddPadding( 10, DXDevice.SafeAreaLR() );

		const double headerHt = LogHeader.HeaderHt;

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

		// 3 columns
		layout.AddStarColumn( 43 );		    // 0: log
		layout.AddFixedColumn( 1 );			// 1: divider
		layout.AddStarColumn( 57 );			// 2: view

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

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

		const double headerHt = LogHeader.HeaderHt;

		// 5 rows
		layout.AddFixedRow( headerHt );		// 0: header
		layout.AddStarRow( 50 );			// 1: log
		layout.AddFixedRow( 1 );		    // 2: divider
		layout.AddStarRow( 50 );			// 3: view
		layout.AddFixedRow( 48 );			// 4: buttons

		// 1 column
		layout.AddStarColumn();

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

//
