using System;
using System.Collections.Generic;
using System.Linq;
using Volpe.Cafe.DfsSrModels;
using Volpe.Cafe.Generic;
using Volpe.Cafe.Initializers;

namespace Volpe.Cafe.ModelSettings
{
    /// <summary>
    /// Provides an object for storing a collection of parameters by calendar or model year. Each of the entries in the
    /// list is stored as a key/value pair, where the key is defined by the calender or model year, while the value is
    /// the parameter, of type T, associated that year.
    /// </summary>
    /// <typeparam name="T">The type of values in the list.</typeparam>
    [Serializable]
    public class ValueByYear<T>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ValueByYear{T}"/> class.
        /// </summary>
        /// <param name="years">The range of years for which annual values are defined.</param>
        /// <param name="values">The range of values corresponding to the supplied <paramref name="years"/>.</param>
        /// <param name="interpolationMethod">The interpolation method to use for filling in missing years.</param>
        public ValueByYear(int[] years, T[] values, Func<int, int[], T[], T> interpolationMethod = null)
        {
            this.MinYear = years.Min();
            this.MaxYear = years.Max();
            this.AllValues = new T[this.MaxYear - this.MinYear + 1];

            if (interpolationMethod == null) {
                interpolationMethod = GetValueNearestNeighbor;
            }

            for (int year = this.MinYear; year <= this.MaxYear; year++) {
                this.AllValues[year - this.MinYear] = interpolationMethod(year, years, values);
            }
        }

        /// <summary>
        /// Gets a value associated with the specified year.
        /// </summary>
        /// <remarks>
        /// If the specified <paramref name="year"/> is out-of-bounds, the first value is returned if year is less than
        /// <see cref="MinYear"/>, or the last value is returned if year is greater than <see cref="MaxYear"/>.
        /// </remarks>
        public T this[int year]
        {
            get {
                int index = Math.Max(this.MinYear, Math.Min(this.MaxYear, year)) - this.MinYear;
                return this.AllValues[index];
            }
        }

        /// <summary>Gets the lowest year for which annual values are defined.</summary>
        public int MinYear { get; private set; }
        /// <summary>Gets the highest year for which annual values are defined.</summary>
        public int MaxYear { get; private set; }

        // all values between min-year and max-year (without any gaps)
        // an interpolation method was used in the ctor (if required) to fill any missing years
        private T[] AllValues { get; set; }

