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

namespace iStatVball3;

/*
 * Used to add raw stat summary data (from v2 ISV import or local editing) into running metric and team totals for stats
 * analysis.
 */
public static class MetricsSkillRaw
{
	/* Methods */

	// Adds raw summary stats into specified accumulated metrics and totals
	public static async Task Accumulate( DataConfig config )
	{
		// Aggregate total summary for scope
		StatSummary summary = await Aggregate( config );

		// Might not have summary data
		if ( summary is { HasPlayerData: true } )
		{
			var metricsList = config.Dimension?.Metrics;

			if ( metricsList != null )
			{
				// Add any new dimensions from v2 data
				BuildDimensions( config, summary );

				MetricsSkill totals = config.Totals as MetricsSkill;
				var stats = summary.PlayerStats;

				// Accumulate stats for each player and totals
				foreach ( string key in stats.Keys )
				{
					string rootId = GetPlayer( config, key )?.RootId;

					// Key mapped to root table
					if ( (rootId != null) && metricsList.TryGetValue( rootId, out var value ) )
					{
						MetricsSkill metrics = value as MetricsSkill;
						var raw = stats[ key ];

						// Add summary for this player into accumulated metrics
						Add( metrics, raw );

						config.HasSummary = true;

						// Also add into running totals
						Add( totals, raw );
					}
				}

				// Do NOT override totals SetsPlayed

				// Populates player names/numbers if applicable
				PopulatePlayers( config );
			}
		}
	}

	// Adds raw summary stats into specified accumulated totals
	public static async Task AccumulateTotals( DataConfig config, MetricsSkill totals )
	{
		// Aggregate total summary for scope
		StatSummary summary = await Aggregate( config );

		if ( summary != null )
		{
			var stats = summary.PlayerStats;

			// Accumulate stats for each player and totals
			foreach ( string rootId in stats.Keys )
			{
				var raw = stats[ rootId ];

				Add( totals, raw );
			}
		}
	}

	// Aggregates all raw summary data within scope
	private static async Task<StatSummary> Aggregate( DataConfig config )
	{
		object obj = config.ScopeObject;

		return config.Scope switch
		{
			DataConfig.SetScope		      => ((Set)obj).AggregateRaw(),
			DataConfig.MatchScope	      => ((Match)obj).AggregateRaw(),
			DataConfig.TournamentDayScope => ((TournamentDay)obj).AggregateRaw(),
			DataConfig.TournamentScope    => ((Tournament)obj).AggregateRaw(),
			DataConfig.SeasonScope	      => await ((Season)obj).AggregateRaw( config.TagFilter ),

			// Career
			DataConfig.TeamScope		  => await ((Team)obj).AggregateRaw(),
			DataConfig.OrganizationScope  => await ((Organization)obj).AggregateRaw(),

			// Tags
			DataConfig.LineupScope		  => ((Lineup)obj).AggregateRaw(),
			DataConfig.StatisticianScope  => ((Statistician)obj).AggregateRaw(),
			DataConfig.OpponentScope	  => ((Opponent)obj).AggregateRaw(),
			DataConfig.VenueScope		  => ((Venue)obj).AggregateRaw(),

			_ => null,
		};
	}

	// Creates dimensions for v2 data based on raw summary
	private static void BuildDimensions( DataConfig config, StatSummary summary )
	{
		foreach ( string rootId in summary.PlayerStats.Keys )
		{
			BuildDimension( config, rootId );
		}
	}

	// Creates individual dimension metric for specified player
	private static void BuildDimension( DataConfig config, string rootId )
	{
		var metricsList = config.Dimension.Metrics;

		// Lookup player in season
		Player player = GetPlayer( config, rootId );

		if ( player != null )
		{
			// Do NOT include in dimension if user does not have access to
			// specified player or if player not in dimension filter.
			//
			if ( config.HasAccess( rootId ) && config.FilterDimension( player ) )
			{
				// Map to root table if needed
				string key = player.RootId;

				// 'Jane Doe'
				string label = player.GetAnalyzeName( config.MaxLabelLen );

				// Create metrics object for player
				if ( !metricsList.ContainsKey( key ) )
				{
					MetricsSkill metrics = new()
					{
						// Populate dimensional info
						Type = KeyDimension.PlayerKey,
						TypeKey = null,
						TypeLabel = null,

						// Object associated with dimension
						TypeObject = player,

						Key = key,
						Label = label,

						// Base values
						Player_Number = player.Number,
						Player_NamePos = player.GetNamePosition()
					};

					// Add to list
					metricsList.Add( key, metrics );
				}
			}
		}
	}

