using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Utils;
using InnovEnergy.WireFormat.VictronV1;

namespace InnovEnergy.VenusLogger;

internal static class Devices
{

    public static Maybe<Device> Combine(this DeviceType deviceType, params Maybe<Device>[] devices)
    {
        return devices
              .Flatten()
              .Combine(deviceType);
    }

    public static Maybe<Device> Combine(this IEnumerable<Device> devices, DeviceType deviceType)
    {
        var devs = devices as IReadOnlyCollection<Device> ?? devices.ToList();

        return devs.Combine(deviceType);
    }

    public static Maybe<Device> Combine(this IReadOnlyCollection<Device> devices, DeviceType deviceType)
    {
        return devices
              .EquivalentDevice(deviceType)
              .Do(d => d.Devices.Add(devices)); // Maybe;
    }

    public static Maybe<Device> EquivalentDevice(this DeviceType deviceType, params Maybe<Device>[] devices)
    {
        return devices
              .Flatten()
              .EquivalentDevice(deviceType);
    }

    public static Maybe<Device> EquivalentDevice(this IEnumerable<Device> devices, DeviceType deviceType)
    {
        var devs = devices as IReadOnlyCollection<Device> ?? devices.ToList();

        return devs.EquivalentDevice(deviceType);
    }

    public static Maybe<Device> EquivalentDevice(this IReadOnlyCollection<Device> devices, DeviceType deviceType)
    {
        if (devices.Count <= 0)
            return null;

        if (devices.Count == 1)
            return new Device { Type = deviceType, Phases = { devices.First().Phases } };

        var voltages = devices.GetPhaseVoltages();

        var phases = voltages.Select((v, phase) => new Phase
        {
            Voltage = v,
            Current = devices.Sum(d => d.Phases[phase].Current),
            Power   = devices.Sum(d => d.Phases[phase].Power),
        });

        return new Device
        {
            Type = deviceType,
            Phases = { phases }
        };
    }

    public static Device ReversePhases(this Device device)
    {
        device.Phases.ForEach(p =>
        {
            p.Current = -p.Current;
            p.Power = -p.Power;
        });

        return device;
    }

    [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
    private static IEnumerable<Single> GetPhaseVoltages(this IReadOnlyCollection<Device> devices)
    {
        // TODO: warning if voltages unequal?

        var nPhases = devices.Max(d => d.Phases.Count);

        var phases = Enumerable.Range(0, nPhases);

        var voltages = phases
                      .Select(AverageVoltage)
                      .ToList();

        if (nPhases != devices.Min(d => d.Phases.Count))
            EqualizeNumberOfPhases();

        return voltages;

        Single AverageVoltage(Int32 phase)
        {
            return devices
                  .Where(d => d.Phases.Count > phase)
                  .Average(d => d.Phases[phase].Voltage);
        }

        void EqualizeNumberOfPhases()
        {
            foreach (var phase in phases)
            foreach (var device in devices.Where(d => d.Phases.Count <= phase))
            {
                device.Phases.Add(new Phase
                {
                    Current = 0,
                    Voltage = voltages[phase]
                });
            }
        }
    }

}