using System.Collections;
using InnovEnergy.Lib.Units.Power;
using InnovEnergy.Lib.Utils;
using static System.Reflection.BindingFlags;

namespace InnovEnergy.Lib.Units;

public static class Units
{
    public static Byte DisplaySignificantDigits { get; set; } = 3;
    public static Byte JsonSignificantDigits    { get; set; } = 3;
    
    public static Current       A      (this Double value) => value;
    public static Voltage       V      (this Double value) => value;
    public static ActivePower   W      (this Double value) => value;
    public static ReactivePower Var    (this Double value) => value;
    public static ApparentPower Va     (this Double value) => value;
    public static Resistance    Ohm    (this Double value) => value;
    public static Frequency     Hz     (this Double value) => value;
    public static Angle         Rad    (this Double value) => value;
    public static Temperature   Celsius(this Double value) => value;
    public static Energy        KWh    (this Double value) => value * 1000;
    public static Energy        Wh     (this Double value) => value;
    public static Percent       Percent(this Double value) => value;
    
    public static Current       A      (this Int32 value) => value;
    public static Voltage       V      (this Int32 value) => value;
    public static ActivePower   W      (this Int32 value) => value;
    public static ReactivePower Var    (this Int32 value) => value;
    public static ApparentPower Va     (this Int32 value) => value;
    public static Resistance    Ohm    (this Int32 value) => value;
    public static Frequency     Hz     (this Int32 value) => value;
    public static Angle         Rad    (this Int32 value) => value;
    public static Temperature   Celsius(this Int32 value) => value;
    public static Energy        KWh    (this Int32 value) => value * 1000;
    public static Energy        Wh     (this Int32 value) => value;
    public static Percent       Percent(this Int32 value) => value;
    
    public static String ToCsv(this Object thing)
    {
        var csvLines = new List<String>();
        var visited  = new HashSet<Object>();

        GetCsv(thing);

        return csvLines.JoinLines();
        
        void GetCsv(Object? parent, String path = "")
        {
            if (parent == null)
                return;

            var type = parent.GetType();
            
            if (parent is Unit u)
            {
                csvLines.Add($"{path};{u.Value};{u.Symbol}");  // leaf: unit
            }
            else if (parent is String s)
            {
                csvLines.Add($"{path};{s};");  // leaf: String
            }
            else if(type.IsEnum || (type.IsValueType && !type.IsNested))
            {
                csvLines.Add($"{path};{parent};"); // leaf: value type
            }
            else if (parent is IEnumerable enumerable)
            {
                var enumerableType = enumerable.GetType();

                var elementType = enumerableType.GetGenericArguments().SingleOrDefault() ??
                                  enumerableType.GetElementType();  // for arrays
                
                if (elementType is null)
                    return;
                
                if (elementType.IsValueType || elementType == typeof(String))
                {
                    var value = enumerable.Cast<Object>().JoinWith(",");
                    csvLines.Add($"{path};{value};"); 
                }
                else
                {
                    var i = 1;
                    foreach (var o in enumerable)
                        GetCsv(o, $"{path}/{i++}");
                }
            }
            else
            {
                //if (!visited.Add(parent))
                 //   return;

                foreach (var p in type.GetProperties(Instance | Public))
                {
                    var name  = p.Name;
                    var value = p.GetValue(parent);
                    GetCsv(value, $"{path}/{name}");
                }
            }
        }
    }
}