        private static T GetValueNearestNeighbor(int year, int[] years, T[] values)
        {
            for (int i = 0; i < years.Length; i++)
            {
                if (years[i] == year) { return values[i]; } // found exact match
                if (years[i] > year)
                {   // found approximate location
                    //  i=0: year is < min-year; return min value
                    //  i>0: return value of year closest to desired year (nearest neighbor)
                    if (i == 0) { return values[i]; }
                    //
                    int y0 = years[i - 1];
                    int y1 = years[i];
                    int index = (y1 - year < year - y0) ? i : i - 1;
                    return values[index];
                }
            }

            // if exhausted all available years, that means desired year
            // is greater than max-year -- return last available value
            return values.Last();
        }

    }


    /// <summary>
    /// Provides economic parameters for benefits calculations.
    /// </summary>
    [Serializable]
    public sealed class EconomicParameters
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EconomicParameters"/> class.
        /// </summary>
        public EconomicParameters(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>
        /// Gets the percent change in average annual VMT per vehicle resulting from a percent change in fuel cost per
        /// mile driven.
        /// </summary>
        public double ReboundEffect { get; private set; }

        /// <summary>Gets the base year for average annual VMT usage data.</summary>
        public int VMTBaseYear { get; private set; }

        /// <summary>
        /// Gets the difference between a vehicle's EPA fuel economy rating and its actual on-road fuel economy,
        /// expressed as percent.
        /// </summary>
        public FuelValue OnRoadGap { get; private set; }

        /// <summary>
        /// Gets the fixed component of average refueling time in minutes, which includes the time required for vehicle
        /// owners to detour to a fueling station, pay for fuel, and return to route.
        /// </summary>
        public FuelValue RefuelTime { get; private set; }
        /// <summary>Gets the average tank volume refilled during a refueling stop.</summary>
        public double RefuelTankVolume { get; private set; }
        /// <summary>Gets the share of total refueling events to consider for benefits calculation.</summary>
        public double RefuelEventsScale { get; private set; }
        /// <summary>
        /// Gets the amount that the driver of a vehicle would be willing to pay to reduce the time required to make a
        /// trip.
        /// </summary>
        public double VehicleTravelValue { get; private set; }

        /// <summary>
        /// Gets the recharging threshold parameters applicable to a 200-mile battery-electric vehicle.
        /// </summary>
        public EVRechargeThresholds BEV200Thresholds { get; private set; }
        /// <summary>
        /// Gets the recharging threshold parameters applicable to a 300-mile battery-electric vehicle.
        /// </summary>
        public EVRechargeThresholds BEV300Thresholds { get; private set; }
        /// <summary>
        /// Gets the recharging threshold parameters applicable to a 400-mile battery-electric vehicle.
        /// </summary>
        public EVRechargeThresholds BEV400Thresholds { get; private set; }
        /// <summary>
        /// Gets the recharging threshold parameters applicable to a 500-mile battery-electric vehicle.
        /// </summary>
        public EVRechargeThresholds BEV500Thresholds { get; private set; }


        /// <summary>
        /// Gets the average value (in dollars) a driver places on a single refueling operation.
        /// </summary>
        /// <param name="fuelType">The fuel type to refuel.</param>
        /// <param name="vehicleTankSize">The tank size of the vehicle.</param>
        /// <returns>The value of a single refueling operation.</returns>
        public double GetRefuelingValue(FuelType fuelType, double vehicleTankSize)
        {
            return this.GetRefuelingTime(fuelType, vehicleTankSize) * this.VehicleTravelValue;
        }

        /// <summary>
        /// Gets the average amount of time (in hours) required to find a fueling station and refuel a vehicle of the
        /// specified fuel type and with the specified tank size.
        /// </summary>
        /// <param name="fuelType">The fuel type to refuel.</param>
        /// <param name="vehicleTankSize">The tank size of the vehicle.</param>
        /// <returns>The amount of time (in hours) required to find a fueling station and refuel a vehicle.</returns>
        public double GetRefuelingTime(FuelType fuelType, double vehicleTankSize)
        {
            return (this.RefuelTime[fuelType] + vehicleTankSize * this.RefuelTankVolume / 7.5) / 60;
        }

    }

    /// <summary>
    /// Provides recharging threshold parameters applicable to a battery-electric vehicle.
    /// </summary>
    [Serializable]
    public sealed class EVRechargeThresholds
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EVRechargeThresholds"/> class.
        /// </summary>
        public EVRechargeThresholds(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>
        /// Gets the assumed charge frequency of an electric vehicle, that is the cumulative number of miles driven
        /// before a mid-trip charging event is triggered.
        /// </summary>
        public double ChargeFrequency { get; private set; }
        /// <summary>Gets the percent share of miles that will be recharged mid-trip.</summary>
        public double ShareCharged { get; private set; }
        /// <summary>Gets the typical recharge rate for an electric vehicle, specified in miles/hour.</summary>
        public double ChargeRate { get; private set; }

    }

    /// <summary>
    /// Provides external costs from additional vehicle use due to "rebound" effect.
    /// </summary>
    [Serializable]
    public sealed class ExternalCosts
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ExternalCosts"/> class.
        /// </summary>
        public ExternalCosts(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>Gets the congestion component of external costs from additional vehicle use due to "rebound" effect, specified in $/vehicle-mile.</summary>
        public double Congestion { get; private set; }
        /// <summary>Gets the noise component of external costs from additional vehicle use due to "rebound" effect, specified in $/vehicle-mile.</summary>
        public double Noise { get; private set; }

    }

    /// <summary>
    /// Provides ownership and operating costs associated with purchase of new vehicles.
    /// </summary>
    [Serializable]
    public sealed class OperatingCosts
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="OperatingCosts"/> class.
        /// </summary>
        public OperatingCosts(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>
        /// Gets the average percentage of the vehicle's final MSRP the consumer pays in taxes and fees when purchasing
        /// a new vehicle.
        /// </summary>
        public double TaxesAndFees { get; private set; }
        /// <summary>
        /// Gets the average length of time (in months) used by consumers to finance a new vehicle purchase.
        /// </summary>
        public double FinancingTerm { get; private set; }
        /// <summary>Gets the average interest rate used by consumers to finance a new vehicle purchase.</summary>
        public double FinancingInterest { get; private set; }
        /// <summary>Gets the percentage of consumers that choose to finance their new vehicle purchase.</summary>
        public double ShareFinanced { get; private set; }
        /// <summary>Gets the typical depreciation rate of a new vehicle.</summary>
        public double VehicleDepreciation { get; private set; }
        /// <summary>Gets the average number of months during which the vehicle is expected to be resold.</summary>
        public int AverageResaleTime { get; private set; }

    }

    /// <summary>
    /// Provides economic costs of oil imports.
    /// </summary>
    [Serializable]
    public sealed class EconomicCosts
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EconomicCosts"/> class.
        /// </summary>
        public EconomicCosts(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>Gets the lowest calendar year for which economic cost data is available.</summary>
        public int MinCY { get; private set; }
        /// <summary>Gets the highest calendar year for which economic cost data is available.</summary>
        public int MaxCY { get; private set; }

        /// <summary>
        /// Gets the "monopsony" component of economic costs of oil imports, specified in $/gallon.
        /// </summary>
        /// <remarks>
        /// This value represents the demand cost for imported oil, determined by a complex set of factors, including
        /// the relative importance of U.S. imports in the world oil market and demand to its world price among other
        /// participants in the international oil market.
        /// </remarks>
        private double[] Monopsony { get; set; }
        /// <summary>
        /// Gets the price shock component of economic costs of oil imports, specified in $/gallon.
        /// </summary>
        /// <remarks>
        /// This value represents the expected value of cost to U.S. economy from reduction in potential output
        /// resulting from risk of significant increases in world petroleum price. This includes costs resulting from
        /// inefficiencies in resource use caused by incomplete adjustments to industry output levels and mixes of
        /// production input when world oil price changes rapidly.
        /// </remarks>
        private double[] PriceShock { get; set; }
        /// <summary>
        /// Gets the military security component of economic costs of oil imports, specified in $/gallon.
        /// </summary>
        /// <remarks>
        /// This value represents the cost to taxpayers for maintaining a military presence to secure the supply of oil
        /// imports from potentially unstable regions of the world and protect the nation against their interruption.
        /// </remarks>
        private double[] MilitarySecurity { get; set; }


        /// <summary>
        /// Returns the total economic costs of oil imports, comprising of monopsony, price shock, and military
        /// security costs for the specified calendar year.
        /// </summary>
        public double GetTotalEconomicCosts(int year)
        {
            int index = Math.Max(Math.Min(year, this.MaxCY) - this.MinCY, 0);
            return (this.Monopsony[index] + this.PriceShock[index] + this.MilitarySecurity[index]);
        }

    }

    /// <summary>
    /// Provides additional macroeconomic parameters.
    /// </summary>
    [Serializable]
    public sealed class MacroeconomicParameters
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="MacroeconomicParameters"/> class.
        /// </summary>
        public MacroeconomicParameters(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>Gets the lowest calendar year for which macroeconomic parameters are available.</summary>
        public int MinCY { get; private set; }
        /// <summary>Gets the highest calendar year for which macroeconomic parameters are available.</summary>
        public int MaxCY { get; private set; }

        /// <summary>Gets or sets the gross domestic product (GDP), in billions of dollars, by calendar year.</summary>
        private double[] GDP { get; set; }
        /// <summary>Gets or sets the number of households, in thousands, by calendar year.</summary>
        private double[] NumHouseholds { get; set; }
        /// <summary>Gets or sets the consumer sentiment by calendar year.</summary>
        private double[] ConsumerSentiment { get; set; }
        /// <summary>Gets or sets the U.S. population, in millions, by calendar year.</summary>
        private double[] USPopulation { get; set; }
        /// <summary>
        /// Gets or sets the real disposable personal income (RDPI), in billions of dollars, by calendar year.
        /// </summary>
        private double[] RDPI { get; set; }


        /// <summary>
        /// Returns the gross domestic product (GDP), in billions of dollars, for the specified calendar year.
        /// </summary>
        public double GetGdp(int year)
        {
            return this.GDP[this.YearToIndex(year)];
        }
        /// <summary>
        /// Returns the GDP growth rate for the specified calendar year.
        /// </summary>
        public double GetGdpGrowthRate(int year)
        {
            double gdpThisYear = this.GetGdp(year);
            double gdpLastYear = this.GetGdp(year - 1);
            return (gdpLastYear == 0) ? 0 : (gdpThisYear - gdpLastYear) / gdpLastYear;
        }

        /// <summary>
        /// Returns the number of households, in thousands, for the specified calendar year.
        /// </summary>
        public double GetNumHouseholds(int year)
        {
            return this.NumHouseholds[this.YearToIndex(year)];
        }
        /// <summary>
        /// Returns the consumer sentiment for the specified calendar year.
        /// </summary>
        public double GetConsumerSentiment(int year)
        {
            return this.ConsumerSentiment[this.YearToIndex(year)];
        }
        /// <summary>
        /// Returns the U.S. population, in millions, for the specified calendar year.
        /// </summary>
        public double GetUSPopulation(int year)
        {
            return this.USPopulation[this.YearToIndex(year)];
        }

        /// <summary>
        /// Returns the real disposable personal income (RDPI), in billions of dollars, for the specified calendar
        /// year.
        /// </summary>
        public double GetRdpi(int year)
        {
            return this.RDPI[this.YearToIndex(year)];
        }
        /// <summary>
        /// Returns the real disposable personal income (RDPI) per capita, in thousands of dollars, for the specified
        /// calendar year.
        /// </summary>
        public double GetRdpiPerCapita(int year)
        {
            int index = this.YearToIndex(year);
            return this.RDPI[index] / this.USPopulation[index];
        }
        /// <summary>
        /// Returns the natural logarithm of the real disposable personal income (RDPI) per capita for the specified
        /// calendar year.
        /// </summary>
        public double GetLogRdpiPerCapita(int year)
        {
            return Math.Log(this.GetRdpiPerCapita(year));
        }

        private int YearToIndex(int year)
        {
            return Math.Max(Math.Min(year, this.MaxCY) - this.MinCY, 0);
        }

    }

    /// <summary>
    /// Provides parameters for use with the VMT model.
    /// </summary>
    [Serializable]
    public sealed class VMTModelParameters
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="VMTModelParameters"/> class.
        /// </summary>
        public VMTModelParameters(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);

            // MinCY/MaxCY define global range of years specified in the input table
            // we need to adjust these ranges based on first/last non-zero value
            int newMinCY = 0;
            for (int i = 0; i < this.HistoricMPG.Length; i++) {
                if (this.HistoricMPG[i] != 0 || this.HistoricVMT[i] != 0) {
                    newMinCY = this.MinCY + i;
                    break;
                }
            }

            int newMaxCY = 0;
            for (int i = this.HistoricMPG.Length - 1; i >= 0; i--) {
                if (this.HistoricMPG[i] != 0 || this.HistoricVMT[i] != 0) {
                    newMaxCY = this.MinCY + i;
                    break;
                }
            }

            int offset = newMinCY - this.MinCY;
            int count = newMaxCY - newMinCY + 1;
            this.HistoricMPG = this.HistoricMPG.Skip(offset).Take(count).ToArray();
            this.HistoricVMT = this.HistoricVMT.Skip(offset).Take(count).ToArray();
            this.MinCY = newMinCY;
            this.MaxCY = newMaxCY;
        }


        /// <summary>Gets the lowest calendar year for which VMT model parameters are available.</summary>
        public int MinCY { get; private set; }
        /// <summary>Gets the highest calendar year for which VMT model parameters are available.</summary>
        public int MaxCY { get; private set; }

        /// <summary>Gets or sets the total VMT of the on-road fleet per calendar year.</summary>
        private double[] HistoricVMT { get; set; }
        /// <summary>Gets or sets the average miles/gallon rating of the on-road fleet per calendar year.</summary>
        private double[] HistoricMPG { get; set; }


        /// <summary>
        /// Returns the total VMT of the on-road fleet for the specified calendar year.
        /// </summary>
        public double GetHistoricVMT(int year)
        {
            return this.HistoricVMT[this.YearToIndex(year)];
        }

        /// <summary>
        /// Returns the average miles/gallon rating of the on-road fleet for the specified calendar year.
        /// </summary>
        public double GetHistoricMPG(int year)
        {
            return this.HistoricMPG[this.YearToIndex(year)];
        }

        private int YearToIndex(int year)
        {
            return Math.Max(Math.Min(year, this.MaxCY) - this.MinCY, 0);
        }

    }
    
    /// <summary>
    /// Provides economic values for benefits calculations.
    /// </summary>
    [Serializable]
    public sealed class EconomicValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EconomicValues"/> class.
        /// </summary>
        public EconomicValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>
        /// Gets an array of social discount rates, which is the percent rate by which the dollar value of a benefit or
        /// cost is reduced when its receipt or payment is postponed by one additional year into the future.
        /// </summary>
        public double[] DiscountRate { get; private set; }
        /// <summary>
        /// Gets the calendar year to use for "present year" discounting.
        /// If a base year value is used, social discounting is assumed, with all costs and benefits being discounted
        /// to that year. If 0 is used, private discounting is implied, with all costs and benefits being discounted to
        /// the model year being analyzed.
        /// </summary>
        public int DRBaseYear { get; private set; }
        /// <summary>Gets an array of consumer discount rates.</summary>
        public double[] ConsumerDiscRate { get; private set; }
        /// <summary>
        /// Gets the discount rates to apply to costs arising from damage caused by GHG pollutants associated with the
        /// various GHG estimates.
        /// </summary>
        public double[] CO2DiscountRate { get; set; }

        /// <summary>Gets deflator to apply to the current US dollars to convert to the 2012-USD.</summary>
        public double Deflator2012 { get; private set; }

        /// <summary>Gets the economic parameters for benefits calculations.</summary>
        public VSValue<EconomicParameters> Parameters { get; private set; }
        /// <summary>Gets the external costs from additional vehicle use due to "rebound" effect.</summary>
        public VSValue<ExternalCosts> ExternalCosts { get; private set; }
        /// <summary>Gets the ownership and operating costs associated with purchase of new vehicles.</summary>
        public VSValue<OperatingCosts> OperatingCosts { get; private set; }
        /// <summary>Gets the economic costs of oil imports.</summary>
        public EconomicCosts EconomicCosts { get; private set; }
        /// <summary>Gets the additional macroeconomic parameters.</summary>
        public MacroeconomicParameters MacroParameters { get; private set; }
        /// <summary>Gets the parameters for use with the VMT model.</summary>
        public VMTModelParameters VMTModelParameters { get; private set; }


        /// <summary>
        /// Returns low, average, high, or very high CO2 discount rate for the specified CO2 estimates.
        /// </summary>
        /// <param name="co2Estimates">The low, average, high, or very high CO2 estimates to use.</param>
        /// <returns>Low, average, high, or very high CO2 discount rate for the specified CO2 estimates.</returns>
        public double GetCO2DiscountRate(Estimates co2Estimates)
        {
            return this.CO2DiscountRate[(int)co2Estimates];
        }

    }

    /// <summary>
    /// Provides vehicle age data for cars, vans, SUVs, and pickups.
    /// </summary>
    [Serializable]
    public sealed class VehicleAgeData : VSValue<double[]>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="VehicleAgeData"/> class.
        /// </summary>
        public VehicleAgeData(double[] cars, double[] vansSuvs, double[] pickups, double[] class2b3)
            : base(cars, vansSuvs, pickups, class2b3) { }

        /// <summary>Gets the vehicle age data based on the given vehicle style.</summary>
        /// <exception cref="System.IndexOutOfRangeException">
        ///     The <paramref name="vehStyle"/> specified does not represent a valid <see cref="VehicleStyle"/>.
        /// </exception>
        public new double[] this[VehicleStyle vehStyle] => base[vehStyle];

    }

    /// <summary>
    /// Provides a forecast of fuel prices per calendar year for all supported fuel types.
    /// </summary>
    [Serializable]
    public sealed class FuelPrices
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FuelPrices"/> class.
        /// </summary>
        public FuelPrices(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>Gets the lowest calendar year for which forecast fuel price data is available.</summary>
        public int MinCY { get; private set; }
        /// <summary>Gets the highest calendar year for which forecast fuel price data is available.</summary>
        public int MaxCY { get; private set; }

        /// <summary>Gets the forecast data for fuel taxes per calendar year.</summary>
        public FTValue<double[]> FuelTax { get; private set; }
        /// <summary>Gets the forecast data for retail fuel prices per calendar year.</summary>
        public FTValue<double[]> FuelPrice { get; private set; }


        /// <summary>
        /// Returns forecast data for fuel taxes for the specified fuel type, in the specified calendar year.
        /// </summary>
        /// <param name="fuelType">The fuel type for which to obtain the fuel taxes.</param>
        /// <param name="year">The calendar year for which to obtain the fuel taxes.</param>
        /// <returns>
        /// Forecast data for fuel taxes for the specified fuel type, in the specified calendar year.
        /// </returns>
        public double GetFuelTax(FuelType fuelType, int year)
        {
            int index = Math.Max(Math.Min(year, this.MaxCY) - this.MinCY, 0);
            return this.FuelTax[fuelType][index];
        }
        /// <summary>
        /// Returns forecast data for fuel taxes for the specified fuel type, in the specified calendar year, using the
        /// specified fuel properties information.
        /// </summary>
        /// <param name="fuelType">The fuel type for which to obtain the fuel taxes.</param>
        /// <param name="year">The calendar year for which to obtain the fuel taxes.</param>
        /// <param name="fuelProperties">The physical and chemical properties of various fuel types</param>
        /// <returns>
        /// Forecast data for fuel taxes for the specified fuel type, in the specified calendar year.
        /// </returns>
        public double GetFuelTax(FuelType fuelType, int year, FuelProperties fuelProperties)
        {
            return this.ConvertToGGE(this.GetFuelTax(fuelType, year), fuelType, fuelProperties);
        }

        /// <summary>
        /// Returns forecast data for retail fuel price for the specified fuel type, in the specified calendar year.
        /// </summary>
        /// <param name="fuelType">The fuel type for which to obtain the retail fuel prices.</param>
        /// <param name="year">The calendar year for which to obtain the retail fuel prices.</param>
        /// <returns>
        /// Forecast data for retail fuel price for the specified fuel type, in the specified calendar year.
        /// </returns>
        public double GetFuelPrice(FuelType fuelType, int year)
        {
            int index = Math.Max(Math.Min(year, this.MaxCY) - this.MinCY, 0);
            return this.FuelPrice[fuelType][index];
        }
        /// <summary>
        /// Returns forecast data for retail fuel price for the specified fuel type, in the specified calendar year.
        /// </summary>
        /// <param name="fuelType">The fuel type for which to obtain the retail fuel prices.</param>
        /// <param name="year">The calendar year for which to obtain the retail fuel prices.</param>
        /// <param name="fuelProperties">The physical and chemical properties of various fuel types.</param>
        /// <returns>
        /// Forecast data for retail fuel price for the specified fuel type, in the specified calendar year.
        /// </returns>
        public double GetFuelPrice(FuelType fuelType, int year, FuelProperties fuelProperties)
        {
            return this.ConvertToGGE(this.GetFuelPrice(fuelType, year), fuelType, fuelProperties);
        }

        private double ConvertToGGE(double value, FuelType fuelType, FuelProperties fuelProperties)
        {
            FuelValue ed = fuelProperties.EnergyDensity;
            return (fuelType == FuelType.Electricity) ? value * ed.Gasoline / ed.Electricity
                 : (fuelType == FuelType.Hydrogen   ) ? value * ed.Gasoline / ed.Hydrogen
                 : (fuelType == FuelType.CNG        ) ? value * ed.Gasoline / ed.CNG
                 : value;
        }

    }

    /// <summary>
    /// Provides historic fleet data, defined over a range of model years, for all classes of vehicles.
    /// </summary>
    [Serializable]
    public sealed class HistoricFleetData
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="HistoricFleetData"/> class.
        /// </summary>
        public HistoricFleetData(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>Gets the lowest model year for which historic fleet data is available.</summary>
        public int MinMY { get; private set; }
        /// <summary>Gets the highest model year for which historic fleet data is available.</summary>
        public int MaxMY { get; private set; }

        /// <summary>Gets or sets the historic fleet data.</summary>
        private VSValue<HistoricFleetDataItem[]> HistoricData { get; set; }


        /// <summary>
        /// Returns the initial production for the specified vehicle class and model year.
        /// </summary>
        /// <param name="vehClass">The vehicle class for which to obtain the historic fleet data.</param>
        /// <param name="year">The model year for which to obtain the historic fleet data.</param>
        /// <returns>The initial fleet data for the specified item-index and model year.</returns>
        public double GetInitialProduction(VehicleClass vehClass, int year)
        {
            return vehClass.IsLDV   () ? GetInitialProduction(VehicleStyle.GenericCar)
                 : vehClass.IsLDT12a() ? GetInitialProduction(VehicleStyle.GenericVanSUV) +
                                         GetInitialProduction(VehicleStyle.GenericPickup)
                 : vehClass.IsLDT2b3() ? GetInitialProduction(VehicleStyle.Generic2b3)
                 : 0;

            // local helper method
            double GetInitialProduction(VehicleStyle style)
            {
                return this.GetHistoricFleetData(style, year).InitialProduction;
            }
        }

        /// <summary>
        /// Returns the annual historic fleet data for the specified model year.
        /// </summary>
        /// <param name="year">The model year for which to obtain the annual historic fleet data.</param>
        /// <param name="minMY">The minimum model year available for compliance modeling.</param>
        /// <param name="survivalRates">
        ///     Vehicle survival rates to use for estimating the historic annual fleet data.
        /// </param>
        /// <returns>The annual historic fleet data for the specified model year.</returns>
        public VSValue<double[]> GetAnnualHistoricFleetData(int year, int minMY, VehicleAgeData survivalRates)
        {
            var vsFleetData = new VSValue<double[]>();

            for (int i = 0; i < vsFleetData.Items.Length; ++i) {
                double[] fleet = new double[Globals.CY];
                int age = minMY - year;

                if (age < Globals.CY) {
                    double[] surv = survivalRates.Items[i];
                    fleet[age++] = this.GetHistoricFleetData(i, year).OnRoadFleet;

                    for (; age < Globals.CY; age++) {
                        fleet[age] = (surv[age - 1] == 0) ? 0 : fleet[age - 1] * surv[age] / surv[age - 1];
                    }
                }

                vsFleetData.Items[i] = fleet;
            }

            return vsFleetData;
        }

        /// <summary>
        /// Returns the historic fleet data for the specified vehicle style and model year.
        /// </summary>
        /// <param name="vehStyle">The vehicle style for which to obtain the historic fleet data.</param>
        /// <param name="year">The model year for which to obtain the historic fleet data.</param>
        /// <returns>The historic fleet data for the specified vehicle style and model year.</returns>
        public HistoricFleetDataItem GetHistoricFleetData(VehicleStyle vehStyle, int year)
        {
            int itemIndex = VSValue<object>.GetIndex(vehStyle);
            return this.GetHistoricFleetData(itemIndex, year);
        }
        /// <summary>
        /// Returns the historic fleet data for the specified item-index and model year.
        /// </summary>
        /// <param name="itemIndex">
        ///     An index corresponding to the vehicle style for which to obtain the historic fleet data.
        /// </param>
        /// <param name="year">The model year for which to obtain the historic fleet data.</param>
        /// <returns>The historic fleet data for the specified item-index and model year.</returns>
        public HistoricFleetDataItem GetHistoricFleetData(int itemIndex, int year)
        {
            int index = Math.Max(Math.Min(year, this.MaxMY) - this.MinMY, 0);
            return this.HistoricData.Items[itemIndex][index];
        }

    }

    /// <summary>
    /// Provides historic fleet data for a single model year, and for a single class of vehicles.
    /// </summary>
    [Serializable]
    public sealed class HistoricFleetDataItem
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="HistoricFleetDataItem"/> class.
        /// </summary>
        public HistoricFleetDataItem(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>
        /// Gets the initial production (the on-road fleet at age 0) for all vehicles of a specific historic model
        /// year.
        /// </summary>
        public double InitialProduction { get; private set; }
        /// <summary>
        /// Gets the surviving on-road fleet of all vehicles produced during a specific historic model year. This
        /// on-road fleet should be pre-scrapped to the calendar year that equates to the first model year evaluated
        /// for compliance.
        /// </summary>
        public double OnRoadFleet { get; private set; }
        /// <summary>
        /// Gets the share of the on-road fleet that are regulated as passenger cars, among all vehicles produced
        /// during a specific historic model year.
        /// </summary>
        public double PCShare { get; private set; }

        /// <summary>
        /// Gets the average on-road fuel economy for vehicles produced during a specific historic model year.
        /// </summary>
        public FuelValue OnRoadFE { get; private set; }
        /// <summary>
        /// Gets the average fuel economy shares for vehicles produced during a specific historic model year.
        /// </summary>
        public FuelValue FuelShare { get; private set; }

        /// <summary>
        /// Gets the average horsepower for vehicles produced during a specific historic model year.
        /// </summary>
        public double Horsepower { get; private set; }
        /// <summary>
        /// Gets the average curb weight for vehicles produced during a specific historic model year.
        /// </summary>
        public double CurbWeight { get; private set; }
        /// <summary>
        /// Gets the average fuel tank capacity for vehicles produced during a specific historic model year.
        /// </summary>
        public double FuelCapacity { get; private set; }
        /// <summary>
        /// Gets the average transaction price for vehicles sold during a specific historic model year.
        /// </summary>
        public double TransactionPrice { get; private set; }

    }

    /// <summary>
    /// Provides parameters for use with the Dynamic Fleet Share (DFS) model.
    /// </summary>
    [Serializable]
    public sealed class DynamicFleetShareModelValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DynamicFleetShareModelValues"/> class.
        /// </summary>
        public DynamicFleetShareModelValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>Gets the coefficients to use for estimating fleet share.</summary>
        public double[] DfsCoefficients { get; private set; }
        /// <summary>Gets the bounding coefficients to use for estimating the "floor" of the DFS model.</summary>
        public double[] BoundingCoefficients { get; private set; }

        /// <summary>
        /// Gets the static per-year car shares to use with the <see cref="DfsModel.UserDefinedShares"/> DFS model.
        /// </summary>
        public IAnnualValue<double> StaticCarShares { get; private set; }
    }

    /// <summary>
    /// Provides parameters for use with the Sales Response (SR) model.
    /// </summary>
    [Serializable]
    public sealed class SalesModelValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SalesModelValues"/> class.
        /// </summary>
        public SalesModelValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>
        /// Gets the macroeconomic model coefficients to use for estimating the total light-duty vehicle sales in each
        /// future model year for the baseline scenario, whenever the <see cref="SalesEstimator.NominalForecast"/>
        /// option is selected for the DFS/SR model.
        /// </summary>
        public double[] NFCoefficientsLD { get; private set; }
        /// <summary>
        /// Gets the macroeconomic model coefficients to use for estimating the total HDPUV vehicle sales in each
        /// future model year for the baseline scenario whenever the <see cref="SalesEstimator.NominalForecast"/>
        /// option is selected for the DFS/SR model.
        /// </summary>
        public double[] NFCoefficientsHDPUV{ get; private set; }

        /// <summary>
        /// Gets the annual sales forecast to use for estimating the total light-duty vehicle sales in each future
        /// model year for the baseline scenario, whenever the <see cref="SalesEstimator.UserDefinedForecast"/> option
        /// is selected for the DFS/SR model.
        /// </summary>
        public IAnnualValue<double> AnnualForecastLD { get; private set; }
        /// <summary>
        /// Gets the annual sales forecast to use for estimating the total HDPUV vehicle sales in each future model
        /// year for the baseline scenario, whenever the <see cref="SalesEstimator.UserDefinedForecast"/> option is
        /// selected for the DFS/SR model.
        /// </summary>
        public IAnnualValue<double> AnnualForecastHDPUV { get; private set; }

    }

    /// <summary>
    /// Provides parameters for use with the Dynamic Scrappage model.
    /// </summary>
    [Serializable]
    public sealed class DynamicScrappageModelValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DynamicScrappageModelValues"/> class.
        /// </summary>
        public DynamicScrappageModelValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>Gets the beta coefficients to use for estimating scrappage.</summary>
        public VSValue<double[]> BetaCoefficients { get; private set; }

    }

    /// <summary>
    /// Provides values necessary for safety calculations.
    /// </summary>
    [Serializable]
    public sealed class SafetyValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SafetyValues"/> class.
        /// </summary>
        public SafetyValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>Gets the boundary, in lbs., between small and large weight effects.</summary>
        public SCValue<double> Threshold { get; private set; }
        /// <summary>
        /// Gets the effect of weight reduction (the change in fatalities per 100 lb reduction in curb weight) for
        /// vehicles below the weight threshold.
        /// </summary>
        public SCValue<double> SmallEffect { get; private set; }
        /// <summary>
        /// Gets the effect of weight reduction (the change in fatalities per 100 lb reduction in curb weight) for
        /// vehicles above the weight threshold.
        /// </summary>
        public SCValue<double> LargeEffect { get; private set; }

        /// <summary>
        /// Gets a tuple of the social costs arising from vehicle-related fatalities along with the associated annual
        /// growth rate applicable to those costs.
        /// </summary>
        public (double Cost, double GrowthRate) FatalityCosts { get; private set; }
        /// <summary>
        /// Gets a tuple of the social costs arising from vehicle-related non-fatal injuries along with the associated
        /// annual growth rate applicable to those costs.
        /// </summary>
        public (double Cost, double GrowthRate) NonFatalInjuryCosts { get; private set; }
        /// <summary>
        /// Gets a tuple of the social costs arising from vehicle-related property damage crashes along with the
        /// associated annual growth rate applicable to those costs.
        /// </summary>
        public (double Cost, double GrowthRate) PropertyDamageCrashCosts { get; private set; }

        /// <summary>Gets the base year for annual growth rate for fatality costs per vehicle.</summary>
        public int BaseYear { get; private set; }

        /// <summary>
        /// Gets the fatality risk internalized by the driver, attributed to the additional miles driven due to
        /// rebound.
        /// </summary>
        public double FatalityRisk { get; private set; }


        /// <summary>
        /// Returns the effect of weight reduction (the change in fatalities per 100 lb reduction in curb weight) for
        /// the specified vehicle safety class and curb weight.
        /// </summary>
        public double GetEffect(SafetyClass safetyClass, double curbWeight)
        {
            return (curbWeight < this.Threshold[safetyClass])
                ? this.SmallEffect[safetyClass]
                : this.LargeEffect[safetyClass];
        }

        /// <summary>
        /// Returns the social costs arising from vehicle-related fatalities, adjusted by the associated growth rate
        /// to reflect the costs during the specified calendar year.
        /// </summary>
        public double GetFatalityCosts(int calendarYear)
        {
            return this.GetSafetyCostsCommon(calendarYear, this.FatalityCosts);
        }
        /// <summary>
        /// Returns the social costs arising from vehicle-related non-fatal injuries, adjusted by the associated growth
        /// rate to reflect the costs during the specified calendar year.
        /// </summary>
        public double GetNonFatalInjuryCosts(int calendarYear)
        {
            return this.GetSafetyCostsCommon(calendarYear, this.NonFatalInjuryCosts);
        }
        /// <summary>
        /// Returns the social costs arising from vehicle-related property damage crashes, adjusted by the associated
        /// growth rate to reflect the costs during the specified calendar year.
        /// </summary>
        public double GetPropertyDamageCrashCosts(int calendarYear)
        {
            return this.GetSafetyCostsCommon(calendarYear, this.PropertyDamageCrashCosts);
        }

        // fatality/non-fatal costs helper method
        private double GetSafetyCostsCommon(int calendarYear, (double Cost, double GrowthRate) costValue)
        {
            return costValue.Cost * Math.Pow(1 + costValue.GrowthRate, calendarYear - this.BaseYear);
        }

    }

    /// <summary>
    /// Provides fatality rates by model year and vehicle age.
    /// </summary>
    [Serializable]
    public sealed class FatalityRates
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FatalityRates"/> class.
        /// </summary>
        public FatalityRates(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>Gets the lowest model year for which fatality rates data is available.</summary>
        public int MinMY { get; private set; }
        /// <summary>Gets the highest model year for which fatality rates data is available.</summary>
        public int MaxMY { get; private set; }

        /// <summary>
        /// Gets a table of low estimates of fatality rates (specified per billion VMT).
        /// </summary>
        private double[][] FatalityRateLow { get; set; }
        /// <summary>
        /// Gets a table of average estimates of fatality rates (specified per billion VMT).
        /// </summary>
        private double[][] FatalityRateAverage { get; set; }
        /// <summary>
        /// Gets a table of high estimates of fatality rates (specified per billion VMT).
        /// </summary>
        private double[][] FatalityRateHigh { get; set; }

        /// <summary>
        /// Gets a table of low estimates of non-fatal injury rates (specified per billion VMT).
        /// </summary>
        private double[][] NonFatalInjuryRateLow { get; set; }
        /// <summary>
        /// Gets a table of average estimates of non-fatal injury rates (specified per billion VMT).
        /// </summary>
        private double[][] NonFatalInjuryRateAverage { get; set; }
        /// <summary>
        /// Gets a table of high estimates of non-fatal injury rates (specified per billion VMT).
        /// </summary>
        private double[][] NonFatalInjuryRateHigh { get; set; }

        /// <summary>
        /// Gets a table of low estimates of property damage crash rates (specified per billion VMT).
        /// </summary>
        private double[][] PropertyDamageCrashRateLow { get; set; }
        /// <summary>
        /// Gets a table of average estimates of property damage crash rates (specified per billion VMT).
        /// </summary>
        private double[][] PropertyDamageCrashRateAverage { get; set; }
        /// <summary>
        /// Gets a table of high estimates of property damage crash rates (specified per billion VMT).
        /// </summary>
        private double[][] PropertyDamageCrashRateHigh { get; set; }


        /// <summary>
        /// Returns fatality rate for the specified technology effectiveness estimates, model year, and age. Fatality
        /// rates are denominated in units per billion VMT.
        /// </summary>
        public double GetFatalityRate(Estimates fatalityEstimates, int year, int age)
        {
            return this.GetRateCommon(
                fatalityEstimates, year, age,
                this.FatalityRateLow, this.FatalityRateAverage, this.FatalityRateHigh);
        }

        /// <summary>
        /// Returns non-fatal injury rate for the specified technology effectiveness estimates, model year, and age.
        /// Non-fatal injury rates are denominated in units per billion VMT.
        /// </summary>
        public double GetNonFatalInjuryRate(Estimates fatalityEstimates, int year, int age)
        {
            return this.GetRateCommon(
                fatalityEstimates, year, age,
                this.NonFatalInjuryRateLow, this.NonFatalInjuryRateAverage, this.NonFatalInjuryRateHigh);
        }

        /// <summary>
        /// Returns property damage crash rate for the specified technology effectiveness estimates, model year, and
        /// age. Property damage crash rates are denominated in units per billion VMT.
        /// </summary>
        public double GetPropertyDamageCrashRate(Estimates fatalityEstimates, int year, int age)
        {
            return this.GetRateCommon(
                fatalityEstimates, year, age,
                this.PropertyDamageCrashRateLow, this.PropertyDamageCrashRateAverage, this.PropertyDamageCrashRateHigh);
        }

        private double GetRateCommon(
            Estimates fatalityEstimates, int year, int age,
            double[][] lowRate, double[][] averageRate, double[][] highRate)
        {
            double[][] rate = (fatalityEstimates == Estimates.Low) ? lowRate
                            : (fatalityEstimates == Estimates.High) ? highRate
                            : averageRate;
            int index = this.YearToIndex(year);
            return rate[index][age];
        }

        private int YearToIndex(int year)
        {
            return Math.Max(Math.Min(year, this.MaxMY) - this.MinMY, 0);
        }

    }

    /// <summary>
    /// Provides fine tuning parameters for using credit trading in the modeling system.
    /// </summary>
    [Serializable]
    public sealed class CreditTradingValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CreditTradingValues"/> class.
        /// </summary>
        public CreditTradingValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        //----- credit trading options -----
        /// <summary>
        /// Gets whether to consider credit trading between manufacturers within the same regulatory class and model
        /// year.
        /// </summary>
        public bool AllowMfrTrading { get; private set; }
        /// <summary>
        /// Gets whether to consider credit transfers between regulatory classes within the same manufacturer and model
        /// year.
        /// </summary>
        public bool AllowFleetTransfers { get; private set; }

        /// <summary>
        /// Gets whether to carry credits forward into the analysis year from earlier model years within the same
        /// manufacturer and regulatory class.
        /// </summary>
        public bool AllowCarryForward { get; private set; }
        /// <summary>Gets the maximum number of model years to look forward.</summary>
        public int CarryForwardYears { get; private set; }

        /// <summary>
        /// Gets whether to carry credits backward into the analysis year from future model years within the same
        /// manufacturer and regulatory class.
        /// </summary>
        public bool AllowCarryBackward { get; private set; }
        /// <summary>Gets the maximum number of model years to look backward.</summary>
        public int CarryBackwardYears { get; private set; }

        //----- additional runtime options -----
        /// <summary>Gets the maximum number of model years to consider when using expiring credits.</summary>
        public int MaxExpiringYears { get; private set; }

        //----- transfer caps & VMT by model year -----
        /// <summary>
        /// Gets the first model year when credits may be traded, transferred, or carried forward/backward.
        /// </summary>
        private int MinMY { get; set; }

        /// <summary>
        /// Gets the transfer caps, specified in mpg, corresponding to the maximum amount of credits that may be
        /// transferred into a regulatory class for each model year. The cap from the latest model year is carried
        /// forward for all subsequent years.
        /// </summary>
        private RCValue<double[]> TransferCaps { get; set; }

        /// <summary>
        /// Gets the assumed lifetime VMT to use when credits are traded or transferred between compliance categories.
        /// The assumed lifetime VMT from the latest year is carried forward for all subsequent years. This parameter
        /// is ignored for credit carry forward and carry back operations.
        /// </summary>
        private RCValue<double[]> AdjustmentVMT { get; set; }


        /// <summary>
        /// Returns the transfer caps for the specified regulatory class, in the specified model year.
        /// </summary>
        /// <param name="regClass">The regulatory class for which to obtain the adjustment VMT.</param>
        /// <param name="year">The model year for which to obtain the transfer caps.</param>
        /// <returns>The transfer caps for the specified regulatory class, in the specified model year.</returns>
        /// <remarks>
        /// The transfer caps, specified in mpg, correspond to the maximum amount of credits that may be transferred
        /// into a regulatory class for each model year. The cap from the last defined model year is used in all
        /// subsequent years.
        /// </remarks>
        public double GetTransferCaps(RegulatoryClass regClass, int year)
        {
            if (year < this.MinMY) {
                return 0;
            }
            int index = Math.Min(year - this.MinMY, this.TransferCaps[regClass].Length - 1);
            return this.TransferCaps[regClass][index];
        }

        /// <summary>
        /// Returns the adjustment VMT for the specified regulatory class, in the specified model year.
        /// </summary>
        /// <param name="regClass">The regulatory class for which to obtain the adjustment VMT.</param>
        /// <param name="year">The model year for which to obtain the adjustment VMT.</param>
        /// <returns>The adjustment VMT for the specified regulatory class, in the specified model year.</returns>
        /// <remarks>
        /// The adjustment VMT represents the assumed lifetime VMT to use when credits are traded or transferred
        /// between compliance categories. The assumed lifetime VMT from the last defined model year is used in all
        /// subsequent years. This parameter is ignored for credit carry forward and carry back operations.
        /// </remarks>
        public double GetAdjustmentVMT(RegulatoryClass regClass, int year)
        {
            if (year < this.MinMY) {
                return 0;
            }
            int index = Math.Min(year - this.MinMY, this.AdjustmentVMT[regClass].Length - 1);
            return this.AdjustmentVMT[regClass][index];
        }

    }

    /// <summary>
    /// Provides fine tuning parameters for using ZEV credits in the modeling system.
    /// </summary>
    [Serializable]
    public sealed class ZEVCreditValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ZEVCreditValues"/> class.
        /// </summary>
        public ZEVCreditValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }


        /// <summary>Gets the lowest model year for which ZEV credit values are available.</summary>
        private int MinMY { get; set; }
        /// <summary>Gets the highest model year for which ZEV credit values are available.</summary>
        private int MaxMY { get; set; }

        /// <summary>
        /// Gets the minimum percentage of zero emission vehicle (ZEV) credits that a manufacturer must generate in
        /// order to meet the ZEV requirement in each specified model year, as applicable to the light duty fleet.
        /// </summary>
        private double[] ZevRequirement { get; set; }
        /// <summary>
        /// Gets the maximum percentage of ZEV credits that a manufacturer may generate from PHEVs in order to meet the
        /// ZEV requirement in each specified model year, as applicable to the light duty fleet.
        /// </summary>
        private double[] PhevCap { get; set; }

        /// <summary>
        /// Gets the minimum percentage of zero emission vehicle (ZEV) credits that a manufacturer must generate in
        /// order to meet the ZEV requirement in each specified model year, as applicable to the HDPUV fleet.
        /// </summary>
        private double[] ZevRequirementHDPUV { get; set; }
        /// <summary>
        /// Gets the maximum percentage of ZEV credits that a manufacturer may generate from PHEVs in order to meet the
        /// ZEV requirement in each specified model year, as applicable to the HDPUV fleet.
        /// </summary>
        private double[] PhevCapHDPUV { get; set; }


        /// <summary>
        /// Returns the ZEV requirement for the specified model year and regulatory class, which is the minimum
        /// percentage of ZEV credits that a manufacturer must generate in order to meet the ZEV requirement.
        /// </summary>
        public double GetZevRequirement(int year, RegulatoryClass regClass)
        {
            int index = Math.Max(Math.Min(year, this.MaxMY) - this.MinMY, 0);
            return regClass.IsHDPUV() ? this.ZevRequirementHDPUV[index] : this.ZevRequirement[index];
        }
        /// <summary>
        /// Returns the PHEV allowance for the specified model year and regulatory class, which is the maximum
        /// percentage of ZEV credits that a manufacturer may generate from PHEVs in order to meet the ZEV requirement.
        /// </summary>
        public double GetPhevCap(int year, RegulatoryClass regClass)
        {
            int index = Math.Max(Math.Min(year, this.MaxMY) - this.MinMY, 0);
            return regClass.IsHDPUV() ? this.PhevCapHDPUV[index] : this.PhevCap[index];
        }

    }

    /// <summary>
    /// Provides parameters for calculating employment effects.
    /// </summary>
    [Serializable]
    public sealed class EmploymentValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EmploymentValues"/> class.
        /// </summary>
        public EmploymentValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>Gets the manufacturer's revenue per employee.</summary>
        public double OEMRevenue { get; private set; }
        /// <summary>Gets the manufacturer supplier's revenue per employee.</summary>
        public double SuplierRevenue { get; private set; }
        /// <summary>Gets the retail price estimate markup applied to technology costs.</summary>
        public double RPE { get; private set; }
        /// <summary>Gets the annual labor hours per employee.</summary>
        public double AnnualLaborHours { get; private set; }
        /// <summary>Gets multiplier to apply to US assembly/manufacturing labor hours.</summary>
        public double USAssemblyMultiplier { get; private set; }
        /// <summary>Gets multiplier to apply to all labor hours.</summary>
        public double GlobalMultiplier { get; private set; }

    }

    /// <summary>
    /// Provides physical and chemical properties of various fuel types.
    /// </summary>
    [Serializable]
    public sealed class FuelProperties
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FuelProperties"/> class.
        /// </summary>
        public FuelProperties(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>
        /// Gets the energy density of various fuel types (specified in BTU/gallon, BTU/kW-h, or BTU/scf).
        /// </summary>
        public FuelValue EnergyDensity { get; private set; }
        /// <summary>
        /// Gets the mass density of various fuel types (specified in grams/gallon, grams/kW-h, or grams/scf).
        /// </summary>
        public FuelValue MassDensity { get; private set; }
        /// <summary>Gets the carbon content of various fuel types (specified in % by weight).</summary>
        public FuelValue CarbonContent { get; private set; }
        /// <summary>
        /// Gets the Carbon Dioxide upstream emissions for various fuel types (specified in grams/gallon, grams/kW-h,
        /// or grams/scf).
        /// </summary>
        public FuelValue CO2Emissions { get; private set; }
        /// <summary>
        /// Gets the Sulfur Dioxide upstream emissions for various fuel types (specified in grams/MMBTU).
        /// </summary>
        public FuelValue SO2Emissions { get; private set; }

        /// <summary>
        /// Obtains the carbon content attributed to the specified <paramref name="fuel"/>.
        /// </summary>
        /// <remarks>
        /// If the specified <paramref name="fuel"/> type is gasoline, diesel, or E85, the content of CO-2 per gallon
        /// of given fuel is returned. If the specified <paramref name="fuel"/> type is electricity, hydrogen, or CNG,
        /// the content of CO-2 per gallon of gasoline is returned instead.
        /// <br/><br/>
        /// The content of CO-2 may be used when converting a vehicle's fuel economy from mpg to grams/mile of CO-2.
        /// Since a vehicle's fuel economy is measured in native units (for gasoline, diesel, and E85) or in GGE (for
        /// electricity, hydrogen, and CNG), for GGE fuel types, this method returns the fuel carbon content of
        /// gasoline (rather than native units) in order to perform the conversion accurately.
        /// </remarks>
        public double GetFuelCarbonContent(FuelType fuel)
        {
            FuelValue co2Emissions = this.CO2Emissions;
            return fuel.IsGEGFuel() ? co2Emissions.Gasoline : co2Emissions[fuel];
        }

    }

    /// <summary>
    /// Provides fuel import assumptions for various fuel types.
    /// </summary>
    [Serializable]
    public sealed class FuelImportAssumptions
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FuelImportAssumptions"/> class.
        /// </summary>
        public FuelImportAssumptions(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>Gets an assumed value for share of fuel savings leading to lower fuel imports.</summary>
        public double FuelSavingsLeadingToLowerImports { get; private set; }
        /// <summary>Gets an assumed value for share of fuel savings leading to reduced domestic fuel refining.</summary>
        public double FuelSavingsLeadingToReducedRefining { get; private set; }
        /// <summary>Gets an assumed value for share of reduced domestic refining from domestic crude.</summary>
        public double ReducedRefiningFromDomesticCrude { get; private set; }
        /// <summary>Gets an assumed value for share of reduced domestic refining from imported crude.</summary>
        public double ReducedRefiningFromImportedCrude { get; private set; }

        /// <summary>
        /// Gets the share of domestic fuel refining, calculated as:
        /// FuelSavingsLeadingToLowerImports + FuelSavingsLeadingToReducedRefining * ReducedRefiningFromImportedCrude.
        /// </summary>
        public double GetShareOfDomesticRefining()
        {
            return (this.FuelSavingsLeadingToLowerImports +
                    this.FuelSavingsLeadingToReducedRefining * this.ReducedRefiningFromImportedCrude);
        }

    }

    /// <summary>
    /// Provides a collection of fuel import assumptions for various fuel types by calendar year.
    /// </summary>
    [Serializable]
    public sealed class FuelImportAssumptionsCollection
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FuelImportAssumptionsCollection"/> class.
        /// </summary>
        public FuelImportAssumptionsCollection(ValueByYear<FTValue<FuelImportAssumptions>> values)
        {
            this.Values = values;
        }

        /// <summary>Gets the fuel import assumptions for the specified fuel type and calendar year.</summary>
        public FuelImportAssumptions this[FuelType fuelType, int year] => this.Values[year][fuelType];

        /// <summary>Gets the fuel import assumptions, indexed as: calendarYear, fuelType, assumption.</summary>
        private ValueByYear<FTValue<FuelImportAssumptions>> Values { get; set; }

    }

    /// <summary>
    /// Provides costs arising from greenhouse emission damage.
    /// </summary>
    [Serializable]
    public sealed class GreenhouseEmissionCosts
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="GreenhouseEmissionCosts"/> class.
        /// </summary>
        public GreenhouseEmissionCosts(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);

            this.CO2 = new double[][] { this.CostsLowCO2, this.CostsAvgCO2, this.CostsHighCO2, this.CostsVeryHighCO2 };
            this.CH4 = new double[][] { this.CostsLowCH4, this.CostsAvgCH4, this.CostsHighCH4, this.CostsVeryHighCH4 };
            this.N2O = new double[][] { this.CostsLowN2O, this.CostsAvgN2O, this.CostsHighN2O, this.CostsVeryHighN2O };
        }


        /// <summary>Gets the lowest calendar year for which CO-2 price data is available.</summary>
        public int MinCY { get; private set; }
        /// <summary>Gets the highest calendar year for which CO-2 price data is available.</summary>
        public int MaxCY { get; private set; }

        private double[] CostsLowCO2 { get; set; }
        private double[] CostsAvgCO2 { get; set; }
        private double[] CostsHighCO2 { get; set; }
        private double[] CostsVeryHighCO2 { get; set; }

        private double[] CostsLowCH4 { get; set; }
        private double[] CostsAvgCH4 { get; set; }
        private double[] CostsHighCH4 { get; set; }
        private double[] CostsVeryHighCH4 { get; set; }

        private double[] CostsLowN2O { get; set; }
        private double[] CostsAvgN2O { get; set; }
        private double[] CostsHighN2O { get; set; }
        private double[] CostsVeryHighN2O { get; set; }

        // the first index in the array corresponds to the cost estimate (low, average, high, very high); 
        // the second index is the calendar year.
        private double[][] CO2 { get; }
        private double[][] CH4 { get; }
        private double[][] N2O { get; }


        /// <summary>
        /// Returns low, average, high, or very high CO2 cost for the specified cost estimates, in the specified
        /// calendar year.
        /// </summary>
        public double GetCO2Costs(Estimates costEstimates, int year)
        {
            return this.CO2[(int)costEstimates][this.YearToIndex(year)];
        }
        /// <summary>
        /// Returns low, average, high, or very high CH4 cost for the specified cost estimates, in the specified
        /// calendar year.
        /// </summary>
        public double GetCH4Costs(Estimates costEstimates, int year)
        {
            return this.CH4[(int)costEstimates][this.YearToIndex(year)];
        }
        /// <summary>
        /// Returns low, average, high, or very high N2O cost for the specified cost estimates, in the specified
        /// calendar year.
        /// </summary>
        public double GetN2OCosts(Estimates costEstimates, int year)
        {
            return this.N2O[(int)costEstimates][this.YearToIndex(year)];
        }

        private int YearToIndex(int year)
        {
            return Math.Max(Math.Min(year, this.MaxCY) - this.MinCY, 0);
        }

    }

    /// <summary>
    /// Provides costs arising from criteria emission damage.
    /// </summary>
    [Serializable]
    public sealed class CriteriaEmissionCosts
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CriteriaEmissionCosts"/> class.
        /// </summary>
        public CriteriaEmissionCosts(
            Dictionary<Estimates, ValueByYear<CriteriaEmissionsValues>> dr3,
            Dictionary<Estimates, ValueByYear<CriteriaEmissionsValues>> dr7)
        {
            this.DR3 = dr3[Estimates.Average];
            this.DR7 = dr7[Estimates.Average];
        }

        /// <summary>
        /// Gets the emission damage costs pre-discounted at 3%, associated with upstream and tailpipe emissions
        /// of NOx, SO2, and PM2.5 criteria pollutants. This value is specified by calendar year for each criteria
        /// pollutant.
        /// </summary>
        public ValueByYear<CriteriaEmissionsValues> DR3 { get; private set; }
        /// <summary>
        /// Gets the emission damage costs pre-discounted at 7%, associated with upstream and tailpipe emissions
        /// of NOx, SO2, and PM2.5 criteria pollutants. This value is specified by calendar year for each criteria
        /// pollutant.
        /// </summary>
        public ValueByYear<CriteriaEmissionsValues> DR7 { get; private set; }

    }

    /// <summary>
    /// Provides emission health impacts associated with upstream and tailpipe emissions of NOx, SO2, and PM2.5
    /// criteria pollutants during specific calendar years.
    /// </summary>
    [Serializable]
    public sealed class EmissionHealthImpacts
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EmissionHealthImpacts"/> class.
        /// </summary>
        public EmissionHealthImpacts(List<ValueByYear<CriteriaEmissionsValues>> healthImpacts)
        {
            this.HealthImpacts = healthImpacts.ToArray();
        }

        /// <summary>
        /// Gets the emission health impacts, associated with upstream and tailpipe emissions of NOx, SO2, and PM2.5
        /// criteria pollutants, for the specified emission health impact and calendar year.
        /// </summary>
        public CriteriaEmissionsValues this[EmissionHealthImpact healthImpact, int year]
        {
            get => this.HealthImpacts[(int)healthImpact][year];
        }

        /// <summary>
        /// Specifies the emission health impacts, associated with upstream and tailpipe emissions of NOx, SO2, and
        /// PM2.5 criteria pollutants.
        /// This value is indexed as: EmissionHealthImpact (int), calendar year, criteria pollutant.
        /// </summary>
        private ValueByYear<CriteriaEmissionsValues>[] HealthImpacts { get; set; }

    }

    /// <summary>
    /// Provides emission data associated with various criteria emissions.
    /// </summary>
    [Serializable]
    public sealed class CriteriaEmissionsValue
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CriteriaEmissionsValue"/> class.
        /// </summary>
        public CriteriaEmissionsValue(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>Gets emissions data associated with the Nitrogen Oxides pollutant.</summary>
        public double NOX { get; private set; }
        /// <summary>Gets emissions data associated with the Sulfur Dioxide pollutant.</summary>
        public double SO2 { get; private set; }
        /// <summary>Gets emissions data associated with the Particulate Matter (diameter of ~2.5 micrometers) pollutant.</summary>
        public double PM { get; private set; }

    }

    /// <summary>
    /// Provides emission data associated with various criteria emissions by petroleum production categories and mobile
    /// sources.
    /// </summary>
    [Serializable]
    public sealed class CriteriaEmissionsValues
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CriteriaEmissionsValues"/> class.
        /// </summary>
        public CriteriaEmissionsValues(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>Gets the upstream emissions data for the petroleum refineries sector.</summary>
        public CriteriaEmissionsValue UpstreamRefineries { get; private set; }
        /// <summary>Gets the upstream emissions data for the petroleum extraction sector.</summary>
        public CriteriaEmissionsValue UpstreamExtraction { get; private set; }
        /// <summary>Gets the upstream emissions data for the petroleum transportation sector.</summary>
        public CriteriaEmissionsValue UpstreamTransportation { get; private set; }
        /// <summary>
        /// Gets the upstream emissions data for the fuel transportation, storage, and distribution sector.
        /// </summary>
        public CriteriaEmissionsValue UpstreamFuelTSD { get; private set; }
        /// <summary>Gets the upstream emissions data for the electricity generation sector.</summary>
        public CriteriaEmissionsValue UpstreamElectricity { get; private set; }

        /// <summary>
        /// Gets the downstream emissions data for the on-road light duty gasoline cars/motorcycles sector.
        /// </summary>
        public CriteriaEmissionsValue DownstreamGasLDV { get; private set; }
        /// <summary>Gets the downstream emissions data for the on-road light duty gasoline trucks sector.</summary>
        public CriteriaEmissionsValue DownstreamGasLDT { get; private set; }
        /// <summary>
        /// Gets the downstream emissions data for the on-road light duty diesel vehicles (cars and trucks) sector.
        /// </summary>
        public CriteriaEmissionsValue DownstreamDslLDx { get; private set; }
        /// <summary>Gets the downstream emissions data for the on-road class 2b/3 gasoline vehicles sector.</summary>
        public CriteriaEmissionsValue DownstreamGas2b3 { get; private set; }
        /// <summary>Gets the downstream emissions data for the on-road class 2b/3 diesel vehicles sector.</summary>
        public CriteriaEmissionsValue DownstreamDsl2b3 { get; private set; }

    }

    /// <summary>
    /// Provides a collection of upstream emissions, in grams/million BTU, for various fuel types (from all stages of
    /// fuel production and distribution) by calendar year.
    /// </summary>
    [Serializable]
    public sealed class UpstreamEmissions
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="UpstreamEmissions"/> class.
        /// </summary>
        public UpstreamEmissions(FTValue<ValueByYear<UpstreamEmissionsValue>> emissions)
        {
            this.Emissions = emissions;
        }

        /// <summary>Gets the upstream emissions for the specified fuel type and calendar year.</summary>
        public UpstreamEmissionsValue this[FuelType fuelType, int year] => this.Emissions[fuelType][year];

        /// <summary>
        /// Specifies the upstream emissions for all pollutants.
        /// This value is indexed as: FuelType, calendar year, pollutant.
        /// </summary>
        private FTValue<ValueByYear<UpstreamEmissionsValue>> Emissions { get; set; }

        /// <summary>
        /// Defines the default interpolation method to use for expanding sparse annual upstream emissions values.
        /// </summary>
        public static UpstreamEmissionsValue InterpolateEmissionsValue(
            int year, int[] years, UpstreamEmissionsValue[] values)
        {
            for (int i = 0; i < years.Length; i++)
            {
                if (years[i] == year) {
                    // found exact match
                    return values[i];
                }

                if (years[i] > year) {
                    // found approximate location
                    //  i=0: year is < min-year; return min value
                    //  i>0: return interpolated value between two closest year
                    if (i == 0) {
                        return values[i];
                    }

                    double y0 = years[i - 1];
                    double y1 = years[i];
                    double yrMult = (year - y0) / (y1 - y0);

                    var value = new UpstreamEmissionsValue();

                    foreach (var pollutant in Pollutants.List) {
                        var valueByCategory = value[pollutant];
                        var v0 = values[i - 1][pollutant];
                        var v1 = values[i][pollutant];

                        valueByCategory.Extraction     = yrMult * (v1.Extraction     - v0.Extraction    ) + v0.Extraction;
                        valueByCategory.Transportation = yrMult * (v1.Transportation - v0.Transportation) + v0.Transportation;
                        valueByCategory.Refining       = yrMult * (v1.Refining       - v0.Refining      ) + v0.Refining;
                        valueByCategory.FuelTSD        = yrMult * (v1.FuelTSD        - v0.FuelTSD       ) + v0.FuelTSD;
                        valueByCategory.Total          = yrMult * (v1.Total          - v0.Total         ) + v0.Total;
                    }

                    return value;
                }
            }

            // if exhausted all available years, that means desired year
            // is greater than max-year -- return last available value
            return values.Last();
        }

    }

    /// <summary>
    /// Provides tailpipe emissions, in grams/mile, for various vehicle classes by model year and age.
    /// </summary>
    [Serializable]
    public sealed class TailpipeEmissions
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TailpipeEmissions"/> class.
        /// </summary>
        public TailpipeEmissions(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>Gets the lowest model year for which tailpipe emissions data is available.</summary>
        public int MinMY { get; private set; }
        /// <summary>Gets the highest model year for which tailpipe emissions data is available.</summary>
        public int MaxMY { get; private set; }

        /// <summary>
        /// Gets the tailpipe emissions for the specified vehicle class, model year, and vehicle age.
        /// </summary>
        public EmissionsValue this[VehicleClass vehClass, int year, int age]
        {
            get {
                int index = Math.Max(Math.Min(year, this.MaxMY) - this.MinMY, 0);
                return this.Emissions[vehClass][index][age];
            }
        }

        /// <summary>
        /// Gets the tailpipe emissions for all pollutants, indexed as: vehClass, modelYear, age, pollutant.
        /// </summary>
        private VCValue<EmissionsValue[][]> Emissions { get; set; }

    }

    /// <summary>
    /// Provides brake and tire wear emission rates, in grams/mile, for various vehicle classes and fuel types.
    /// </summary>
    [Serializable]
    public sealed class BrakeTireWearEmissions
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="BrakeTireWearEmissions"/> class.
        /// </summary>
        public BrakeTireWearEmissions(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);
        }

        /// <summary>
        /// Gets the break and tire wear emission rates for the specified vehicle class and fuel type.
        /// </summary>
        public BtwRates this[int year, VehicleClass vehClass, FuelType fuelType] => year > 2010 
            ? this.Emissions2011[vehClass][fuelType] 
            : this.Emissions2010[vehClass][fuelType];

        /// <summary>
        /// Gets the break and tire wear emission rates for PM2.5 pollutant in model years 2010 and prior.
        /// </summary>
        private VCValue<FTValue<BtwRates>> Emissions2010 { get; set; }
        /// <summary>
        /// Gets the break and tire wear emission rates for PM2.5 pollutant in model years 2011 and after.
        /// </summary>
        private VCValue<FTValue<BtwRates>> Emissions2011 { get; set; }

        /// <summary>
        /// Defines the brake and tire wear emission rates, specified in grams/mile.
        /// </summary>
        [Serializable]
        public struct BtwRates
        {
            /// <summary>
            /// Initializes a new <see cref="BtwRates"/> instance, using the specified break wear and tire wear values.
            /// </summary>
            public BtwRates(double brakeWear, double tireWear)
            {
                this.BrakeWear = brakeWear;
                this.TireWear = tireWear;
            }

            /// <summary>Gets the emissions rates associated with brake wear, specified in grams/mile.</summary>
            public double BrakeWear { get; private set; }
            /// <summary>Gets the emissions rates associated with tire wear, specified in grams/mile.</summary>
            public double TireWear { get; private set; }

            /// <summary>
            /// Gets the combined emissions rates associated with brake and tire wear, specified in grams/mile.
            /// </summary>
            public double Total => this.BrakeWear + this.TireWear;

        }

    }

}
