using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using InnovEnergy.S3.Metadata;
using InnovEnergy.Time.Unix;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.Time.Unix.UnixTimeSpan;

namespace InnovEnergy.S3;

public enum MyEnum
{
    Foo = 0x01,
    Bar = 0x02,
    Baz = 0x04
}


public record Test(Double X, Double Y, Double Z);




public static class Program
{
    private static readonly Random Rng = new Random(0);






    public static void Main(String[] args)
    {

        // var x = Observable
        //          .Range(0, 22)
        //          .Select(i=> new Dictionary<String, int>
        //          {
        //              {"i", i},
        //              {"i*2", i * 2},
        //              {"i/2", i / 2},
        //          })
        //          .DictObserve(o => Observable.Where(o, i=> i < 10));
        //
        // x.Subscribe(ints => Console.WriteLine(String.Join(Environment.NewLine,ints) + "\n")  );


        var src = Observable
            .Interval(TimeSpan.FromSeconds(1))
            .BufferBy(i => i / 5)
            .Subscribe(b =>
            {
                var values = b.Select(e => e.ToString()).Aggregate("", (x, y) => $"{x}\n{y}");
                var average = b.Average();
                var msg = $"{values}\nAverage: {average}";

                Console.WriteLine(msg);
            });



        Console.ReadLine();
        // var t = new Test(12, 2, 1);
        //
        // foreach (var m in typeof(Test).GetMembers().OfType<MethodInfo>())
        // {
        //     var parameters = m
        //                     .GetParameters()
        //                     .Select(p => $"{p.ParameterType.Name} {p.Name}")
        //                     .Aggregate("", (a, b) => a + ", " + b)
        //                     .TrimStart(", ".ToCharArray());
        //
        //     Console.WriteLine($"{m.ReturnType.Name} {m.Name}({parameters})");
        // }


        return;

        var s = new Subject<String>();

        Observable.Interval(TimeSpan.FromSeconds(2), TaskPoolScheduler.Default)
            .WithLatestFrom(s, (_, x) => x)
            .Subscribe(Console.WriteLine);

        while (true)
        {
            var k = Console.ReadKey(true);
            s.OnNext(k.KeyChar.ToString());
        }




        var levels = new []
        {
            new AggregationLevel( 2.Seconds(),  54.Weeks()),
            new AggregationLevel(10.Seconds(),  54.Weeks()),
            new AggregationLevel( 1.Minutes(),  54.Weeks()),
            new AggregationLevel( 5.Minutes(), 540.Weeks()),
            new AggregationLevel(15.Minutes(), 540.Weeks()),
            new AggregationLevel( 1.Hours()  , 540.Weeks()),
            new AggregationLevel( 6.Hours()  , 540.Weeks()),
            new AggregationLevel( 1.Days()   , 540.Weeks()),
            new AggregationLevel( 1.Weeks()  , Forever)
        };

        var resolution = 2.Seconds();

        for (int i = 0; i < 10; i++)
        {
            var startTime = UnixTime.Epoch + Rng.Next(0, 2.Weeks().Ticks.ConvertTo<Int32>()).Seconds();
            var split     = Rng.Next(2, 1000);

            //Test(startTime, resolution, levels, split);
        }


        Console.WriteLine("Done");
    }

    // private static void Test(UnixTime startTime, UnixTimeSpan resolution, AggregationLevel[] aggregationLevels, Int32 split)
    // {
    //     var times = Enumerable
    //                .Range(0, 2.Weeks().Ticks.ConvertTo<Int32>())
    //                .Select(t => startTime + t * resolution)
    //                .ToReadOnlyList();
    //
    //     var records = times
    //                  //.Where(_ => Rng.NextDouble() > .25) // "loose" 1 in 4
    //                  .Select(t => (
    //                      record: new Record
    //                      (
    //                       //   new NumberField("Current", t.Ticks / 2, "A"),
    //                          new SubRecord("Sub1", new Record(new NumberField("Current1", t.Ticks / 4, "A"))),
    //                          new SubRecord("Sub2", new Record(new NumberField("Current2", t.Ticks / 8, "A")))
    //                      ),
    //                      time: t + Rng.NextDouble().Apply(Math.Round).ConvertTo<Int32>().Seconds() // add noise
    //                  ))
    //                  .ToList();
    //
    //
    //     var allMemory = new Dictionary<String, TimeStampedRecord>();
    //     using (var driverAll = new MemoryDriver(aggregationLevels, startTime, allMemory))
    //     {
    //         foreach (var record in records)
    //             driverAll.WriteRecord(record.record, record.time);
    //     }
    //
    //     var list = allMemory.Where(kv => kv.Key.StartsWith("1w")).ToList();
    //
    //     var splitMemory = new Dictionary<String, TimeStampedRecord>();
    //     using (var driver1 = new MemoryDriver(aggregationLevels, startTime, splitMemory))
    //     {
    //         foreach (var record in records.Take(split))
    //             driver1.WriteRecord(record.record, record.time);
    //     }
    //
    //     using (var driver2 = new MemoryDriver(aggregationLevels, records.ElementAt(split-1).time, splitMemory))
    //     {
    //         foreach (var record in records.Skip(split-1))
    //             driver2.WriteRecord(record.record, record.time);
    //     }
    //
    //
    //
    //     var zip = Enumerable.Zip(
    //         allMemory.OrderBy(kv => kv.Key),
    //         splitMemory.OrderBy(kv => kv.Key)
    //     );
    //
    //     foreach (var (l, r) in zip)
    //     {
    //         var nl = l.Value.Record.Fields.OfType<NumberField>().FirstOrDefault()?.Value;
    //         var nr = r.Value.Record.Fields.OfType<NumberField>().FirstOrDefault()?.Value;
    //
    //         if (l.Key != r.Key)
    //         {
    //             Console.WriteLine($"{l.Key} <=> {r.Key}");
    //             break;
    //         }
    //
    //         if (nl != nr)
    //         {
    //             Console.WriteLine($"{l.Value.TimeStamp.ToUtcDateTime()}:   {nl} <=> {nr}");
    //         }
    //     }
    // }


    public static String CreateRandomState()
    {
        var r = Rng.NextDouble() * 100;

        return r switch
        {
            >= 90 => "Heating",
            >= 10 => "SelfConsumption",
            _     => "CalibrationCharge"
        };
    }







}