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

/*
 * Helper class responsible for processing all data necessary to populate a TrendView line graph. Stats are aggregated,
 * accumulated, and calculated here.
 */
public class TrendGraph
{
	/* Constants */
	public const string TeamKey = "_team";

	/* Properties */

	// Graph data
	public Dictionary<string,List<TrendDatum>> Data { get; private set; }

	// External ref
	public TrendView View { get; set; }

	/* Fields */
	private JsonTrend jsonTrend;

	/* Methods */
	public TrendGraph()
	{
		Data = new Dictionary<string,List<TrendDatum>>();
	}

	// Post construction initialization
	public void Init( JsonTrend json )
	{
		jsonTrend = json;
	}

	// Accumulates, calculates, and (re)displays all data for this graph
	public async Task UpdateData( DataConfig config )
	{
		View.Clear();

		// Reset data series
		Data.Clear();
		Data.Add( TeamKey, [] );

		string scope = config.Scope;

		// Generate all data series based on scope
		switch ( scope )
		{
			case DataConfig.MatchScope: await ProcessMatch( config ); break;
			case DataConfig.TournamentDayScope: await ProcessDay( config ); break;
			case DataConfig.TournamentScope: await ProcessTournament( config ); break;
			case DataConfig.SeasonScope: await ProcessSeason( config ); break;
			case DataConfig.TeamScope: await ProcessTeam( config ); break;
		}

		// Add team series to graph (MUST be first)
		View.AddSeries( Data[ TeamKey ], true );

		// Add all player series to graph
		foreach ( string key in Data.Keys )
		{
			if ( key != TeamKey )
			{
				View.AddSeries( Data[ key ], false );
			}
		}

		// Scope specific config
		View.UpdateScope( scope );
	}

	// Processes trend series for Match scope
	private async Task ProcessMatch( DataConfig config )
	{
		// Sorted by set number
		if ( config.ScopeObject is Match match )
		{
			List<Set> sets = match.Sets.OrderBy( s => s.Number ).ToList();

			// Process each set in match
			foreach ( Set set in sets )
			{
				if ( set.IsEnded )
				{
					// Aggregate
					DataStats stats = await set.Aggregate();
					stats.RawStats = stats;

					// X value
					string label = set.Name;

					// Y value(s)
					ProcessY( config, stats, label );
				}
			}
		}
	}

	// Processes trend series for Tournament Day scope
	private async Task ProcessDay( DataConfig config )
	{
		// Sorted by match date
		if ( config.ScopeObject is TournamentDay day )
		{
			List<Match> matches = day.Matches.OrderBy( m => m.MatchTime ).ToList();

			// Each match in day is data point
			foreach ( Match match in matches )
			{
				if ( match.IsEnded )
				{
					// Aggregate
					DataStats stats = await match.Aggregate();
					stats.RawStats = stats;

					// X value
					string label = GetMatchLabel( match );

					// Y value(s)
					ProcessY( config, stats, label );
				}
			}
		}
	}

	// Processes trend series for Tournament scope
	private async Task ProcessTournament( DataConfig config )
	{
		// Sorted by match date
		if ( config.ScopeObject is TournamentDay tournament )
		{
			List<Match> matches = tournament.Matches.OrderBy( m => m.MatchTime ).ToList();

			// Each match in tournament is data point
			foreach ( Match match in matches )
			{
				if ( match.IsEnded )
				{
					// Aggregate
					DataStats stats = await match.Aggregate();
					stats.RawStats = stats;

					// X value
					string label = GetMatchLabel( match );

					// Y value(s)
					ProcessY( config, stats, label );
				}
			}
		}
	}

