using System.Collections.Generic;
using System.Text.Json;
namespace Cthangover.Live2D.Cubism.Framework
{
///
/// JSON parser for the Cubism .motion3.json format.
/// Provides typed access to the Meta section (duration, loop, fps, fade times,
/// curve statistics, beziers-restricted flag) and the Curves/UserData sections.
///
/// Parsed curve segments are returned as flat float arrays (interleaved
/// segment-type markers and point data), decoded by .
///
public class CubismMotionJson
{
private readonly JsonDocument _json;
private readonly JsonElement _meta;
private readonly JsonElement _curves;
private readonly JsonElement _userData;
private readonly bool _hasUserData;
private readonly bool _isLoop;
private readonly float _duration;
private readonly float _fps;
private readonly int _curveCount;
private readonly int _totalSegmentCount;
private readonly int _totalPointCount;
private readonly bool _areBeziersRestricted;
///
/// Parses the given .motion3.json bytes and pre-extracts all Meta fields
/// into readonly fields for fast repeated access.
///
public CubismMotionJson(byte[] jsonBytes)
{
_json = JsonDocument.Parse(jsonBytes);
var root = _json.RootElement;
_meta = root.GetProperty("Meta");
_curves = root.GetProperty("Curves");
_hasUserData = root.TryGetProperty("UserData", out _userData);
_duration = _meta.GetProperty("Duration").GetSingle();
_isLoop = _meta.TryGetProperty("Loop", out var loop) && loop.GetBoolean();
_fps = _meta.TryGetProperty("Fps", out var fpsEl) ? fpsEl.GetSingle() : 30f;
_curveCount = _meta.TryGetProperty("CurveCount", out var ccEl) ? ccEl.GetInt32() : 0;
_totalSegmentCount = _meta.TryGetProperty("TotalSegmentCount", out var tscEl) ? tscEl.GetInt32() : 0;
_totalPointCount = _meta.TryGetProperty("TotalPointCount", out var tpcEl) ? tpcEl.GetInt32() : 0;
_areBeziersRestricted = _meta.TryGetProperty("AreBeziersRestricted", out var abrEl) && abrEl.GetBoolean();
}
/// Total motion duration in seconds from the Meta section.
public float GetDuration() => _duration;
/// Whether the motion loops.
public bool IsLoop() => _isLoop;
/// Frames per second, default 30.
public float GetFps() => _fps;
/// Number of curves in this motion.
public int GetCurveCount() => _curveCount;
/// Total segment count across all curves.
public int GetTotalSegmentCount() => _totalSegmentCount;
/// Total point count across all curves.
public int GetTotalPointCount() => _totalPointCount;
///
/// If true, bezier curves use binary search instead of the Cardano formula
/// (non-standard bezier parameterization).
///
public bool AreBeziersRestricted() => _areBeziersRestricted;
///
/// Motion-level fade-in time in seconds, or -1 if not specified.
///
public float GetFadeInTime() =>
_meta.TryGetProperty("FadeInTime", out var v) ? v.GetSingle() : -1f;
///
/// Motion-level fade-out time in seconds, or -1 if not specified.
///
public float GetFadeOutTime() =>
_meta.TryGetProperty("FadeOutTime", out var v) ? v.GetSingle() : -1f;
///
/// Returns the target string for the curve at the given index
/// ("Model", "Parameter", or "PartOpacity").
///
public string GetCurveTarget(int index) =>
_curves[index].GetProperty("Target").GetString();
///
/// Returns the parameter/part/model ID string for the curve at the given index.
///
public string GetCurveId(int index) =>
_curves[index].GetProperty("Id").GetString();
///
/// Per-curve fade-in time in seconds, or -1 if not specified.
///
public float GetCurveFadeInTime(int index) =>
_curves[index].TryGetProperty("FadeInTime", out var v) ? v.GetSingle() : -1f;
///
/// Per-curve fade-out time in seconds, or -1 if not specified.
///
public float GetCurveFadeOutTime(int index) =>
_curves[index].TryGetProperty("FadeOutTime", out var v) ? v.GetSingle() : -1f;
///
/// Reads the raw float array of segment data for a curve.
/// The first element is the segment type marker (cast to int),
/// followed by point data depending on the segment type.
///
public void GetCurveSegments(int index, out float[] segments)
{
var segArr = _curves[index].GetProperty("Segments");
segments = new float[segArr.GetArrayLength()];
int si = 0;
foreach (var s in segArr.EnumerateArray())
segments[si++] = s.GetSingle();
}
/// Number of user-data events in the motion.
public int GetEventCount() => _hasUserData ? _userData.GetArrayLength() : 0;
/// Fire time of the event at the given index, in seconds.
public float GetEventTime(int index) =>
_hasUserData && index < _userData.GetArrayLength()
? _userData[index].GetProperty("Time").GetSingle() : 0f;
/// String value of the event at the given index.
public string GetEventValue(int index) =>
_hasUserData && index < _userData.GetArrayLength()
? _userData[index].GetProperty("Value").GetString() : null;
}
}