using System;
using System.Text;
using Volpe.Cafe.Generic;
namespace Volpe.Cafe.Data
{
    [Serializable]
    public struct FuelValue
	{
        #region 
        static FuelValue()
        {
            FuelValue.NaN  = new FuelValue(double.NaN);
            FuelValue.Zero = new FuelValue(0);
            FuelValue.One  = new FuelValue(1);
        }
        FuelValue(double value) : this(value, value, value, value, value, value, value, value, value) { }
        FuelValue(double gasoline, double ethanol85, double diesel, double biodiesel20, double electricity, double hydrogen,
            double cng, double lng, double lpg)
            : this()
        {
            this.Gasoline    = gasoline;
            this.Ethanol85   = ethanol85;
            this.Diesel      = diesel;
            this.Biodiesel20 = biodiesel20;
            this.Electricity = electricity;
            this.Hydrogen    = hydrogen;
            this.CNG         = cng;
            this.LNG         = lng;
            this.LPG         = lpg;
        }
        #endregion
        #region 
        #region 
        public static FuelValue operator +(FuelValue value1, FuelValue value2)
        {
            return new FuelValue(value1.Gasoline    + value2.Gasoline,
                                 value1.Ethanol85   + value2.Ethanol85,
                                 value1.Diesel      + value2.Diesel,
                                 value1.Biodiesel20 + value2.Biodiesel20,
                                 value1.Electricity + value2.Electricity,
                                 value1.Hydrogen    + value2.Hydrogen,
                                 value1.CNG         + value2.CNG,
                                 value1.LNG         + value2.LNG,
                                 value1.LPG         + value2.LPG);
        }
        public static FuelValue operator +(FuelValue value1, double value2)
        {
            return value1 + new FuelValue(value2);
        }
        public static FuelValue operator +(double value1, FuelValue value2)
        {
            return new FuelValue(value1) + value2;
        }
        public static FuelValue operator +(FuelValue value1, int value2)
        {
            return value1 + new FuelValue(value2);
        }
        public static FuelValue operator +(int value1, FuelValue value2)
        {
            return new FuelValue(value1) + value2;
        }
        public static FuelValue operator -(FuelValue value1, FuelValue value2)
        {
            return new FuelValue(value1.Gasoline    - value2.Gasoline,
                                 value1.Ethanol85   - value2.Ethanol85,
                                 value1.Diesel      - value2.Diesel,
                                 value1.Biodiesel20 - value2.Biodiesel20,
                                 value1.Electricity - value2.Electricity,
                                 value1.Hydrogen    - value2.Hydrogen,
                                 value1.CNG         - value2.CNG,
                                 value1.LNG         - value2.LNG,
                                 value1.LPG         - value2.LPG);
        }
        public static FuelValue operator -(FuelValue value1, double value2)
        {
            return value1 - new FuelValue(value2);
        }
        public static FuelValue operator -(double value1, FuelValue value2)
        {
            return new FuelValue(value1) - value2;
        }
        public static FuelValue operator -(FuelValue value1, int value2)
        {
            return value1 - new FuelValue(value2);
        }
        public static FuelValue operator -(int value1, FuelValue value2)
        {
            return new FuelValue(value1) - value2;
        }
        public static FuelValue operator *(FuelValue value1, FuelValue value2)
        {
            return new FuelValue(value1.Gasoline    * value2.Gasoline,
                                 value1.Ethanol85   * value2.Ethanol85,
                                 value1.Diesel      * value2.Diesel,
                                 value1.Biodiesel20 * value2.Biodiesel20,
                                 value1.Electricity * value2.Electricity,
                                 value1.Hydrogen    * value2.Hydrogen,
                                 value1.CNG         * value2.CNG,
                                 value1.LNG         * value2.LNG,
                                 value1.LPG         * value2.LPG);
        }
        public static FuelValue operator *(FuelValue value1, double value2)
        {
            return value1 * new FuelValue(value2);
        }
        public static FuelValue operator *(double value1, FuelValue value2)
        {
            return new FuelValue(value1) * value2;
        }
        public static FuelValue operator *(FuelValue value1, int value2)
        {
            return value1 * new FuelValue(value2);
        }
        public static FuelValue operator *(int value1, FuelValue value2)
        {
            return new FuelValue(value1) * value2;
        }
        public static FuelValue operator /(FuelValue value1, FuelValue value2)
        {
            return new FuelValue(value1.Gasoline    / value2.Gasoline,
                                 value1.Ethanol85   / value2.Ethanol85,
                                 value1.Diesel      / value2.Diesel,
                                 value1.Biodiesel20 / value2.Biodiesel20,
                                 value1.Electricity / value2.Electricity,
                                 value1.Hydrogen    / value2.Hydrogen,
                                 value1.CNG         / value2.CNG,
                                 value1.LNG         / value2.LNG,
                                 value1.LPG         / value2.LPG);
        }
        public static FuelValue operator /(FuelValue value1, double value2)
        {
            return value1 / new FuelValue(value2);
        }
        public static FuelValue operator /(double value1, FuelValue value2)
        {
            return new FuelValue(value1) / value2;
        }
        public static FuelValue operator /(FuelValue value1, int value2)
        {
            return value1 / new FuelValue(value2);
        }
        public static FuelValue operator /(int value1, FuelValue value2)
        {
            return new FuelValue(value1) / value2;
        }
        public static FuelValue operator +(FuelValue value)
        {
            return FuelValue.Zero + value;
        }
        public static FuelValue operator -(FuelValue value)
        {
            return FuelValue.Zero - value;
        }
        public static FuelValue operator ++(FuelValue value)
        {
            return value + FuelValue.One;
        }
        public static FuelValue operator --(FuelValue value)
        {
            return value - FuelValue.One;
        }
        public static bool operator ==(FuelValue value1, FuelValue value2)
        {
            return value1.Equals(value2);
        }
        public static bool operator !=(FuelValue value1, FuelValue value2)
        {
            return !value1.Equals(value2);
        }
        #endregion
        #region 
        public override string ToString()
        {
            return "{Gasoline="    + this.Gasoline   .ToString() +
                  ", Ethanol85="   + this.Ethanol85  .ToString() +
                  ", Diesel="      + this.Diesel     .ToString() +
                  ", Biodiesel20=" + this.Biodiesel20.ToString() +
                  ", Electricity=" + this.Electricity.ToString() +
                  ", Hydrogen="    + this.Hydrogen   .ToString() +
                  ", CNG="         + this.CNG        .ToString() +
                  ", LNG="         + this.LNG        .ToString() +
                  ", LPG="         + this.LPG        .ToString() + "}";
        }
        public string ToString(string format)
        {
            return "{Gasoline="    + this.Gasoline   .ToString(format) +
                  ", Ethanol85="   + this.Ethanol85  .ToString(format) +
                  ", Diesel="      + this.Diesel     .ToString(format) +
                  ", Biodiesel20=" + this.Biodiesel20.ToString(format) +
                  ", Electricity=" + this.Electricity.ToString(format) +
                  ", Hydrogen="    + this.Hydrogen   .ToString(format) +
                  ", CNG="         + this.CNG        .ToString(format) +
                  ", LNG="         + this.LNG        .ToString(format) +
                  ", LPG="         + this.LPG        .ToString(format) + "}";
        }
        public string ToString(int digits)
        {
            return "{Gasoline="    + Math.Round(this.Gasoline   , digits).ToString() +
                  ", Ethanol85="   + Math.Round(this.Ethanol85  , digits).ToString() +
                  ", Diesel="      + Math.Round(this.Diesel     , digits).ToString() +
                  ", Biodiesel20=" + Math.Round(this.Biodiesel20, digits).ToString() +
                  ", Electricity=" + Math.Round(this.Electricity, digits).ToString() +
                  ", Hydrogen="    + Math.Round(this.Hydrogen   , digits).ToString() +
                  ", CNG="         + Math.Round(this.CNG        , digits).ToString() +
                  ", LNG="         + Math.Round(this.LNG        , digits).ToString() +
                  ", LPG="         + Math.Round(this.LPG        , digits).ToString() + "}";
        }
        public override int GetHashCode()
        {
            return (this.Gasoline   .GetHashCode() ^
                    this.Ethanol85  .GetHashCode() ^
                    this.Diesel     .GetHashCode() ^
                    this.Biodiesel20.GetHashCode() ^
                    this.Electricity.GetHashCode() ^
                    this.Hydrogen   .GetHashCode() ^
                    this.CNG        .GetHashCode() ^
                    this.LNG        .GetHashCode() ^
                    this.LPG        .GetHashCode());
        }
        public override bool Equals(object obj)
        {
            return (obj is FuelValue) ? this.Equals((FuelValue)obj) : base.Equals(obj);
        }
        public bool Equals(FuelValue value)
        {
            return (this.Gasoline    == value.Gasoline    &&
                    this.Ethanol85   == value.Ethanol85   &&
                    this.Diesel      == value.Diesel      &&
                    this.Biodiesel20 == value.Biodiesel20 &&
                    this.Electricity == value.Electricity &&
                    this.Hydrogen    == value.Hydrogen    &&
                    this.CNG         == value.CNG         &&
                    this.LNG         == value.LNG         &&
                    this.LPG         == value.LPG);
        }
        #endregion
        public static FuelValue Sum(params FuelValue[] values)
        {
            FuelValue sum = new FuelValue();
            for (int i = 0; i < values.Length; i++)
            {
                sum += values[i];
            }
            return sum;
        }
        public void Clear()
        {
            this.Gasoline    =
            this.Ethanol85   =
            this.Diesel      =
            this.Biodiesel20 =
            this.Electricity =
            this.Hydrogen    =
            this.CNG         =
            this.LNG         =
            this.LPG         = 0;
        }
        public double ArithmeticMean()
        {
            return this.ArithmeticMean(FuelValue.One);
        }
        public double ArithmeticMean(FuelValue weight)
        {
            return (this * weight).Total / weight.Total;
        }
        public double GeometricMean()
        {
            return this.GeometricMean(FuelValue.One);
        }
        public double GeometricMean(FuelValue weight)
        {
            double d = 0;
            foreach (FuelType fuel in FTValue<object>.Classes)
            {
                if (this[fuel] != 0 && !double.IsNaN(this[fuel]) && !double.IsInfinity(this[fuel]))
                {
                    d += weight[fuel] * Math.Log(this[fuel]);
                }
            }
            return Math.Exp(d / weight.Total);
        }
        public double HarmonicMean()
        {
            return this.HarmonicMean(FuelValue.One);
        }
        public double HarmonicMean(FuelValue weight)
        {
            double d = 0;
            foreach (FuelType fuel in FTValue<object>.Classes)
            {
                if (this[fuel] != 0 && !double.IsNaN(this[fuel]) && !double.IsInfinity(this[fuel]))
                {
                    d += weight[fuel] / this[fuel];
                }
            }
            return (d == 0) ? 0 : weight.Total / d;
        }
        #endregion
        #region 
        public double this[FuelType fuelType]
        {
            get
            {
                if      (fuelType == FuelType.Gasoline   ) { return this.Gasoline   ; }
                else if (fuelType == FuelType.Ethanol85  ) { return this.Ethanol85  ; }
                else if (fuelType == FuelType.Diesel     ) { return this.Diesel     ; }
                else if (fuelType == FuelType.Biodiesel20) { return this.Biodiesel20; }
                else if (fuelType == FuelType.Electricity) { return this.Electricity; }
                else if (fuelType == FuelType.Hydrogen   ) { return this.Hydrogen   ; }
                else if (fuelType == FuelType.CNG        ) { return this.CNG        ; }
                else if (fuelType == FuelType.LNG        ) { return this.LNG        ; }
                else if (fuelType == FuelType.LPG        ) { return this.LPG        ; }
                else if (fuelType == FuelType.All        ) { return this.Total      ; }
                else
                {   
                    double value = 0;
                    if ((fuelType & FuelType.Gasoline   ) == FuelType.Gasoline   ) { value += this.Gasoline;    }
                    if ((fuelType & FuelType.Ethanol85  ) == FuelType.Ethanol85  ) { value += this.Ethanol85;   }
                    if ((fuelType & FuelType.Diesel     ) == FuelType.Diesel     ) { value += this.Diesel;      }
                    if ((fuelType & FuelType.Biodiesel20) == FuelType.Biodiesel20) { value += this.Biodiesel20; }
                    if ((fuelType & FuelType.Electricity) == FuelType.Electricity) { value += this.Electricity; }
                    if ((fuelType & FuelType.Hydrogen   ) == FuelType.Hydrogen   ) { value += this.Hydrogen;    }
                    if ((fuelType & FuelType.CNG        ) == FuelType.CNG        ) { value += this.CNG;         }
                    if ((fuelType & FuelType.LNG        ) == FuelType.LNG        ) { value += this.LNG;         }
                    if ((fuelType & FuelType.LPG        ) == FuelType.LPG        ) { value += this.LPG;         }
                    return value;
                }
            }
            set
            {
                if      (fuelType == FuelType.Gasoline   ) { this.Gasoline    = value; }
                else if (fuelType == FuelType.Ethanol85  ) { this.Ethanol85   = value; }
                else if (fuelType == FuelType.Diesel     ) { this.Diesel      = value; }
                else if (fuelType == FuelType.Biodiesel20) { this.Biodiesel20 = value; }
                else if (fuelType == FuelType.Electricity) { this.Electricity = value; }
                else if (fuelType == FuelType.Hydrogen   ) { this.Hydrogen    = value; }
                else if (fuelType == FuelType.CNG        ) { this.CNG         = value; }
                else if (fuelType == FuelType.LNG        ) { this.LNG         = value; }
                else if (fuelType == FuelType.LPG        ) { this.LPG         = value; }
            }
        }
        public double Gasoline { get; private set; }
        public double Ethanol85 { get; private set; }
        public double Diesel { get; private set; }
        public double Biodiesel20 { get; private set; }
        public double Electricity { get; private set; }
        public double Hydrogen { get; private set; }
        public double CNG { get; private set; }
        public double LNG { get; private set; }
        public double LPG { get; private set; }
        public double Total
        {
            get
            {
                if (this.IsNaN             ) { return double.NaN;              }
                if (this.IsPositiveInfinity) { return double.PositiveInfinity; }
                if (this.IsNegativeInfinity) { return double.NegativeInfinity; }
                double total = 0;
                if (!double.IsNaN(this.Gasoline   ) && !double.IsInfinity(this.Gasoline   )) { total += this.Gasoline   ; }
                if (!double.IsNaN(this.Ethanol85  ) && !double.IsInfinity(this.Ethanol85  )) { total += this.Ethanol85  ; }
                if (!double.IsNaN(this.Diesel     ) && !double.IsInfinity(this.Diesel     )) { total += this.Diesel     ; }
                if (!double.IsNaN(this.Biodiesel20) && !double.IsInfinity(this.Biodiesel20)) { total += this.Biodiesel20; }
                if (!double.IsNaN(this.Electricity) && !double.IsInfinity(this.Electricity)) { total += this.Electricity; }
                if (!double.IsNaN(this.Hydrogen   ) && !double.IsInfinity(this.Hydrogen   )) { total += this.Hydrogen   ; }
                if (!double.IsNaN(this.CNG        ) && !double.IsInfinity(this.CNG        )) { total += this.CNG        ; }
                if (!double.IsNaN(this.LNG        ) && !double.IsInfinity(this.LNG        )) { total += this.LNG        ; }
                if (!double.IsNaN(this.LPG        ) && !double.IsInfinity(this.LPG        )) { total += this.LPG        ; }
                return total;
            }
        }
        public bool IsValid
        {
            get
            {
                return (this.Gasoline    >= 0 &&
                        this.Ethanol85   >= 0 &&
                        this.Diesel      >= 0 &&
                        this.Biodiesel20 >= 0 &&
                        this.Electricity >= 0 &&
                        this.Hydrogen    >= 0 &&
                        this.CNG         >= 0 &&
                        this.LNG         >= 0 &&
                        this.LPG         >= 0);
            }
        }
        public bool IsEmpty { get { return this.Equals(FuelValue.Zero); } }
        public bool IsNaN
        {
            get
            {
                return (double.IsNaN(this.Gasoline   ) &&
                        double.IsNaN(this.Ethanol85  ) &&
                        double.IsNaN(this.Diesel     ) &&
                        double.IsNaN(this.Biodiesel20) &&
                        double.IsNaN(this.Electricity) &&
                        double.IsNaN(this.Hydrogen   ) &&
                        double.IsNaN(this.CNG        ) &&
                        double.IsNaN(this.LNG        ) &&
                        double.IsNaN(this.LPG        ));
            }
        }
        public bool IsPositiveInfinity
        {
            get
            {
                return (double.IsPositiveInfinity(this.Gasoline   ) &&
                        double.IsPositiveInfinity(this.Ethanol85  ) &&
                        double.IsPositiveInfinity(this.Diesel     ) &&
                        double.IsPositiveInfinity(this.Biodiesel20) &&
                        double.IsPositiveInfinity(this.Electricity) &&
                        double.IsPositiveInfinity(this.Hydrogen   ) &&
                        double.IsPositiveInfinity(this.CNG        ) &&
                        double.IsPositiveInfinity(this.LNG        ) &&
                        double.IsPositiveInfinity(this.LPG        ));
            }
        }
        public bool IsNegativeInfinity
        {
            get
            {
                return (double.IsNegativeInfinity(this.Gasoline   ) &&
                        double.IsNegativeInfinity(this.Ethanol85  ) &&
                        double.IsNegativeInfinity(this.Diesel     ) &&
                        double.IsNegativeInfinity(this.Biodiesel20) &&
                        double.IsNegativeInfinity(this.Electricity) &&
                        double.IsNegativeInfinity(this.Hydrogen   ) &&
                        double.IsNegativeInfinity(this.CNG        ) &&
                        double.IsNegativeInfinity(this.LNG        ) &&
                        double.IsNegativeInfinity(this.LPG        ));
            }
        }
        public bool IsMultiFuel
        {
            get
            {
                int f = (int)this.FuelType;
                return ((f & (f - 1)) != 0);
            }
        }
        public FuelType FuelType
        {
            get
            {
                FuelType fuelType = FuelType.None;
                if (this.Gasoline    > 0) { fuelType |= FuelType.Gasoline   ; }
                if (this.Ethanol85   > 0) { fuelType |= FuelType.Ethanol85  ; }
                if (this.Diesel      > 0) { fuelType |= FuelType.Diesel     ; }
                if (this.Biodiesel20 > 0) { fuelType |= FuelType.Biodiesel20; }
                if (this.Electricity > 0) { fuelType |= FuelType.Electricity; }
                if (this.Hydrogen    > 0) { fuelType |= FuelType.Hydrogen   ; }
                if (this.CNG         > 0) { fuelType |= FuelType.CNG        ; }
                if (this.LNG         > 0) { fuelType |= FuelType.LNG        ; }
                if (this.LPG         > 0) { fuelType |= FuelType.LPG        ; }
                return fuelType;
            }
        }
        public double PrimaryValue { get { return this[this.PrimaryFuel]; } }
        public FuelType PrimaryFuel
        {
            get
            {
                return 
                       (this.Gasoline    > 0) ? FuelType.Gasoline    :
                       (this.Diesel      > 0) ? FuelType.Diesel      :
                       (this.Electricity > 0) ? FuelType.Electricity :
                       (this.Hydrogen    > 0) ? FuelType.Hydrogen    :
                       (this.CNG         > 0) ? FuelType.CNG         :
                       (this.LNG         > 0) ? FuelType.LNG         :
                       (this.LPG         > 0) ? FuelType.LPG         :
                       (this.Ethanol85   > 0) ? FuelType.Ethanol85   :
                       (this.Biodiesel20 > 0) ? FuelType.Biodiesel20 : FuelType.None;
            }
        }
        #endregion
        #region 
        public static readonly FuelValue NaN;
        public static readonly FuelValue Zero;
        public static readonly FuelValue One;
        #endregion
	}
}

