/* 
 * 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;

namespace iStatVball3;

/*
 * Implements state machine functionality specific to the Legacy engine.
 */
public class LegacyState : RecordState
{
	/* Properties */
	public override bool IsRallyInProgress => IsInProgress();

	/* Fields */

	// UI references
	private readonly PlayerGrid playerGrid;

	// Engine specific ref
	private readonly LegacyScore legacyScore;

	// Legacy logic
	private readonly Legacy legacy;

	/* Methods */

	// Saves external references, init helpers (start of each set)
	public LegacyState( RecordConfig config ) : base( config )
	{
		legacyScore = (LegacyScore)config.Scoreboard;

		// Save UI references
		playerGrid = config.PlayerGrid;

		// Handle legacy logic
		legacy = new Legacy( this, legacyScore, playerGrid );
	}

	// Post construction initialization
	public override async Task Init()
	{
		legacy.Init( Set.Roster1 );

		// Init children
		await legacyScore.Init( this, legacy );
		playerGrid.Init( legacy );
	}

	/* State Control */

	// Determines if rally currently in-progress
	private bool IsInProgress()
	{
		return State switch
		{
			Legacy.StartState or 
			Legacy.ServeState or 
			Legacy.ReceiveState => false,

			_ => true,
		};
	}

	// Initializes state machine (start of each set)
	public override async Task Start( bool restore, bool update )
	{
		await base.Start( restore, update );

		// Start engine
		legacy.Start();

		// Init lineup
		legacy.SetLineup( Lineup1, Rotation1 );

		Legacy.EditMode mode = (Lineup1.IsEmpty() && !restore) ? Legacy.EditMode.Lineup : Legacy.EditMode.Off;

		// Blank lineup starts in edit mode
		legacy.SetEditMode( mode, false );

		// MUST be after setting lineups
		ChangeServeSide();

		// Ready for first rally
		InitRally( true, update );

		// Warn if no setter with Full Auto and pre-created lineup
		if ( (mode == Legacy.EditMode.Off) && !restore && (Shell.Settings.LegacySet == Legacy.AutoSetFull) )
		{
			if ( !Lineup1.HasSetter() )
			{
				DXAlert.ShowOk( "alert.warn", "record.err.setter" );
			}
		}

		// AUTO: process auto swap at start of set
		auto.Process( RecordAuto.AutoSwap, Rotation1, Team1Serving() );
	}

	// Preps Legacy engine at start of each rally
	public override void InitRally( bool sideout, bool update )
	{
		base.InitRally( sideout, update );

		// Init children
		legacy.InitRally();
	}

	// Called on first contact of rally
	public override bool StartRally()
	{
		base.StartRally();

		legacy.StartRally();

		return true;
	}

	// Currently unused
	public override void Populate()
	{}

	// Change server on sideout, rotate court view
	public override void ChangeServeSide( bool change = false )
	{
		base.ChangeServeSide( change );

		if ( change && Team1Serving() )
		{
			legacy.UpdateCourt();
		}
	}

	// Legacy specific undo handling
	public override void RestoreState( Stat stat, bool update )
	{
		base.RestoreState( stat, update );

		// Must advance to serve/pass
		if ( State == Legacy.StartState )
		{
			legacy.AdvanceState( true, null );
		}

		StatState state = stat.State;

		// Restore lineup
		if ( state.HasEntries )
		{
			legacy.SetLineup( Lineup1, Rotation1 );
		}

		legacy.UpdateCourt();

		legacy.HandleAutoFocus();
		legacy.ResetLastTap();

		// Possibly un-rotate after point
		if ( IsRallyInProgress && Shell.Settings.LegacySmart )
		{
			legacy.Court.UpdateSmart();
		}
	}

	// Forces end to rally and manually awards point
	public override async Task ForcePoint( RecordCourt.Side side )
	{
		await ForcePoint( true, side );
	}

	/* Lineup */

	// Returns current server (zone 1)
	public override LineupEntry GetServer( bool isTeam1 )
	{
		return null;
	}

	// Returns current setter (backrow first)
	public override LineupEntry GetSetter( bool isTeam1 )
	{
		return legacy.Court.GetSetter();
	}

	// Returns list of all players currently on court (rotation order)
	public override List<LineupEntry> GetTeam( bool isTeam1 )
	{
		return null;
	}

	// Returns list of players currently in frontrow (zones 2-4)
	public override List<LineupEntry> GetFrontrow( bool isTeam1 )
	{
		return null;
	}

	// Returns list of players currently in backrow (zones 5-6-1)
	public IList<LineupEntry> GetBackrow()
	{
		return legacy.Court.GetBackrow();
	}

	// Determines if libero currently on court
	public bool IsLiberoIn()
	{
		return (legacy.Court.GetLibero() != null);
	}

	// Determines if libero currently on court and in backrow
	public bool IsLiberoBack()
	{
		LineupEntry entry = legacy.Court.GetLibero();

		return entry is { IsBackRow: true };
	}

