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

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

using DXLib.Utils;

namespace iStatVball3;

/*
 * Shared implementation for the top-level stat recording interface for both the RallyFlow and Legacy engines. Main
 * entry point into the interface.
 */
public abstract class RecordEngine : DXLayout
{
	/* Events */
	public Action PauseTapped { set => Scoreboard.PauseTapped = value; }

	/* Properties */

	// Master state machine
	public RecordState StateMachine { get; protected set; }

	// Used for accessing time offset for video sync
	public RecordSync VideoSync { get; set; }

	// Sizing
	public static double ScoreboardWd { get; protected set; }
	public static double ScoreboardHt { get; protected set; }

	// Mobile only
	public virtual bool IsUndoDisabled { get; set; }
	public virtual bool IsFaultDisabled { get; set; }

	// Allow external access
	public RecordScore Scoreboard { get; protected set; }

	/* Fields */

	// Initialization refs
	protected Set recordSet;

	protected Team team1;
	protected Team team2;

	protected RecordLineup lineup1;
	protected RecordLineup lineup2;

	protected RecordConfig config;

	// Master layout
	protected readonly DXAbsoluteGestures layout;

	// Lineup save
	private List<LineupEntry> saveEntries;
	private Action saveCallback;

	/* Methods */
	protected RecordEngine( DXAbsoluteGestures layout )
	{
		this.layout = layout;

		IsVisible = false;

		// Tap config
		MR.Gestures.Settings.MsUntilTapped = 60;
	}

	// Configures recording UI for new set
	public virtual async Task Init( Set set )
	{
		recordSet = set;

		Match match = set.Match;

		team1 = match.Team1;
		team2 = match.Team2;

		lineup1 = GetLineup1();
		lineup2 = GetLineup2();

		// Init shared children
		await Scoreboard.Init( set );

		// Populate configuration from set data
		config = new RecordConfig
		{
			Set = set,

			Team1 = team1,
			Team2 = team2,

			ServeFirst = set.Serve1 ? team1 : team2,
			ServeSide = RecordCourt.SideFromKey( set.Serve1 ? set.Side1 : set.Side2 ),

			Rotation1 = set.Rotation1,
			Rotation2 = set.Rotation2,

			Lineup1 = lineup1,
			Lineup2 = lineup2,

			Points1 = set.Points1,
			Points2 = set.Points2,

			// UI references
			Parent = this,
			Scoreboard = Scoreboard
		};
	}

	/* Lineups */

	// Returns starting lineup for Team 1
	private RecordLineup GetLineup1()
	{
		Match match = recordSet.Match;

		// Team 1 always first order team
		RecordLineup lineup = new()
		{
			Name = match.Team1Name,
			Abbreviation = match.Team1Abbrev
		};

		switch ( recordSet.Type1 )
		{
			// Pre-created or blank lineup
			case "lineup":
			case "blank":
			{
				lineup.FromLineup( recordSet.Lineup1 );
				break;
			}
		}

		return lineup;
	}

	// Returns starting lineup for Team 2 (currently always anonymous)
	private RecordLineup GetLineup2()
	{
		Match match = recordSet.Match;

		return new RecordLineup
		{
			Name = match.Team2Name,
			Abbreviation = match.Team2Abbrev
		};
	}

	/* Life Cycle */

	// Starts/resumes stat recording for a set
	public async Task Start()
	{
		Shell.CurrentUser.RecordingSet = recordSet;

		// Start new set
		if ( recordSet.IsNew )
		{
			bool update = !recordSet.IsRallySynced;
			
			await StateMachine.Start( false, update );
			await recordSet.Start();
		}
		// Restore in-progress set
		else
		{
			await StateMachine.Restore( recordSet );
		}
	}

	// Ends stat recording for a set
	public virtual async Task End( int points1, int points2 )
	{
		DXSpinner.Start();

		// Notify watcher(s)
		StateMachine.ProcessEnd();

		// Persist all end data
		await recordSet.End( points1, points2 );

		Shell shell = Shell.Instance;

		// Hide recording UI
		if ( recordSet.IsRallySynced )
		{
			shell.HideTool( recordSet );
		}
		else
		{
			shell.HideRecordForm( recordSet );
		}

		DXSpinner.Stop();
	}

	/* Lineup Save */

	// Dynamically creates and saves lineup from recording interface
	public void SaveLineup( List<LineupEntry> entries, Action callback )
	{
		// Lineup must be valid
		if ( RecordLineup.Validate( entries, true, callback ) )
		{
			saveEntries = entries;
			saveCallback = callback;

			// Prompt for lineup name
			DXInputAlert.ShowText( "save.title", "save.msg", DXString.Get( "save.field" ), null, OnSave, callback );
		}
	}

	// User confirmed lineup name
	private async void OnSave( string name )
	{
		Season season = recordSet.Match.Season;

		// Must be unique
		if ( season.GetLineupByName( name ) != null )
		{
			DXAlert.ShowError( "save.title", "lineup.err.name" );
		}
		else
		{
			DXSpinner.Start();

			// Create new lineup
			Lineup lineup = new()
			{
				Name = name,
				Description = null,
				Notes = null,

				// Children
				Entries = new List<LineupEntry>( saveEntries ),
				Subs = null,
				Swaps = null,

				// Set parent
				SeasonId = season.UniqueId,
				Season = season
			};

			// Add to parent
			season.Lineups.Add( lineup );

			// Persist
			await lineup.Create();

			// Success
			DXAlert.ShowOk( "save.title", "save.success", name, saveCallback );

			DXSpinner.Stop();
		}
	}

	/* Video */

	// Returns current time offset (ms) for video sync
	public async Task<int> GetVideoOffset()
	{
		return (VideoSync == null) ? -1 : await VideoSync.GetOffset();
	}

	/* Mobile */

	// Required by some engines on mobile
	public virtual void UpdateScore( int scoreA, int scoreB ) {}
	public virtual void UpdateTeams( string teamA, string teamB ) {}
	public virtual void UpdateTeamColors( Color colorA, Color colorB ) {}

	public virtual Task<bool> CloseDrawers() { return Task.FromResult( true ); }
	
	/* Layout */

	// Some controls require special handling on rotate
	public override void Rotate()
	{
		Scoreboard.Rotate();
	}

	// Redraws entire recording UI
	public override void UpdateLayout( LayoutType type )
	{
		base.UpdateLayout( type );

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

//