	/* Get Player */

	// Finds player within scope
	private static Player GetPlayer( DataConfig config, string key )
	{
		return config.Scope switch
		{
			DataConfig.OrganizationScope => GetOrgPlayer( (Organization)config.ScopeObject, key ),
			DataConfig.TeamScope => GetTeamPlayer( (Team)config.ScopeObject, key ),

			_ => GetSeasonPlayer( config, key ),
		};
	}

	// Finds player within organization scope
	private static Player GetOrgPlayer( Organization org, string key )
	{
		// Search each team
		foreach ( Team team in org.Teams )
		{
			Player player = GetTeamPlayer( team, key );

			if ( player != null )
			{
				return player;
			}
		}

		return null;
	}

	// Finds player within team scope
	private static Player GetTeamPlayer( Team team, string key )
	{
		// Search each season
		foreach ( Season season in team.Seasons )
		{
			Player player = season.GetRootPlayer( key );

			if ( player != null )
			{
				return player;
			}
		}

		return null;
	}

	// Finds player within any scope season or lower
	private static Player GetSeasonPlayer( DataConfig config, string key )
	{
		Season season = GetSeason( config );

		// Lookup by ID
		return season?.GetRootPlayer( key );
	}

	// Returns season matching specified configuration
	private static Season GetSeason( DataConfig config )
	{
		// Set/match/etc
		object obj = config.ScopeObject;

		// Access season based on scope
		return config.Scope switch
		{
			DataConfig.SetScope =>		     ((Set)obj).Match.Season,
			DataConfig.MatchScope =>	     ((Match)obj).Season,
			DataConfig.TournamentDayScope => ((TournamentDay)obj).Tournament.Season,
			DataConfig.TournamentScope =>    ((Tournament)obj).Season,
			DataConfig.SeasonScope =>	     (Season)obj,

			_ => null,
		};
	}

	/* Populate */

	// Populates player names/numbers for BoxScore grid
	private static void PopulatePlayers( DataConfig config )
	{
		if ( (config.DataSet == DataConfig.SkillsData) && (config.Scope == DataConfig.MatchScope) )
		{
			Match match = config.ScopeObject as Match;
			Season season = match?.Season;

			// Player dimension
			var metricsList = config.Dimension.Metrics;

			// Add name/number for each player
			foreach ( string playerId in metricsList.Keys )
			{
				if ( metricsList[ playerId ] is MetricsSkill metrics )
				{
					// May have already been defined
					if ( string.IsNullOrEmpty( metrics.Player_Number ) )
					{
						Player player = season?.GetPlayer( playerId );

						// 'Jane Smith (OH)'
						if ( player != null )
						{
							metrics.Player_Number = player.Number;
							metrics.Player_NamePos = player.GetNamePosition();
						}
					}
				}
			}
		}
	}

	// Adds all values from specified raw summary to accumulated metrics
	private static void Add( MetricsSkill metrics, IDictionary<string,int> raw )
	{
		if ( metrics != null )
		{
			// Add imported sets played
			metrics.SetsPlayed += GetValue( raw, StatKey.ImportPlayingSets );

			// Do NOT add sets played to total
			if ( !metrics.IsTotal )
			{
				metrics.SetsPlayed += GetValue( raw, StatKey.PlayingSets );
			}

			// Scoring
			metrics.Scoring_Points += GetValue( raw, StatKey.ScoringEarned );

			// Serving
			metrics.Serve_Zeros  += GetValue( raw, StatKey.ServingATT );
			metrics.Serve_Aces   += GetValue( raw, StatKey.ServingACE );
			metrics.Serve_Errors += GetValue( raw, StatKey.ServingERR );

			metrics.Serve_Points += GetValue( raw, StatKey.ServingPoints );

			metrics.Serve_Rating0 += GetValue( raw, StatKey.Serving0 );
			metrics.Serve_Rating1 += GetValue( raw, StatKey.Serving1 );
			metrics.Serve_Rating2 += GetValue( raw, StatKey.Serving2 );
			metrics.Serve_Rating3 += GetValue( raw, StatKey.Serving3 );
			metrics.Serve_Rating4 += GetValue( raw, StatKey.Serving4 );

			// Setting
			metrics.Set_Zeros   += GetValue( raw, StatKey.SettingATT );
			metrics.Set_Assists += GetValue( raw, StatKey.SettingASST );
			metrics.Set_Errors  += GetValue( raw, StatKey.SettingERR );

			// Hitting
			metrics.Attack_Zeros  += GetValue( raw, StatKey.HittingATT );
			metrics.Attack_Kills  += GetValue( raw, StatKey.HittingKILL );
			metrics.Attack_Errors += GetValue( raw, StatKey.HittingERR );

			// Blocking
			metrics.Block_Zeros   += GetValue( raw, StatKey.BlockingATT );
			metrics.Block_Blocks  += GetValue( raw, StatKey.BlockingBLK );
			metrics.Block_Assists += GetValue( raw, StatKey.BlockingASST );
			metrics.Block_Errors  += GetValue( raw, StatKey.BlockingERR );

			// Defense
			metrics.Defense_Zeros += GetValue( raw, StatKey.DefenseATT );
			metrics.Defense_Digs += GetValue( raw, StatKey.DefenseDIG );
			metrics.Defense_Errors += GetValue( raw, StatKey.DefenseERR );

			// Add rating based categories
			AddReceive( metrics, raw );
			AddPassing( metrics, raw );
		}
	}

