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

using System.Text.Json.Serialization;
using Plugin.Firebase.Firestore;

namespace iStatVball3;

/*
 * A container for raw stats data for one set in a match as well as for all players that participated in that set. The
 * raw data can be imported from a v2 ISV file or can be used for editing local stats.
 *
 * Persisted as a map in the parent set NOT as a root level collection.
 */
public class StatSummary : IFirestoreObject
{
	/* Constants */

	// Byte size of stats (used for binary offsets)
	public const int StatSize = 4;
	public const int FieldSize = (StatSize / 2);

	// Indicates player played in set (tally editing only)
	public const string PlayedKey = "PlayingSets";

	// Number sets played (from imported ISV2)
	public const string ImportPlayedKey = "ImportPlayingSets";

	/* Properties */

	// Set level stats (keyed by stat key)
	[FirestoreProperty("SetStats")] public Dictionary<string,int> SetStats { get; set; }

	// Player level stats (keyed by player ID, then stat key)
	[FirestoreProperty("PlayerStats")] public Dictionary<string,Dictionary<string,int>> PlayerStats { get; set; }

	/* Ignored */

	// Number of sets aggregated during analysis
	[JsonIgnore] public int SetCount { get; set; }

	// Summary populated with player stats?
	[JsonIgnore] public bool HasPlayerData => (PlayerStats.Count > 0);

	/* Methods */

	// Constructor used for population from db
	public StatSummary()
	{
		SetStats = new Dictionary<string,int>();
		PlayerStats = new Dictionary<string,Dictionary<string,int>>();
	}

	// Creates stat summary from existing summary (fully deep copy)
	public StatSummary( StatSummary summary )
	{
		var setStats = summary.SetStats;
		SetStats = new Dictionary<string,int>( setStats.Count );

		// Deep copy set stats
		foreach ( string key in setStats.Keys )
		{
			SetStats[ key ] = setStats[ key ];
		}

		var playerStats = summary.PlayerStats;
		PlayerStats = new Dictionary<string,Dictionary<string,int>>( playerStats.Count );

		// Deep copy all player stat dictionaries
		foreach ( string key in playerStats.Keys )
		{
			var stats = playerStats[ key ];
			PlayerStats[ key ] = new Dictionary<string,int>( stats.Count );

			// Deep copy individual player stats
			foreach ( string statKey in stats.Keys )
			{
				PlayerStats[ key ][ statKey ] = stats[ statKey ];
			}
		}
	}

	// Adds all stat values from specified summary into this summary
	public void Add( StatSummary summary, bool includeSet = false )
	{
		if ( summary != null )
		{
			// Must accumulate here
			SetCount += summary.SetCount;

			var dest = PlayerStats;
			var src = summary.PlayerStats;

			// Add each player
			foreach ( string playerId in src.Keys )
			{
				var srcPlayer = src[ playerId ];

				// Destination may have fewer than source
				if ( !dest.ContainsKey( playerId ) )
				{
					dest.Add( playerId, new Dictionary<string,int>() );
				}

				var destPlayer = dest[ playerId ];

				// Add each stat for player
				foreach ( string key in srcPlayer.Keys )
				{
					// Destination may have fewer than source
					destPlayer.TryAdd( key, 0 );

					int addend1 = srcPlayer[ key ];
					int addend2 = destPlayer[ key ];

					int sum = (addend1 + addend2);

					// Save total in this summary
					destPlayer[ key ] = sum;
				}
			}

			// Optionally add set summary stats
			if ( includeSet )
			{
				var destSet = SetStats;
				var srcSet = summary.SetStats;

				// Add each stat for set
				foreach ( string key in srcSet.Keys )
				{
					// Destination may have fewer than source
					destSet.TryAdd( key, 0 );

					int addend1 = srcSet[ key ];
					int addend2 = destSet[ key ];

					int sum = (addend1 + addend2);

					// Save total in this summary
					destSet[ key ] = sum;
				}
			}
		}
	}

	/* Conversion */