	// Processes trend series for Season scope
	private async Task ProcessSeason( DataConfig config )
	{
		// Optionally filter matches
		if ( config.ScopeObject is Season season )
		{
			List<Match> matches = season.FilterMatches( config.TagFilter );

			// Sorted by match date
			List<Match> sorted = matches.OrderBy( m => m.MatchTime ).ToList();

			// Each match in season is data point
			foreach ( Match match in sorted )
			{
				if ( match.IsEnded )
				{
					// Aggregate
					DataStats stats = await match.Aggregate();
					stats.RawStats = stats;

					// X value
					string label = GetMatchLabel( match );

					// Might have multiple matches on same day
					int count = CountLabels( label );

					// 'FOO 12/23 #2'
					if ( count > 0 )
					{
						label = $"{label} #{(count + 1)}";
					}

					// Y value(s)
					ProcessY( config, stats, label );
				}
			}
		}
	}

	// Processes trend series for Team scope
	private async Task ProcessTeam( DataConfig config )
	{
		// Sorted by season date
		if ( config.ScopeObject is Team team )
		{
			List<Season> seasons = team.Seasons.OrderBy( s => s.EndDate ).ToList();

			// Each season in team is data point
			foreach ( Season season in seasons )
			{
				// Aggregate
				DataStats stats = await season.Aggregate();
				stats.RawStats = stats;

				// X value
				string label = season.Name;

				// Y value(s)
				ProcessY( config, stats, label );
			}
		}
	}

	// Accumulates and calculates all Y values for X value
	private void ProcessY( DataConfig config, DataStats stats, string label )
	{
		// Accumulate
		AccumulateDimension( config, stats );
		DataDimension dimension = config.Dimension;

		// Calculate
		dimension.CountSets( stats );
		dimension.Calculate();

		// Do NOT sort keys
		var keys = dimension.Metrics.Keys;

		// Add series Y value for each player
		foreach ( string key in keys )
		{
			DataMetrics metrics = dimension.Metrics[ key ];

			float attempts = metrics.GetFloat( jsonTrend.Attempt );

			// Only include if player had attempts
			if ( attempts > 0 )
			{
				float floatValue = metrics.GetFloat( jsonTrend.Metric );

				// Create data point
				TrendDatum datum = new( label, floatValue );

				// Add to series (keyed by player unique ID)
				if ( !Data.TryGetValue( key, out List<TrendDatum> value ) )
				{
                    value = [];
                    Data.Add( key, value);
				}

                value.Add( datum );
			}
		}

		// Calc team totals
		DataMetrics totals = config.Totals;

		totals.CountSets( stats );
		totals.Calculate();

		float totalValue = totals.GetFloat( jsonTrend.Metric );

		// Create data point
		TrendDatum totalDatum = new( label, totalValue );

		// Add to series
		Data[ TeamKey ].Add( totalDatum );
	}

	// Accumulates all dimension metrics based on analysis data set
	private static void AccumulateDimension( DataConfig config, DataStats stats )
	{
		switch ( config.DataSet )
		{
			// Skills
			case DataConfig.SkillsData:
			{
				MetricsSkillAccumulator.AccumulateDimension( config, stats );
				break;
			}
			// Sideout
			case DataConfig.SideoutData:
			{
				MetricsSideout.AccumulateDimension( config, stats );
				break;
			}
			// Scoring
			case DataConfig.ScoringData:
			{
				MetricsScoring.AccumulateDimension( config, stats );
				break;
			}
			// Playing
			case DataConfig.PlayingData:
			{
				MetricsPlaying.AccumulateDimension( config, stats );
				break;
			}
		}
	}

	// Counts number occurrences of specified label in data set so far
	private int CountLabels( string label )
	{
		int count = 0;

		// Count occurrences
		foreach ( TrendDatum datum in Data[ TeamKey ] )
		{
			if ( datum.Label.Equals( label ) )
			{
				count++;
			}
		}

		return count;
	}

	// Returns X axis label for specified match
	private static string GetMatchLabel( Match match )
	{
		string opponent = match.GetOpponentAbbrev();
		string date = DXUtils.LabelFromDate( match.MatchTime, false );

		// 'FOO M/DD'
		return $"{opponent} {date}";
	}
}

//
