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

using DXLib.Utils;

namespace iStatVball3;

/*
 * Container used to carry an accumulated WHP score for a single action for a player currently on the court. Also
 * provides methods for calculating each component of the WHP score.
 *
 * The WHP scoring algorithm calculates a score for a player for each zone of the court (1-6). The score is a predicted
 * value indicating the likelihood of the player contacting the ball in that zone for the current play. The prediction
 * is based on historic data combined with a default seed.
 */
public class WHPScore
{
	/* Constants */

	// Anchor point within each zone, used for distance calc
	public static readonly Point[] ZoneAnchors =
	[
		new ( 0.0, 1.0 ),		// 1
		new ( 1.0, 1.0 ),		// 2
		new ( 1.0, 0.5 ),		// 3
		new ( 1.0, 0.0 ),		// 4
		new ( 0.0, 0.0 ),		// 5
		new ( 0.0, 0.5 )		// 6
	];

	// Weight of score vs seed in total calc
	private const float ScoreWeight = 2.0f;

	/* Properties */

	// Player/position key
	public LineupEntry Entry { get; set; }

	// Shorthand access to underlying player
	public Player Player => Entry.Player;

	// Profiling
	public int TotalScored { get; private set; }

	/* Fields */

	// Score components
	public float[] ZoneScores { get; }
	public float[] TotalScores { get; }

	public float[] SeedScores { get; }
	public float[] RotSeedScores { get; }
	public float[] TotalSeedScores { get; }

	public int[] ZoneCounts { get; }

	/* Methods */
	public WHPScore()
	{
		const int count = WHP.ZoneCount;

		// Allocate memory
		ZoneScores = new float[ count ];
		TotalScores = new float[ count ];

		SeedScores = new float[ count ];
		RotSeedScores = new float[ count ];
		TotalSeedScores = new float[ count ];

		ZoneCounts = new int[ count ];
	}

	// Clears score components to zero
	public void Clear()
	{
		TotalScored = 0;

		// Must clear selection
		Entry.IsDefault = false;
		Entry.IsPrevious = false;

		// Reset score components
		for ( int i = 0; i < WHP.ZoneCount; i++ )
		{
			ZoneScores[i] = 0;
			TotalScores[i] = 0;

			SeedScores[i] = 0;
			RotSeedScores[i] = 0;
			TotalSeedScores[i] = 0;

			ZoneCounts[i] = 0;
		}
	}

	// Determines if specified action is relevant for WHP scoring
	public static bool IsRelevant( string action )
	{
		return (action != Stats.ServeKey);
	}

	// Returns score item from specified list for given player
	public static WHPScore FindScore( List<WHPScore> scores, Player player )
	{
		return scores.FirstOrDefault( score => score.Player.Equals( player ) );
	}

	/* Scoring */

	// Scores WHP score components for specified stat
	public void Score( WHPConfig config, Stat stat )
	{
		// Score location (zone) component
		if ( config.UseML )
		{
			ScoreZones( stat );
		}

		// Profiling
		ZoneCounts[ config.Zone - 1 ]++;
		TotalScored++;
	}

	// Scores specified stat against all 6 zones
	private void ScoreZones( Stat stat )
	{
		for ( int zone = 0; zone < WHP.ZoneCount; zone++ )
		{
			float raw = ScoreZone( stat, zone );
			float norm = (1.0f - raw);

			// Save normalized score
			ZoneScores[ zone ] += norm;
		}
	}

	// Scores specified stat against given zone
	private static float ScoreZone( Stat stat, int zone )
	{
		Point statPt = RecordCourt.NormalizeSide( stat.StartX, stat.StartY );
		Point zonePt = ZoneAnchors[ zone ];

		// Calc Euclidean distance from zone anchor to stat location
		return DXGraphics.Distance( (float)zonePt.X, (float)zonePt.Y, (float)statPt.X, (float)statPt.Y );
	}

	// Calculates total score
	public void CalculateTotal( WHPConfig config )
	{
		for ( int zone = 0; zone < WHP.ZoneCount; zone++ )
		{
			// Get front/backrow court position
			WHPSeed.Position position = WHPSeed.GetPosition( Entry );

			// Lookup zoned-based seed
			float seed = (float) WHPSeed.GetSeed( config.Action, (zone + 1), position, config.LiberoLB );
			float rotSeed = 1.0f;

			// Lookup rotation-based seed (serve receive only)
			if ( config.Action == Stats.ReceiveKey )
			{
				rotSeed = (float) WHPSeed.GetRotationSeed( (zone + 1), Entry.Zone );
			}

			// Calculate total seed
			float totalSeed = (seed * rotSeed);

			// Save components for profiling
			SeedScores[ zone ] = seed;
			RotSeedScores[ zone ] = rotSeed;
			TotalSeedScores[ zone ] = totalSeed;

			float score = ZoneScores[ zone ];
			float weightedScore = (score * ScoreWeight);

			// Total score is accumulated zone score weighted and then scaled
			// by seed. If ML turned OFF, then only seed is used.
			TotalScores[ zone ] = (config.UseML && (weightedScore != 0.0)) ? (weightedScore * totalSeed) : totalSeed;
		}
	}
}

//