	// Maps specified raw summary key to metrics field value
	public static int GetMetricValue( MetricsSkill metrics, string key )
	{
		// Player may not have stats in set
		if ( metrics == null )
		{
			return 0;
		}

		// Map from raw to metrics field
		return key switch
		{
			"PlayingSets" => metrics.General_SetsPlayed,

			"ServingATT" => metrics.Serve_Zeros,
			"ServingACE" => metrics.Serve_Aces,
			"ServingERR" => metrics.Serve_Errors,

			"HittingATT"  => metrics.Attack_TotalZeros,
			"HittingKILL" => metrics.Attack_TotalKills,
			"HittingERR"  => metrics.Attack_TotalErrors,

			"NewPass0" => metrics.Receive_Rating0,
			"NewPass1" => metrics.Receive_Rating1,
			"NewPass2" => metrics.Receive_Rating2,
			"NewPass3" => metrics.Receive_Rating3,
			"NewPass4" => metrics.Receive_Rating4,

			"BlockingATT"  => metrics.Block_Zeros,
			"BlockingBLK"  => metrics.Block_Blocks,
			"BlockingASST" => metrics.Block_Assists,
			"BlockingERR"  => metrics.Block_Errors,

			"SettingATT"  => metrics.Set_Zeros,
			"SettingASST" => metrics.Set_TotalAssists,
			"SettingERR"  => metrics.Set_Errors,

			"DefenseATT" => metrics.Defense_Zeros,
			"DefenseDIG" => metrics.Defense_Digs,
			"DefenseERR" => metrics.Defense_Errors,

			_ => 0,
		};
	}
}

/* ISV Binary Indices */

// Indices into raw byte array for v2 ISV SetSummary
public enum SetStatKey
{
	Nil = -1,

	Duration = 0,             // Time

	Sets,                     // Results
	SetsWon,
	SetsLost,

	Points,                   // Scoring
	PointsWon,
	PointsLost,

	PointsWonInWin,
	PointsWonInLoss,

	PointsLostInWin,
	PointsLostInLoss,

	Timeouts,                  // Timeouts
	Timeouts1,
	Timeouts2,

	TimeoutsDuration,

	Timeouts1Score,
	Timeouts2Score,
	Timeouts1OppScore,
	Timeouts2OppScore,

	SideoutAtt,                 // Sideout %
	Sideout,

	SideoutAttOpp,
	SideoutOpp,

	FSSO,                       // First Serve Sideout (FSSO) %
	FSSOOpp,

	FBSO,                       // First Ball Sideout (FBSO) %

	ScoringEarnedOpp,           // Opponent scoring
	ScoringErrsOpp,

	NumKeys
}

// Indices into raw byte array for v2 ISV Summary
public enum StatKey
{
	Nil = -1,

	HittingATT = 0,		     // Skills
	HittingKILL,
	HittingERR,
	HittingNA,

	ServingATT,
	ServingACE,
	ServingERR,
	ServingNA,

	Passing0,
	Passing1,
	Passing2,
	Passing3,

	BlockingATT,
	BlockingBLK,
	BlockingASST,
	BlockingERR,

	SettingATT,
	SettingASST,
	SettingERR,
	SettingNA,

	DefenseATT,
	DefenseDIG,
	DefenseERR,
	DefenseNA,

	AttemptsHitting,        // Total Attempts (must occur after 6x4 skills grid)
	AttemptsServing,
	AttemptsPassing,
	AttemptsBlocking,
	AttemptsSetting,
	AttemptsDefense,
	AttemptsTotal,

	ScoringEarned,          // Scoring
	ScoringErrs,

	PlayingSets,            // Playing Time
	PlayingTime,
	PlayingPoints,

	SubsIn,                 // Substitutions
	SubsOut,

	SwapsIn,                // Libero swaps
	SwapsOut,

	FreeBall0,              // Free ball passing
	FreeBall1,
	FreeBall2,
	FreeBall3,

	ServingTurns,           // Points on serve
	ServingPoints,

	Serving0,               // 0-4 serve rating
	Serving1,
	Serving2,
	Serving3,
	Serving4,

	ScoringPlus,            // +/- scoring
	ScoringMinus,

	NewPass0,				// 0-4 passing
	NewPass1,
	NewPass2,
	NewPass3,
	NewPass4,

	NewFreeBall0,
	NewFreeBall1,
	NewFreeBall2,
	NewFreeBall3,
	NewFreeBall4,

	StatPassUnforcedErr,

	// Separates imported sets played from tally edited value
	ImportPlayingSets,

	NumKeys
}

//
