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

/*
 * Base class for all categories of encapsulated metrics (skill, sideout, etc) within a data dimension. Also used for
 * dimensional totals.
 */ 
public abstract class DataMetrics
{
	/* Constants */

	// Undefined magic numbers
	public const float FloatNA = float.NaN;
	public const int IntNA = -1;

	/* Properties */
	public string Type { get; set; }
	public string TypeKey { get; set; }
	public string TypeLabel { get; set; }

	public object TypeObject { get; set; }

	public string Key { get; set; }
	public string Label { get; set; }

	// Team totals?
	public bool IsTotal { get; private set; }

	// Total number sets played for this dimension entry (or total)
	public int SetsPlayed { get; set; }

	// Total number points played for this dimension (player)
	public int TotalPoints { get; private set; }

	// Contains data for at least 1 set?
	public bool IsEmpty => sets.Count == 0;

	// Any stats in this data set recorded using RallyFlow?
	public bool HasRally { get; set; }

	// For static (results table) use only
    public List<object> Objects { get; set; }

    /* Fields */
    private readonly List<Set> sets;

	/* Methods */
	protected DataMetrics( bool total = false )
	{
		IsTotal = total;

		// Required for counting sets played
		sets = [];
	}

	// Uses reflection to return integer for specified metric key
	public int GetInt( string key )
	{
		object value = DXUtils.GetProperty( this, key );

		return Convert.ToInt32( value );
	}

	// Uses reflection to return float for specified metric key
	public float GetFloat( string key )
	{
		object value = DXUtils.GetProperty( this, key );

		return (value == null) ? FloatNA : Convert.ToSingle( value );
	}

	// Adds specified set to list of sets played
	public void AddSetPlayed( Set set )
	{
		if ( !sets.Contains( set ) )
		{
			sets.Add( set );

			TotalPoints += set.TotalPoints;
		}
	}

	// Counts total number of sets played for this dimension entry (or totals)
	public void CountSets( DataStats stats )
	{
		DataStats rawStats = stats.RawStats;

		// Total takes count from sets aggregated (regardless of played)
		if ( IsTotal || (Type != KeyDimension.PlayerKey) )
		{
			SetsPlayed = rawStats.SetCount;
			TotalPoints += rawStats.TotalPoints;
		}
		// Check all point events to see if player on court
		else
		{
			Player player = TypeObject as Player;

			foreach ( Stat stat in rawStats )
			{
				if ( stat.IsPoint )
				{
					StatPoint point = stat.Point;
					Season season = stat.Set.Match.Season;

					// MUST ensure point lineup populated here 
					point.Populate( season );

					// Player played in this set
					if ( point.IsPlayerOnCourt( player ) )
					{
						AddSetPlayed( stat.Set );
					}
				}
			}

			// Count unique sets
			SetsPlayed += sets.Count;
		}
	}

	// Accumulates variables and calculates statistics for all stats in list (used for dashboards)
	public static async Task<DataMetrics> ProcessTotals( DataConfig config, DataStats stats )
	{
		#if DEBUG
			DXProfiler.Start();
		#endif

		string dataSet = config.DataSet;

		// Create data specific type
		DataMetrics metrics = CreateMetrics( dataSet );

		// Accumulate all variables
		foreach ( Stat stat in stats )
		{
			metrics.Accumulate( config, stat );

			// Can have up to 3 block assists per stat
			if ( stat.Result == Stats.BlockAssistsKey )
			{
				metrics.Accumulate( config, stat, 2 );
				metrics.Accumulate( config, stat, 3 );
			}
		}

		// Must count sets played
		metrics.CountSets( stats );

		// Include raw summary data
		if ( config.IsTeam1 && (dataSet == DataConfig.SkillsData) )
		{
			await MetricsSkillRaw.AccumulateTotals( config, (metrics as MetricsSkill) );
		}

		// Calculate statistics
		metrics.Calculate();

		#if DEBUG
			DXProfiler.Mark( "metrics.totals", true );
		#endif

		return metrics;
	}

	// Creates metric object for specified data set type
	private static DataMetrics CreateMetrics( string dataSet )
	{
		return dataSet switch
		{
			DataConfig.SkillsData => new MetricsSkill( true ),
			DataConfig.SideoutData => new MetricsSideout( true ),
			DataConfig.PlayingData => new MetricsPlaying( true ),
			DataConfig.ScoringData => new MetricsScoring( true ),
			DataConfig.MatchData => new MetricsMatch( true ),

			// Error
			_ => null,
		};
	}

	// Returns player from list matching specified unique ID
	public static Player GetPlayer( List<Player> players, string playerId )
	{
		if ( playerId != null )
		{
			// Find match
			foreach ( Player player in players )
			{
				if ( player.EqualsId( playerId ) )
				{
					return player;
				}
			}
		}

		// No match
		return null;
	}

    // Returns list of all matches in specified scope
    protected static List<Match> GetMatches( DataConfig config )
    {
        object obj = config.ScopeObject;

        // NA for lineup, set
        return config.Scope switch
        {
            DataConfig.OrganizationScope => ((Organization)obj).GetMatches(),
            DataConfig.TeamScope => ((Team)obj).GetMatches(),
            DataConfig.SeasonScope => ((Season)obj).GetMatches(),
            DataConfig.TournamentScope => ((Tournament)obj).GetMatches(),
			DataConfig.TournamentDayScope => ((TournamentDay)obj).GetMatches(),
            DataConfig.MatchScope => [ (Match)obj ],
            DataConfig.StatisticianScope => ((Statistician)obj).GetMatches(),
            DataConfig.OpponentScope => ((Opponent)obj).GetMatches(),
            DataConfig.VenueScope => ((Venue)obj).GetMatches(),

            _ => null,
        };
    }

    /* Abstracts */

    // Accumulates all variables for specified stat
    public abstract void Accumulate( DataConfig config, Stat stat, int playerNum = 1 );

	// Calculates all statistics based on pre-accumulated variables
	public abstract void Calculate();
}

//
