using System.Text.Json;
using Cs2Ai;
using ProtoBuf;

var items = new List<GameData>();


foreach (var filePath in Directory.EnumerateFiles("H:\\cs2 ai\\dataset"))
{
    using var file = File.OpenRead(filePath);
    try
    {
        var data = Serializer.Deserialize<GameData>(file);
        if (data.Player1Data.Frames.Count <= 800)
            items.Add(data);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error deserializing file {filePath}: {ex.Message}");
    }
}

Console.WriteLine("Normalization started");
Normalizer.Normalize(items);
Console.WriteLine("Normalization completed");

var dataset = new List<DatasetGame>();

foreach (var gameData in items)
{
    dataset.Add(new DatasetGame
    {
        States = GetStates(gameData),
    });
}

foreach (var datasetGame in dataset)
{
    foreach (var datasetGameState in datasetGame.States)
    {
        foreach (var floats in datasetGameState)
        {
            for (var index = 0; index < floats.Length; index++)
            {
                var f = floats[index];
                if (f > 1)
                    Console.WriteLine($"AAAA {f} {index}");
            }
        }
    }
}

Console.WriteLine($"Dataset count: {dataset.Count}");
Console.WriteLine($"First state length: {dataset.First().States[0][0].Length}");
Console.WriteLine($"Max state count: {dataset.Max(d => d.States.Count)}");

var allStates = dataset.SelectMany(d => d.States);
Console.WriteLine($"Average value at index 17: {allStates.Average(d => d[0][4])}");

File.WriteAllText("H:\\cs2 ai\\dataset.json", JsonSerializer.Serialize(dataset));


List<float[][]> GetStates(GameData game)
{
    Normalizer.NormalizeProperty(game.Player1Data.Frames.Concat(game.Player2Data.Frames).ToList(), f => f.TickedTime,
        (f, val) => f.TickedTime = val);

    var states = new List<float[][]>(game.Player1Data.Frames.Count);
    for (var i = 0; i < game.Player1Data.Frames.Count; i++)
    {
        var state1 = new List<float>();
        FrameToState(state1, game.Player1Data.Frames[i]);
        var state2 = new List<float>();
        FrameToState(state2, game.Player2Data.Frames[i]);

        states.Add(new[]
        {
            state1.ToArray(), state2.ToArray()
        });
    }

    return states;
}

void FrameToState(List<float> state, FrameData frame)
{
    state.Add(frame.TickedTime);
    Flatten(state, frame.Angles);
    Flatten(state, frame.Position);
    Flatten(state, frame.Velocity);
    Flatten(state, frame.PunchAngle);
}

void Flatten(List<float> consumer, float[] array)
{
    if (array != null)
    {
        foreach (var f in array)
        {
            consumer.Add(f);
        }
    }
}

class DatasetGame
{
    public List<float[][]> States { get; set; }
}

public static class Normalizer
{
    public static void Normalize(List<GameData> gameDataList)
    {
        if (gameDataList == null || gameDataList.Count == 0) throw new ArgumentNullException(nameof(gameDataList));

        var allFrames = gameDataList.SelectMany(gd => gd.Player1Data.Frames.Concat(gd.Player2Data.Frames)).ToList();

        if (allFrames.Count == 0) return;

        //NormalizeProperty(allFrames, f => f.TickedTime, (f, val) => f.TickedTime = val);
        NormalizeArrayProperty(allFrames, f => f.Position);
        NormalizeArrayProperty(allFrames, f => f.Angles);
        NormalizeArrayProperty(allFrames, f => f.Velocity);
        NormalizeProperty(allFrames, f => f.Health, (f, val) => f.Health = (int)val);
        NormalizeArrayProperty(allFrames, f => f.PunchAngle);
    }

    public static void NormalizeProperty(List<FrameData> frames, Func<FrameData, float> selector,
        Action<FrameData, float> setter)
    {
        float min = frames.Min(selector);
        float max = frames.Max(selector);
        float range = max - min;
        if (range == 0) return;

        foreach (var frame in frames)
        {
            var newValue = (selector(frame) - min) / range;
            if(newValue > 1)
                Console.WriteLine("wtf");
            setter(frame, (selector(frame) - min) / range);
        }
    }

    private static void NormalizeArrayProperty(List<FrameData> frames, Func<FrameData, float[]> selector)
    {
        int length = selector(frames.First()).Length;
        for (int i = 0; i < length; i++)
        {
            int index = i;
            NormalizeProperty(frames, f => selector(f)[index], (f, val) => selector(f)[index] = val);
        }
    }
}

find a bug, this not print wtf:
if(newValue > 1)
                Console.WriteLine("wtf");

but somehow there values > 1