using System;
using Volpe.Cafe.Settings;
using Volpe.Cafe.Generic;
namespace Volpe.Cafe.Data
{
    [Serializable]
    public sealed class KMYType
    {
        #region 
        KMYType() { }
        KMYType(ModelYear year, Parameters parameters, Estimates priceEstimates, Manufacturer mfr, bool useOCPayback)
        {
            this.ModelYear = year;
            this.CalculateKMY(year, parameters, priceEstimates, mfr, useOCPayback, VehicleClass.LDV   , VehicleStyle.None);
            this.CalculateKMY(year, parameters, priceEstimates, mfr, useOCPayback, VehicleClass.LDT12a, VehicleStyle.Van);
            this.CalculateKMY(year, parameters, priceEstimates, mfr, useOCPayback, VehicleClass.LDT12a, VehicleStyle.SportUtility);
            this.CalculateKMY(year, parameters, priceEstimates, mfr, useOCPayback, VehicleClass.LDT12a, VehicleStyle.Pickup);
            this.CalculateKMY(year, parameters, priceEstimates, mfr, useOCPayback, VehicleClass.LDT12a, VehicleStyle.None);
            this.CalculateKMY(year, parameters, priceEstimates, mfr, useOCPayback, VehicleClass.LDT2b3, VehicleStyle.None);
            this.CalculateKMY(year, parameters, priceEstimates, mfr, useOCPayback, VehicleClass.MDT456, VehicleStyle.None);
        }
        #endregion
        #region 
        internal KMYType Clone()
        {
            KMYType kmy = new KMYType();
            kmy.ModelYear = new ModelYear(this.ModelYear.Year);
            kmy._cars     = this._cars;
            kmy._vans     = this._vans;
            kmy._suvs     = this._suvs;
            kmy._pickups  = this._pickups;
            kmy._class12a = this._class12a;
            kmy._class2b3 = this._class2b3;
            kmy._class456 = this._class456;
            return kmy;
        }
        public override string ToString()
        {
            return
                "{ModelYear="   + this.ModelYear.ToString() +
                ", Cars="       + this._cars    .ToString() +
                ", Vans="       + this._vans    .ToString() +
                ", SUVs="       + this._suvs    .ToString() +
                ", Pickups="    + this._pickups .ToString() +
                ", Class1/2a="  + this._class12a.ToString() +
                ", Class2b/3="  + this._class2b3.ToString() +
                ", Class4/5/6=" + this._class456.ToString() + "}";
        }
        void CalculateKMY(ModelYear year, Parameters parameters, Estimates priceEstimates, Manufacturer mfr, bool useOCPayback,
            VehicleClass vehClass, VehicleStyle vehStyle)
        {
            VehicleAgeData milesDriven   = parameters.MilesDriven;
            VehicleAgeData survivalRates = parameters.SurvivalRates;
            FuelPrices     fuelPrices    = parameters.FuelPrices;
            EconomicValues econData      = parameters.EconomicValues;
            double discRate      = mfr.Description.DiscountRate;
            double paybackPeriod = (useOCPayback) ? mfr.Description.PaybackPeriod_OC : mfr.Description.PaybackPeriod;
            int    payback       = (int)Math.Floor(paybackPeriod);
            double fract         = paybackPeriod - payback;         
            int    yrYear        = year.Year;
            FuelValue energyDensity = parameters.FuelProperties.EnergyDensity;
            double elcRatio = energyDensity.Gasoline / energyDensity.Electricity;
            double hgnRatio = energyDensity.Gasoline / energyDensity.Hydrogen;
            double cngRatio = energyDensity.Gasoline / energyDensity.CNG;
            FuelValue fuelCost = new FuelValue();
            for (int j = 0; j <= payback; j++)
            {
                if (j == payback && fract == 0) { continue; }   
                double surv = survivalRates.GetVehicleAgeData(vehClass, vehStyle)[j];
                double vmtGrowth = econData.VMTGrowthRate[vehClass].GetVMTGrowthFactor(priceEstimates, yrYear, j);
                double miles = milesDriven.GetVehicleAgeData(vehClass, vehStyle)[j] * vmtGrowth;
                int yrIndex = Math.Min(year.Year , fuelPrices.MaxCY) - fuelPrices.MinCY;
                double multiplier = surv * miles / Math.Pow(1 + discRate, j);
                foreach (FuelType fuel in FTValue<object>.Classes)
                {
                    double fuelPrice = fuelPrices.GetFuelPrice(priceEstimates, fuel, year.Year);
                    if      (fuel == FuelType.Electricity) { fuelCost[fuel] = fuelPrice * multiplier * elcRatio; }
                    else if (fuel == FuelType.Hydrogen   ) { fuelCost[fuel] = fuelPrice * multiplier * hgnRatio; }
                    else if (fuel == FuelType.CNG        ) { fuelCost[fuel] = fuelPrice * multiplier * cngRatio; }
                    else                                   { fuelCost[fuel] = fuelPrice * multiplier; }
                }
                this[vehClass, vehStyle] += ((j == payback) ? fuelCost * fract : fuelCost);
            }
        }
        internal static KMYType[] GetKmyValues(Parameters parameters, Estimates priceEstimates, Manufacturer mfr, bool useOCPayback)
        {
            int numYears = mfr.Description.WillingToPayFines.Length;
            KMYType[] kmy = new KMYType[numYears];
            for (int i = 0; i < numYears; i++)
            {
                kmy[i] = new KMYType(ModelYear.NewModelYearFromIndex(i), parameters, priceEstimates, mfr, useOCPayback);
            }
            return kmy;
        }
        #endregion
        #region 
        public FuelValue this[VehicleClass vehClass, VehicleStyle vehStyle]
        {
            get
            {
                switch (vehClass)
                {
                    case VehicleClass.LDV   : return this._cars;
                    case VehicleClass.LDT1  :
                    case VehicleClass.LDT2a :
                    case VehicleClass.LDT12a: return (vehStyle == VehicleStyle.Minivan     ) ? this._vans    :
                                                     (vehStyle == VehicleStyle.Van         ) ? this._vans    :
                                                     (vehStyle == VehicleStyle.SportUtility) ? this._suvs    :
                                                     (vehStyle == VehicleStyle.Pickup      ) ? this._pickups : this._class12a;
                    case VehicleClass.LDT2b :
                    case VehicleClass.LDT3  :
                    case VehicleClass.LDT2b3: return this._class2b3;
                    case VehicleClass.MDT4  :
                    case VehicleClass.MDT5  :
                    case VehicleClass.MDT6  :
                    case VehicleClass.MDT456: return this._class456;
                    default: throw new ArgumentOutOfRangeException("vehClass", "Vehicle class is not specified or not supported.");
                }
            }
            private set
            {
                switch (vehClass)
                {
                    case VehicleClass.LDV:
                        this._cars = value;
                        break;
                    case VehicleClass.LDT1:
                    case VehicleClass.LDT2a:
                    case VehicleClass.LDT12a:
                        switch (vehStyle)
                        {
                            case VehicleStyle.Van         :
                            case VehicleStyle.Minivan     : this._vans     = value; break;
                            case VehicleStyle.SportUtility: this._suvs     = value; break;
                            case VehicleStyle.Pickup      : this._pickups  = value; break;
                            default                       : this._class12a = value; break;
                        }
                        break;
                    case VehicleClass.LDT2b :
                    case VehicleClass.LDT3  :
                    case VehicleClass.LDT2b3:
                        this._class2b3 = value;
                        break;
                    case VehicleClass.MDT4  :
                    case VehicleClass.MDT5  :
                    case VehicleClass.MDT6  :
                    case VehicleClass.MDT456:
                        this._class456 = value;
                        break;
                    default: throw new ArgumentOutOfRangeException("vehClass", "Vehicle class is not specified or not supported.");
                }
            }
        }
        public ModelYear ModelYear { get; private set; }
        #endregion
        #region 
        FuelValue _cars;
        FuelValue _vans;
        FuelValue _suvs;
        FuelValue _pickups;
        FuelValue _class12a;
        FuelValue _class2b3;
        FuelValue _class456;
        #endregion
    }
}

