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

namespace iStatVball3;

/*
 * Provides functionality for generating individual rows of a non-dimensional data grid. Rows in these grids can be
 * static and not dynamically generated. There can also be dynamic non-dimensional and repeating rows.
 */
public static class GridStatic
{
	/* Constants */
	private const char ArrayDelim = '.';

	/* Methods */

	// Generates static grid data
	public static async Task<List<DataRow>> Generate( JsonGrid json, DataConfig config )
	{
		// Load specified data type
		DataMetrics data = await LoadData( config );

        // Keys are automatic
        CreateKeys( json );

        // Variable number of repeating rows
        if ( json.IsSpecial )
        {
			return GenerateVariable( data.Objects, json );
        }

        // Repeat single row multiple times, otherwise normal individual row
        return json.Repeat > 0 ? GenerateRepeating( data, json ) : GenerateFixed( data, json );
	}

	// Loads data from data set specified in external json
	private static async Task<DataMetrics> LoadData( DataConfig config )
	{
		switch ( config.DataSet )
		{
			// Boxscore
			case DataConfig.BoxscoreData:
			{
				return await MetricsBoxScore.LoadData( config );
			}
            // Match win/loss
            case DataConfig.MatchData:
            {
                MetricsMatch metrics = MetricsMatch.LoadData( config );

                metrics.Calculate();

                return metrics;
            }
			// Match results
			case DataConfig.ResultsData:
            {
				return MetricsResults.LoadData( config );
            }
			// Should not happen
            default:
            {
				return null;
            }
		}
	}

	// Generates multiple rows from single row definition
	private static List<DataRow> GenerateRepeating( object data, JsonGrid json )
	{
		List<DataRow> list = new( json.Rows.Count );

		int colCount = json.Columns.Count;

		// N rows
		for ( int r = 0; r < json.Repeat; r++ )
		{
			JsonRow rowJson = json.Rows[0];

			int rowIndex = (r + 1);

			// Can use row number in header
			string header = rowJson.Header.Replace( "<index>", rowIndex.ToString() );

			// Static text header
			DataRow rowData = new()
			{
				Header = header
			};

			// Generate remaining rows
			for ( int c = 1; c < colCount; c++ )
			{
				GenerateRow( rowData, data, json, 0, c, r );
			}

			list.Add( rowData );
		}

		return list;
	}

	// Generates single fixed row from JSON definition
	private static List<DataRow> GenerateFixed( object data, JsonGrid json )
	{
		int colCount = json.Columns.Count;
		int rowCount = json.Rows.Count;

		List<DataRow> list = new( rowCount );

		// Each row is different
		for ( int r = 0; r < rowCount; r++ )
		{
			JsonRow rowJson = json.Rows[r];

			string header = rowJson.Header;
			bool hasHeader = (header != null);

			// Static header
			DataRow rowData = new()
			{
				Header = header
			};

			// Header optional
			int keyCount = hasHeader ? (colCount - 1) : colCount;

			// Create remaining rows
			for ( int c = 0; c < keyCount; c++ )
			{
				int colIndex = hasHeader ? (c + 1) : c;

				GenerateRow( rowData, data, json, r, colIndex ); 
			}

			list.Add( rowData );
		}

		return list;
	}

    // Generates an individual row (either repeating or fixed)
    private static void GenerateRow( DataRow rowData, object data, JsonGrid json, int rowIndex, int colIndex, int repeatIndex = 0 )
	{
		JsonRow rowJson = json.Rows[ rowIndex ];

		// Must account for optional row header
		int keyIndex = (rowJson.Header == null) ? colIndex : (colIndex - 1);

		// Metrics data key
		string key = rowJson.Keys[ keyIndex ];

		// JSON property key 'ValueN'
		string prop = $"Value{colIndex}";

		object value;

		// Array keys denoted as 'key.index'
		if ( key.Contains( ArrayDelim ) )
		{
			string[] parts = key.Split( ArrayDelim );

			// Separate key/index
			string arrayKey = parts[0];
			string arrayIndex = parts[1];

			// Index 'i' indicates dynamic row index
			int index = (arrayIndex == "i") ? repeatIndex : int.Parse( arrayIndex );

			// Get array metrics data
			Array array = data.GetType().GetProperty( arrayKey )?.GetValue( data ) as Array;

			// Offset into array
			value = array?.GetValue( index );
		}
		// Normal singular value
		else
		{
			value = data.GetType().GetProperty( key )?.GetValue( data );
		}

		JsonColumn colJson = json.Columns[ colIndex ];

		// Convert to type specific value
		value = GetValue( colJson.Type, value );

		// Save in row
		rowData.GetType().GetProperty( prop )?.SetValue( rowData, value );
	}

	// Automatically creates column keys for grid data mapping
	private static void CreateKeys( JsonGrid json )
	{
        IList<JsonRow> rows = json.Rows;
        IList<JsonColumn> columns = json.Columns;

		// Most rows specify header
		bool hasHeader = rows is { Count: > 0 } && (json.Rows[0].Header != null);

		if ( hasHeader )
		{
			columns[0].Key = "Header";
		}

		// Some rows can have dynamic first column (no header)
		int start = hasHeader ? 1 : 0;

		// Remaining columns keyed Value1,Value2...
		for ( int i = start; i < columns.Count; i++ )
		{
			columns[i].Key = $"Value{i}";
		}
	}

    // Generates variable number of non-dimensional rows from data set
    private static List<DataRow> GenerateVariable( List<object> data, JsonGrid json )
    {
        int colCount = json.Columns.Count;
        int rowCount = data.Count;

        List<DataRow> list = new( rowCount );

        // Generate row for each data item
        for ( int r = 0; r < rowCount; r++ )
        {
            DataRow rowData = new();

            // Generate JSON defined column, populate from data
            for ( int c = 0; c < colCount; c++ )
            {
                GenerateColumn( rowData, data[r], json.Columns[c], c );
            }

            list.Add( rowData );
        }

        return list;
    }

    // Generates individual column in non-dimensional row
    private static void GenerateColumn( DataRow rowData, object data, JsonColumn column, int colIndex )
    {
        string field = column.Field;

        // Get metrics value from data
        object value = data.GetType().GetProperty( field )?.GetValue( data );

        // Convert to type specific format
        value = GetValue( column.Type, value );

        // JSON property key 'ValueN'
        string key = $"Value{colIndex}";

        // Save in row
        rowData.GetType().GetProperty( key )?.SetValue( rowData, value );
    }

    // Converts specified generic value to display value, handles NA values
    private static object GetValue( string type, object value )
	{
		object result = value;

		switch ( type )
		{
			// Text uses NA string directly (null is NA)
			case AnalyzeKeys.TextKey:
			{
				result = value ?? Grid.NA;
				break;
			}
			// Int must cast to float for NaN (-1 is NA)
			case AnalyzeKeys.IntKey:
			{
				int intValue = (int)value;
				float floatValue = intValue;
					
				result = (floatValue < 0) ? float.NaN : value;
				break;
			}
			// Float types use NaN (-1/NaN is NA)
			case AnalyzeKeys.FloatKey:
			case AnalyzeKeys.PercentKey:
			{
				float floatValue = (float)value;

				result = (float.IsNaN( floatValue ) || (floatValue < 0)) ? float.NaN : value;
				break;
			}
			// Negative values valid for fixed (NaN is NA)
			case AnalyzeKeys.FixedKey:
			{
				float floatValue = (float)value;

				result = float.IsNaN( floatValue ) ? float.NaN : value;
				break;
			}
		}

		return result;
	}
}

//