	// Adds serve receive with 0-4 to 0-3 mapping
	private static void AddReceive( MetricsSkill metrics, IDictionary<string,int> raw )
	{
		// Map ISV2 0-3 to 0-4
		int recv0 = GetValue( raw, StatKey.Passing0 );
		int recv1 = 0;
		int recv2 = GetValue( raw, StatKey.Passing1 );
		int recv3 = GetValue( raw, StatKey.Passing2 );
		int recv4 = GetValue( raw, StatKey.Passing3 );

		// Add ISV2/ISV3 0-4
		recv0 += GetValue( raw, StatKey.NewPass0 );
		recv1 += GetValue( raw, StatKey.NewPass1 );
		recv2 += GetValue( raw, StatKey.NewPass2 );
		recv3 += GetValue( raw, StatKey.NewPass3 );
		recv4 += GetValue( raw, StatKey.NewPass4 );

		bool recv03 = Shell.Settings.IsPass03;

		// 0-3/0-4 already correct here, NO additional mapping
        metrics.Receive_Rating0 += recv0;
        metrics.Receive_Rating1 += recv1;
        metrics.Receive_Rating2 += recv2;
        metrics.Receive_Rating3 += recv3;
        metrics.Receive_Rating4 += recv03 ? 0 : recv4;

        metrics.Receive_Zeros += (recv1 + recv2 + recv3 + recv4);
		metrics.Receive_Errors += recv0;
	}

	// Adds other passing (1st Ball, Freeball, etc) with 0-4 to 0-3 mapping
	private static void AddPassing( MetricsSkill metrics, IDictionary<string, int> raw )
	{
		// Map ISV2 0-3 to 0-4
		int pass0 = GetValue( raw, StatKey.FreeBall0 );
		int pass1 = 0;
		int pass2 = GetValue( raw, StatKey.FreeBall1 );
		int pass3 = GetValue( raw, StatKey.FreeBall2 );
		int pass4 = GetValue( raw, StatKey.FreeBall3 );

		// Add ISV2 0-4
		pass0 += GetValue( raw, StatKey.NewFreeBall0 );
		pass1 += GetValue( raw, StatKey.NewFreeBall1 );
		pass2 += GetValue( raw, StatKey.NewFreeBall2 );
		pass3 += GetValue( raw, StatKey.NewFreeBall3 );
		pass4 += GetValue( raw, StatKey.NewFreeBall4 );

		bool pass03 = Shell.Settings.IsPass03;

		// Display as 0-3 or 0-4
		metrics.Receive_Rating0 += pass03 ? (pass0 + pass1) : pass0;
		metrics.Receive_Rating1 += pass03 ? pass2 : pass1;
		metrics.Receive_Rating2 += pass03 ? pass3 : pass2;
		metrics.Receive_Rating3 += pass03 ? pass4 : pass3;
		metrics.Receive_Rating4 += pass03 ? 0 : pass4;

		metrics.Receive_Zeros += (pass1 + pass2 + pass3 + pass4);
		metrics.Receive_Errors += pass0;
	}

	// Returns value from raw summary data for specified stat key
	private static int GetValue( IDictionary<string,int> raw, StatKey key )
	{
		string keyStr = key.ToString();

		// Default 0
		return (raw.TryGetValue( keyStr, out var value ) ? value : 0);
	}
}

//
