using FluentAvalonia.Core;
using MsgPack;
using Ryujinx.Ava.Utilities.AppLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.PlayReport
{
///
/// A mapping of title IDs to value formatter specs.
///
/// Generally speaking, use the .AddSpec(...) methods instead of creating this class yourself.
///
public class GameSpec
{
private int _lastPriority;
public required string[] TitleIds { get; init; }
public List ValueFormatters { get; } = [];
///
/// Add a value formatter to the current
/// matching a specific key that could exist in a Play Report for the previously specified title IDs.
///
/// The key name to match.
/// The function which can return a potential formatted value.
/// The current , for chaining convenience.
public GameSpec AddValueFormatter(string reportKey, SingleValueFormatter valueFormatter)
=> AddValueFormatter(_lastPriority++, reportKey, valueFormatter);
///
/// Add a value formatter at a specific priority to the current
/// matching a specific key that could exist in a Play Report for the previously specified title IDs.
///
/// The resolution priority of this value formatter. Higher resolves sooner.
/// The key name to match.
/// The function which can return a potential formatted value.
/// The current , for chaining convenience.
public GameSpec AddValueFormatter(int priority, string reportKey,
SingleValueFormatter valueFormatter)
{
ValueFormatters.Add(new FormatterSpec
{
Priority = priority, ReportKeys = [reportKey], Formatter = valueFormatter
});
return this;
}
///
/// Add a multi-value formatter to the current
/// matching a specific set of keys that could exist in a Play Report for the previously specified title IDs.
///
/// The key names to match.
/// The function which can format the values.
/// The current , for chaining convenience.
public GameSpec AddMultiValueFormatter(string[] reportKeys, MultiValueFormatter valueFormatter)
=> AddMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter);
///
/// Add a multi-value formatter at a specific priority to the current
/// matching a specific set of keys that could exist in a Play Report for the previously specified title IDs.
///
/// The resolution priority of this value formatter. Higher resolves sooner.
/// The key names to match.
/// The function which can format the values.
/// The current , for chaining convenience.
public GameSpec AddMultiValueFormatter(int priority, string[] reportKeys,
MultiValueFormatter valueFormatter)
{
ValueFormatters.Add(new MultiFormatterSpec
{
Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter
});
return this;
}
///
/// Add a multi-value formatter to the current
/// matching a specific set of keys that could exist in a Play Report for the previously specified title IDs.
///
/// The 'Sparse' multi-value formatters do not require every key to be present.
/// If you need this requirement, use .
///
/// The key names to match.
/// The function which can format the values.
/// The current , for chaining convenience.
public GameSpec AddSparseMultiValueFormatter(string[] reportKeys, SparseMultiValueFormatter valueFormatter)
=> AddSparseMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter);
///
/// Add a multi-value formatter at a specific priority to the current
/// matching a specific set of keys that could exist in a Play Report for the previously specified title IDs.
///
/// The 'Sparse' multi-value formatters do not require every key to be present.
/// If you need this requirement, use .
///
/// The resolution priority of this value formatter. Higher resolves sooner.
/// The key names to match.
/// The function which can format the values.
/// The current , for chaining convenience.
public GameSpec AddSparseMultiValueFormatter(int priority, string[] reportKeys,
SparseMultiValueFormatter valueFormatter)
{
ValueFormatters.Add(new SparseMultiFormatterSpec
{
Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter
});
return this;
}
}
///
/// A struct containing the data for a mapping of a key in a Play Report to a formatter for its potential value.
///
public class FormatterSpec : FormatterSpecBase
{
public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
{
if (!playReport.ReportData.AsDictionary().TryGetValue(ReportKeys[0], out MessagePackObject valuePackObject))
{
result = null;
return false;
}
result = valuePackObject;
return true;
}
}
///
/// A struct containing the data for a mapping of an arbitrary key set in a Play Report to a formatter for their potential values.
///
public class MultiFormatterSpec : FormatterSpecBase
{
public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
{
List packedObjects = [];
foreach (var reportKey in ReportKeys)
{
if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject))
{
result = null;
return false;
}
packedObjects.Add(valuePackObject);
}
result = packedObjects;
return true;
}
}
///
/// A struct containing the data for a mapping of an arbitrary key set in a Play Report to a formatter for their sparsely populated potential values.
///
public class SparseMultiFormatterSpec : FormatterSpecBase
{
public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
{
Dictionary packedObjects = [];
foreach (var reportKey in ReportKeys)
{
if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject))
continue;
packedObjects.Add(reportKey, valuePackObject);
}
result = packedObjects;
return true;
}
}
public abstract class FormatterSpecBase
{
public abstract bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object data);
public int Priority { get; init; }
public string[] ReportKeys { get; init; }
public Delegate Formatter { get; init; }
public bool Format(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport, out FormattedValue formattedValue)
{
formattedValue = default;
if (!GetData(playReport, out object data))
return false;
if (data is FormattedValue fv)
{
formattedValue = fv;
return true;
}
switch (Formatter)
{
case SingleValueFormatter svf when data is MessagePackObject mpo:
formattedValue = svf(new SingleValue(mpo) { Application = appMeta, PlayReport = playReport });
return true;
case MultiValueFormatter mvf when data is List messagePackObjects:
formattedValue = mvf(new MultiValue(messagePackObjects) { Application = appMeta, PlayReport = playReport });
return true;
case SparseMultiValueFormatter smvf when
data is Dictionary sparseMessagePackObjects:
formattedValue = smvf(new SparseMultiValue(sparseMessagePackObjects) { Application = appMeta, PlayReport = playReport });
return true;
default:
throw new InvalidOperationException("Formatter delegate is not of a known type!");
}
}
}
}