	// Swaps player in specified zone to libero area
	public void SwapLiberoIn( LineupEntry entry )
	{
		// Snapshot before swap
		List<LineupEntry> preList = legacy.GetEntries();

		Player player = entry.Player;

		int lineupZone = legacy.ZoneForPlayer( player );
		int courtZone = playerGrid.ZoneForPlayer( player );

		LineupEntry outEntry = legacy.GetEntry( lineupZone );
		LineupEntry inEntry = legacyScore.GetLiberoEntry( Libero1 );

		int liberoZone = Lineup.LiberoZone1 + (Libero1 - 1);

		// Player out
		legacyScore.SetLiberoEntry( Libero1, outEntry );
		legacy.SetEntry( liberoZone, outEntry );

		// Libero in
		legacy.Court.SetEntry( courtZone, inEntry );
		legacy.SetEntry( lineupZone, inEntry );

		// Snapshot after
		List<LineupEntry> postList = legacy.GetEntries();

		// Persist
		SubSwap( Stats.SwapInKey, Team1OnSideA, outEntry, inEntry, preList, postList );
	}

	// Swaps libero currently on court back to libero area
	public void SwapLiberoOut( bool autoSwap )
	{
		LineupEntry outEntry = legacy.Court.GetLibero();

		if ( outEntry != null )
		{
			LineupEntry inEntry = legacyScore.GetLiberoEntry( Libero1 );

			// Snapshot before swap
			List<LineupEntry> preLineup = legacy.GetEntries();

			Player player = outEntry.Player;

			int courtZone = playerGrid.ZoneForPlayer( player );
			int lineupZone = legacy.ZoneForPlayer( player );

			int liberoZone = Lineup.LiberoZone1 + (Libero1 - 1);

			// Libero out
			legacyScore.SetLiberoEntry( Libero1, outEntry );
			legacy.SetEntry( liberoZone, outEntry );

			// Player in
			legacy.Court.SetEntry( courtZone, inEntry );
			legacy.SetEntry( lineupZone, inEntry );

			// Snapshot after
			List<LineupEntry> postLineup = legacy.GetEntries();

			// Persist
			SubSwap( Stats.SwapOutKey, Team1OnSideA, outEntry, inEntry, preLineup, postLineup, autoSwap );
		}
	}

	// Substitutes player into lineup on court
	public void SubIn( LineupEntry inEntry )
	{
		int courtZone = inEntry.Zone;

		// Snapshot before sub
		List<LineupEntry> preLineup = legacy.GetEntries();

		LineupEntry outEntry = playerGrid.GetEntry( courtZone );
		int lineupZone = legacy.ZoneForPlayer( outEntry.Player );

		// Player in
		legacy.SetEntry( lineupZone, inEntry );

		// Snapshot after
		List<LineupEntry> postLineup = legacy.GetEntries();

		// Persist
		SubSwap( Stats.SubKey, Team1OnSideA, outEntry, inEntry, preLineup, postLineup );
	}

	/* Process */

	// Handles normal legacy point
	public override async Task ProcessPoint( RecordData data, Team team )
	{
		await base.ProcessPoint( data, team );

		ProcessCommon( data.Sideout );
	}

	// Handles manually awarded legacy point
	public override async Task ProcessPoint( RecordData data, RecordCourt.Side side )
	{
		await base.ProcessPoint( data, side );

		ProcessCommon( data.Sideout );
	}

	// Processes common functionality for both normal and manual points
	private void ProcessCommon( bool sideout )
	{
		// Ready for next rally
		InitRally( sideout, true );

		// Update rotation
		if ( sideout )
		{
			legacyScore.UpdateRotation( Rotation1 );

			// Check for libero auto-swap
			if ( IsLiberoIn() && !IsLiberoBack() )
			{
				SwapLiberoOut( true );
			}
		}
	}

	/* UI Update */

	// Updates scoreboard, undo history, etc
	public override void UpdateUI()
	{
		base.UpdateUI();

		// Update rot/subs in scoreboard
		UpdateTeambar( RecordCourt.Side.Unknown );

		// OPTIMIZE: Avoid redundant redraws
		//legacy.HandleAutoFocus();
	}

	// Updates rotation, subs, etc (always Team 1)
	public override void UpdateTeambar( RecordCourt.Side side )
	{
		legacyScore.UpdateRotation( Rotation1 );

		UpdateSubs( Team1Side );
	}

	// Updates only subs remaining portion of scoreboard (always Team 1)
	public override void UpdateSubs( RecordCourt.Side side )
	{
		legacyScore.UpdateSubs( Subs1 );
	}

	// Forces Legacy engine to refresh current court players and libero(s)
	public override void UpdatePlayers()
	{
		legacy.UpdateCourt();
		legacyScore.UpdateLibero();
	}

	/* Event Handling */

	// Forwards key taps to Legacy engine
	public async Task HandleKey( ButtonKey key )
	{
		await legacy.HandleKey( key );
	}
}

//
