#region << Using Directives >>
using System;
using System.Collections.Generic;
using Volpe.Cafe.Utils;
using Volpe.Cafe.Generic;
using VehClass = Volpe.Cafe.VehicleClass;
using RegClass = Volpe.Cafe.RegulatoryClass;
#endregion
namespace Volpe.Cafe.Data
{
    #region 
    [Serializable]
    public sealed class GeneralEffects : ICloneable
    {
        #region 
        GeneralEffects(GeneralEffects prototype)
        {
            this.Sales             = prototype.Sales            .Clone();   
            this.CurbWeight        = prototype.CurbWeight       .Clone();
            this.Footprint         = prototype.Footprint        .Clone();
            this.WorkFactor        = prototype.WorkFactor       .Clone();
            this.RatedFuelEconomy  = prototype.RatedFuelEconomy .Clone();   
            this.OnRoadFuelEconomy = prototype.OnRoadFuelEconomy.Clone();
            this.FuelShare         = prototype.FuelShare        .Clone();
            this.Fleet             = prototype.Fleet            .Clone();   
            this.VMT               = prototype.VMT              .Clone();
        }
        public GeneralEffects()
        {
            this.Sales             = new EffectsValue();    
            this.CurbWeight        = new EffectsValue();
            this.Footprint         = new EffectsValue();
            this.WorkFactor        = new EffectsValue();
            this.RatedFuelEconomy  = new EffectsValue();    
            this.OnRoadFuelEconomy = new EffectsValue();
            this.FuelShare         = new EffectsValue();
            this.Fleet             = new EffectsValue();    
            this.VMT               = new EffectsValue();
        }
        #endregion
        #region 
        #region 
        object ICloneable.Clone()
        {
            return this.Clone();
        }
        public GeneralEffects Clone()
        {
            return new GeneralEffects(this);
        }
        #endregion
        public void AppendValue(GeneralEffects value)
        {
            this.Sales            .AppendValue(value.Sales            );
            this.CurbWeight       .AppendValue(value.CurbWeight       );
            this.Footprint        .AppendValue(value.Footprint        );
            this.WorkFactor       .AppendValue(value.WorkFactor       );
            this.RatedFuelEconomy .AppendValue(value.RatedFuelEconomy );
            this.OnRoadFuelEconomy.AppendValue(value.OnRoadFuelEconomy);
            this.FuelShare        .AppendValue(value.FuelShare        );
            this.Fleet            .AppendValue(value.Fleet            );
            this.VMT              .AppendValue(value.VMT              );
        }
        public double GetSales(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.Sales.GetValue(vehClass, regClass, fuelType);
        }
        public double GetCurbWeight(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.CalcArithmeticMean(vehClass, regClass, fuelType, this.CurbWeight, this.Sales);
        }
        public double GetFootprint(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.CalcArithmeticMean(vehClass, regClass, fuelType, this.Footprint, this.Sales);
        }
        public double GetWorkFactor(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.CalcArithmeticMean(vehClass, regClass, fuelType, this.WorkFactor, this.Sales);
        }
        double CalcArithmeticMean(VehClass vehClass, RegClass regClass, FuelType fuelType, EffectsValue value, EffectsValue sales)
        {
            if (vehClass == VehClass.All && fuelType == FuelType.All)
            {
                double sum = 0;
                foreach (VehClass vc in VCValue<object>.Classes)
                {
                    double s = sales.GetValue(vc, RegClass.None, fuelType);
                    if (s != 0) { sum += value.GetValue(vc, RegClass.None).ArithmeticMean(sales.GetValue(vc, RegClass.None)) * s; }
                }
                return sum / sales.GetValue(VehClass.All, RegClass.None, FuelType.All);
            }
            else if (vehClass == VehClass.All)
            {
                double sum = 0;
                foreach (VehClass vc in VCValue<object>.Classes)
                {
                    double s = sales.GetValue(vc, RegClass.None, fuelType);
                    if (s != 0) { sum += value.GetValue(vc, RegClass.None, fuelType) * s; }
                }
                return sum / sales.GetValue(VehClass.All, RegClass.None, fuelType);
            }
            else if (regClass == RegClass.All && fuelType == FuelType.All)
            {
                double sum = 0;
                foreach (RegClass rc in RCValue<object>.Classes)
                {
                    double s = sales.GetValue(VehClass.None , rc, fuelType);
                    if (s != 0) { sum += value.GetValue(VehClass.None, rc).ArithmeticMean(sales.GetValue(VehClass.None, rc)) * s; }
                }
                return sum / sales.GetValue(VehClass.None, RegClass.All, FuelType.All);
            }
            else if (regClass == RegClass.All)
            {
                double sum = 0;
                foreach (RegClass rc in RCValue<object>.Classes)
                {
                    double s = sales.GetValue(VehClass.None, rc, fuelType);
                    if (s != 0) { sum += value.GetValue(VehClass.None, rc, fuelType) * s; }
                }
                return sum / sales.GetValue(VehClass.None, RegClass.All, fuelType);
            }
            else if (fuelType == FuelType.All)
            {
                return value.GetValue(vehClass, regClass).ArithmeticMean(sales.GetValue(vehClass, regClass));
            }
            else
            {
                return value.GetValue(vehClass, regClass, fuelType);
            }
        }
        public double GetRatedFE(VehClass vehClass, RegClass regClass, FuelType fuelType, EnergyConsumptionEffects energyEffects)
        {
            return this.CalcFuelEconomy(vehClass, regClass, fuelType, this.RatedFuelEconomy, energyEffects.RatedGallons);
        }
        public double GetOnRoadFE(VehClass vehClass, RegClass regClass, FuelType fuelType, EnergyConsumptionEffects energyEffects)
        {
            return this.CalcFuelEconomy(vehClass, regClass, fuelType, this.OnRoadFuelEconomy, energyEffects.Gallons);
        }
        double CalcFuelEconomy(VehClass vehClass, RegClass regClass, FuelType fuelType, EffectsValue value, EffectsValue gallons)
        {
            if (vehClass == VehClass.All || regClass == RegClass.All || fuelType == FuelType.All)
            {
                return this.VMT.GetValue(vehClass, regClass, fuelType) / gallons.GetValue(vehClass, regClass, fuelType);
            }
            return value.GetValue(vehClass, regClass, fuelType);
        }
        public double GetFuelShare(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            if (fuelType == FuelType.All) { return 1; }
            if (vehClass == VehClass.All || regClass == RegClass.All)
            {
                return this.VMT.GetValue(vehClass, regClass, fuelType) / this.VMT.GetValue(vehClass, regClass, FuelType.All);
            }
            return this.FuelShare.GetValue(vehClass, regClass, fuelType);
        }
        public double GetFleet(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.Fleet.GetValue(vehClass, regClass, fuelType);
        }
        public double GetVMT(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.VMT.GetValue(vehClass, regClass, fuelType);
        }
        #endregion
        #region 
        public EffectsValue Sales { get; private set; }
        public EffectsValue CurbWeight { get; private set; }
        public EffectsValue Footprint { get; private set; }
        public EffectsValue WorkFactor { get; private set; }
        public EffectsValue RatedFuelEconomy { get; private set; }
        public EffectsValue OnRoadFuelEconomy { get; private set; }
        public EffectsValue FuelShare { get; private set; }
        public EffectsValue Fleet { get; private set; }
        public EffectsValue VMT { get; private set; }
        #endregion
    }
    [Serializable]
    public sealed class EnergyConsumptionEffects : ICloneable
    {
        #region 
        EnergyConsumptionEffects(EnergyConsumptionEffects prototype)
        {
            this.BTU          = prototype.BTU         .Clone();
            this.Gallons      = prototype.Gallons     .Clone();
            this.RatedGallons = prototype.RatedGallons.Clone();
            this.NativeUnits  = prototype.NativeUnits .Clone();
        }
        public EnergyConsumptionEffects()
        {
            this.BTU          = new EffectsValue();
            this.Gallons      = new EffectsValue();
            this.RatedGallons = new EffectsValue();
            this.NativeUnits  = new EffectsValue();
        }
        #endregion
        #region 
        #region 
        object ICloneable.Clone()
        {
            return this.Clone();
        }
        public EnergyConsumptionEffects Clone()
        {
            return new EnergyConsumptionEffects(this);
        }
        #endregion
        public void AppendValue(EnergyConsumptionEffects value)
        {
            this.BTU         .AppendValue(value.BTU         );
            this.Gallons     .AppendValue(value.Gallons     );
            this.RatedGallons.AppendValue(value.RatedGallons);
            this.NativeUnits .AppendValue(value.NativeUnits );
        }
        public double GetBTU(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.BTU.GetValue(vehClass, regClass, fuelType);
        }
        public double GetGallons(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.Gallons.GetValue(vehClass, regClass, fuelType);
        }
        public double GetRatedGallons(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.RatedGallons.GetValue(vehClass, regClass, fuelType);
        }
        public double GetNativeUnits(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.NativeUnits.GetValue(vehClass, regClass, fuelType);
        }
        #endregion
        #region 
        public EffectsValue BTU { get; private set; }
        public EffectsValue Gallons { get; private set; }
        public EffectsValue RatedGallons { get; private set; }
        public EffectsValue NativeUnits { get; private set; }
        #endregion
    }
    [Serializable]
    public sealed class EmissionEffects : ICloneable
    {
        #region 
        EmissionEffects(EmissionEffects prototype)
        {
            this.UpstreamCO           = prototype.UpstreamCO          .Clone(); 
            this.UpstreamVOC          = prototype.UpstreamVOC         .Clone();
            this.UpstreamNOx          = prototype.UpstreamNOx         .Clone();
            this.UpstreamSO2          = prototype.UpstreamSO2         .Clone();
            this.UpstreamPM           = prototype.UpstreamPM          .Clone();
            this.UpstreamCO2          = prototype.UpstreamCO2         .Clone();
            this.UpstreamCH4          = prototype.UpstreamCH4         .Clone();
            this.UpstreamN2O          = prototype.UpstreamN2O         .Clone();
            this.UpstreamAcetaldehyde = prototype.UpstreamAcetaldehyde.Clone(); 
            this.UpstreamAcrolein     = prototype.UpstreamAcrolein    .Clone();
            this.UpstreamBenzene      = prototype.UpstreamBenzene     .Clone();
            this.UpstreamButadiene    = prototype.UpstreamButadiene   .Clone();
            this.UpstreamFormaldehyde = prototype.UpstreamFormaldehyde.Clone();
            this.UpstreamDPM10        = prototype.UpstreamDPM10       .Clone();
            this.UpstreamMTBE         = prototype.UpstreamMTBE        .Clone();
            this.TailpipeCO           = prototype.TailpipeCO          .Clone(); 
            this.TailpipeVOC          = prototype.TailpipeVOC         .Clone();
            this.TailpipeNOx          = prototype.TailpipeNOx         .Clone();
            this.TailpipeSO2          = prototype.TailpipeSO2         .Clone();
            this.TailpipePM           = prototype.TailpipePM          .Clone();
            this.TailpipeCO2          = prototype.TailpipeCO2         .Clone();
            this.TailpipeCH4          = prototype.TailpipeCH4         .Clone();
            this.TailpipeN2O          = prototype.TailpipeN2O         .Clone();
            this.TailpipeAcetaldehyde = prototype.TailpipeAcetaldehyde.Clone(); 
            this.TailpipeAcrolein     = prototype.TailpipeAcrolein    .Clone();
            this.TailpipeBenzene      = prototype.TailpipeBenzene     .Clone();
            this.TailpipeButadiene    = prototype.TailpipeButadiene   .Clone();
            this.TailpipeFormaldehyde = prototype.TailpipeFormaldehyde.Clone();
            this.TailpipeDPM10        = prototype.TailpipeDPM10       .Clone();
            this.TailpipeMTBE         = prototype.TailpipeMTBE        .Clone();
        }
        public EmissionEffects()
        {
            this.UpstreamCO           = new EffectsValue();     
            this.UpstreamVOC          = new EffectsValue();
            this.UpstreamNOx          = new EffectsValue();
            this.UpstreamSO2          = new EffectsValue();
            this.UpstreamPM           = new EffectsValue();
            this.UpstreamCO2          = new EffectsValue();
            this.UpstreamCH4          = new EffectsValue();
            this.UpstreamN2O          = new EffectsValue();
            this.UpstreamAcetaldehyde = new EffectsValue();     
            this.UpstreamAcrolein     = new EffectsValue();
            this.UpstreamBenzene      = new EffectsValue();
            this.UpstreamButadiene    = new EffectsValue();
            this.UpstreamFormaldehyde = new EffectsValue();
            this.UpstreamDPM10        = new EffectsValue();
            this.UpstreamMTBE         = new EffectsValue();
            this.TailpipeCO           = new EffectsValue();     
            this.TailpipeVOC          = new EffectsValue();
            this.TailpipeNOx          = new EffectsValue();
            this.TailpipeSO2          = new EffectsValue();
            this.TailpipePM           = new EffectsValue();
            this.TailpipeCO2          = new EffectsValue();
            this.TailpipeCH4          = new EffectsValue();
            this.TailpipeN2O          = new EffectsValue();
            this.TailpipeAcetaldehyde = new EffectsValue();     
            this.TailpipeAcrolein     = new EffectsValue();
            this.TailpipeBenzene      = new EffectsValue();
            this.TailpipeButadiene    = new EffectsValue();
            this.TailpipeFormaldehyde = new EffectsValue();
            this.TailpipeDPM10        = new EffectsValue();
            this.TailpipeMTBE         = new EffectsValue();
        }
        #endregion
        #region 
        #region 
        object ICloneable.Clone()
        {
            return this.Clone();
        }
        public EmissionEffects Clone()
        {
            return new EmissionEffects(this);
        }
        #endregion
        public void AppendValue(EmissionEffects value)
        {
            this.UpstreamCO          .AppendValue(value.UpstreamCO          );
            this.UpstreamVOC         .AppendValue(value.UpstreamVOC         );
            this.UpstreamNOx         .AppendValue(value.UpstreamNOx         );
            this.UpstreamSO2         .AppendValue(value.UpstreamSO2         );
            this.UpstreamPM          .AppendValue(value.UpstreamPM          );
            this.UpstreamCO2         .AppendValue(value.UpstreamCO2         );
            this.UpstreamCH4         .AppendValue(value.UpstreamCH4         );
            this.UpstreamN2O         .AppendValue(value.UpstreamN2O         );
            this.UpstreamAcetaldehyde.AppendValue(value.UpstreamAcetaldehyde);
            this.UpstreamAcrolein    .AppendValue(value.UpstreamAcrolein    );
            this.UpstreamBenzene     .AppendValue(value.UpstreamBenzene     );
            this.UpstreamButadiene   .AppendValue(value.UpstreamButadiene   );
            this.UpstreamFormaldehyde.AppendValue(value.UpstreamFormaldehyde);
            this.UpstreamDPM10       .AppendValue(value.UpstreamDPM10       );
            this.UpstreamMTBE        .AppendValue(value.UpstreamMTBE        );
            this.TailpipeCO          .AppendValue(value.TailpipeCO          );
            this.TailpipeVOC         .AppendValue(value.TailpipeVOC         );
            this.TailpipeNOx         .AppendValue(value.TailpipeNOx         );
            this.TailpipeSO2         .AppendValue(value.TailpipeSO2         );
            this.TailpipePM          .AppendValue(value.TailpipePM          );
            this.TailpipeCO2         .AppendValue(value.TailpipeCO2         );
            this.TailpipeCH4         .AppendValue(value.TailpipeCH4         );
            this.TailpipeN2O         .AppendValue(value.TailpipeN2O         );
            this.TailpipeAcetaldehyde.AppendValue(value.TailpipeAcetaldehyde);
            this.TailpipeAcrolein    .AppendValue(value.TailpipeAcrolein    );
            this.TailpipeBenzene     .AppendValue(value.TailpipeBenzene     );
            this.TailpipeButadiene   .AppendValue(value.TailpipeButadiene   );
            this.TailpipeFormaldehyde.AppendValue(value.TailpipeFormaldehyde);
            this.TailpipeDPM10       .AppendValue(value.TailpipeDPM10       );
            this.TailpipeMTBE        .AppendValue(value.TailpipeMTBE        );
        }
        public double GetUpstreamEmissions(VehClass vehClass, RegClass regClass, FuelType fuelType, Pollutant pollutant)
        {
            return this.GetUpstreamEmissions(pollutant).GetValue(vehClass, regClass, fuelType);
        }
        public double GetTailpipeEmissions(VehClass vehClass, RegClass regClass, FuelType fuelType, Pollutant pollutant)
        {
            return this.GetTailpipeEmissions(pollutant).GetValue(vehClass, regClass, fuelType);
        }
        public double GetTotalEmissions(VehClass vehClass, RegClass regClass, FuelType fuelType, Pollutant pollutant)
        {
            EffectsValue upstream, tailpipe;
            this.GetEmissions(pollutant, out upstream, out tailpipe);
            return upstream.GetValue(vehClass, regClass, fuelType) +
                   tailpipe.GetValue(vehClass, regClass, fuelType);
        }
        public double GetTotalEmissions(int age, VehClass vehClass, RegClass regClass, FuelType fuelType, Pollutant pollutant)
        {
            EffectsValue upstream, tailpipe;
            this.GetEmissions(pollutant, out upstream, out tailpipe);
            return upstream.GetAnnualValue(age, vehClass, regClass, fuelType) +
                   tailpipe.GetAnnualValue(age, vehClass, regClass, fuelType);
        }
        public EffectsValue GetUpstreamEmissions(Pollutant pollutant)
        {
            EffectsValue upstream, tailpipe;
            this.GetEmissions(pollutant, out upstream, out tailpipe);
            return upstream;
        }
        public EffectsValue GetTailpipeEmissions(Pollutant pollutant)
        {
            EffectsValue upstream, tailpipe;
            this.GetEmissions(pollutant, out upstream, out tailpipe);
            return tailpipe;
        }
        void GetEmissions(Pollutant pollutant, out EffectsValue upstream, out EffectsValue tailpipe)
        {
            switch (pollutant)
            {
                case Pollutant.CO          : upstream = UpstreamCO          ; tailpipe = TailpipeCO          ; break;
                case Pollutant.VOC         : upstream = UpstreamVOC         ; tailpipe = TailpipeVOC         ; break;
                case Pollutant.NOx         : upstream = UpstreamNOx         ; tailpipe = TailpipeNOx         ; break;
                case Pollutant.SO2         : upstream = UpstreamSO2         ; tailpipe = TailpipeSO2         ; break;
                case Pollutant.PM          : upstream = UpstreamPM          ; tailpipe = TailpipePM          ; break;
                case Pollutant.CO2         : upstream = UpstreamCO2         ; tailpipe = TailpipeCO2         ; break;
                case Pollutant.CH4         : upstream = UpstreamCH4         ; tailpipe = TailpipeCH4         ; break;
                case Pollutant.N2O         : upstream = UpstreamN2O         ; tailpipe = TailpipeN2O         ; break;
                case Pollutant.Acetaldehyde: upstream = UpstreamAcetaldehyde; tailpipe = TailpipeAcetaldehyde; break;
                case Pollutant.Acrolein    : upstream = UpstreamAcrolein    ; tailpipe = TailpipeAcrolein    ; break;
                case Pollutant.Benzene     : upstream = UpstreamBenzene     ; tailpipe = TailpipeBenzene     ; break;
                case Pollutant.Butadiene   : upstream = UpstreamButadiene   ; tailpipe = TailpipeButadiene   ; break;
                case Pollutant.Formaldehyde: upstream = UpstreamFormaldehyde; tailpipe = TailpipeFormaldehyde; break;
                case Pollutant.DPM10       : upstream = UpstreamDPM10       ; tailpipe = TailpipeDPM10       ; break;
                case Pollutant.MTBE        : upstream = UpstreamMTBE        ; tailpipe = TailpipeMTBE        ; break;
                default: throw new ArgumentOutOfRangeException("pollutant", "The specified pollutant is not supported.");
            }
        }
        #endregion
        #region 
        public EffectsValue UpstreamCO { get; private set; }
        public EffectsValue UpstreamVOC { get; private set; }
        public EffectsValue UpstreamNOx { get; private set; }
        public EffectsValue UpstreamSO2 { get; private set; }
        public EffectsValue UpstreamPM { get; private set; }
        public EffectsValue UpstreamCO2 { get; private set; }
        public EffectsValue UpstreamCH4 { get; private set; }
        public EffectsValue UpstreamN2O { get; private set; }
        public EffectsValue UpstreamAcetaldehyde { get; private set; }
        public EffectsValue UpstreamAcrolein { get; private set; }
        public EffectsValue UpstreamBenzene { get; private set; }
        public EffectsValue UpstreamButadiene { get; private set; }
        public EffectsValue UpstreamFormaldehyde { get; private set; }
        public EffectsValue UpstreamDPM10 { get; private set; }
        public EffectsValue UpstreamMTBE { get; private set; }
        public EffectsValue TailpipeCO { get; private set; }
        public EffectsValue TailpipeVOC { get; private set; }
        public EffectsValue TailpipeNOx { get; private set; }
        public EffectsValue TailpipeSO2 { get; private set; }
        public EffectsValue TailpipePM { get; private set; }
        public EffectsValue TailpipeCO2 { get; private set; }
        public EffectsValue TailpipeCH4 { get; private set; }
        public EffectsValue TailpipeN2O { get; private set; }
        public EffectsValue TailpipeAcetaldehyde { get; private set; }
        public EffectsValue TailpipeAcrolein { get; private set; }
        public EffectsValue TailpipeBenzene { get; private set; }
        public EffectsValue TailpipeButadiene { get; private set; }
        public EffectsValue TailpipeFormaldehyde { get; private set; }
        public EffectsValue TailpipeDPM10 { get; private set; }
        public EffectsValue TailpipeMTBE { get; private set; }
        #endregion
    }
    [Serializable]
    public sealed class SafetyEffects : ICloneable
    {
        #region 
        SafetyEffects(SafetyEffects prototype)
        {
            this.Fatalities = prototype.Fatalities.Clone();
        }
        public SafetyEffects()
        {
            this.Fatalities = new EffectsValue();
        }
        #endregion
        #region 
        #region 
        object ICloneable.Clone()
        {
            return this.Clone();
        }
        public SafetyEffects Clone()
        {
            return new SafetyEffects(this);
        }
        #endregion
        public void AppendValue(SafetyEffects value)
        {
            this.Fatalities.AppendValue(value.Fatalities);
        }
        public double GetFatalities(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.Fatalities.GetValue(vehClass, regClass, fuelType);
        }
        #endregion
        #region 
        public EffectsValue Fatalities { get; private set; }
        #endregion
    }
    [Serializable]
    public sealed class OwnerAndSocietalCosts : ICloneable
    {
        #region 
        OwnerAndSocietalCosts(OwnerAndSocietalCosts prototype)
        {
            this.DiscountRate       = prototype.DiscountRate;
            this.PreTaxFuelCosts    = prototype.PreTaxFuelCosts   .Clone();
            this.FuelTaxCosts       = prototype.FuelTaxCosts      .Clone();
            this.RetailFuelCosts    = prototype.RetailFuelCosts   .Clone();
            this.DriveSurplus       = prototype.DriveSurplus      .Clone();
            this.RefuelSurplus      = prototype.RefuelSurplus     .Clone();
            this.EconomicCosts      = prototype.EconomicCosts     .Clone();
            this.CongestionCosts    = prototype.CongestionCosts   .Clone();
            this.AccidentCosts      = prototype.AccidentCosts     .Clone();
            this.NoiseCosts         = prototype.NoiseCosts        .Clone();
            this.FatalityCosts      = prototype.FatalityCosts     .Clone();
            this.EmissionsCostsCO   = prototype.EmissionsCostsCO  .Clone();
            this.EmissionsCostsVOC  = prototype.EmissionsCostsVOC .Clone();
            this.EmissionsCostsNOx  = prototype.EmissionsCostsNOx .Clone();
            this.EmissionsCostsSO2  = prototype.EmissionsCostsSO2 .Clone();
            this.EmissionsCostsPM   = prototype.EmissionsCostsPM  .Clone();
            this.EmissionsCostsCO2  = prototype.EmissionsCostsCO2 .Clone();
            this.EmissionsCostsCH4  = prototype.EmissionsCostsCH4 .Clone();
            this.EmissionsCostsN2O  = prototype.EmissionsCostsN2O .Clone();
            this.TotalConsumerCosts = prototype.TotalConsumerCosts.Clone();
            this.TotalSocialCosts   = prototype.TotalSocialCosts  .Clone();
        }
        public OwnerAndSocietalCosts(VCValue<double> discRate)
        {
            this.DiscountRate = discRate;
            this.PreTaxFuelCosts    = new EffectsValue();
            this.FuelTaxCosts       = new EffectsValue();
            this.RetailFuelCosts    = new EffectsValue();
            this.DriveSurplus       = new EffectsValue();
            this.RefuelSurplus      = new EffectsValue();
            this.EconomicCosts      = new EffectsValue();
            this.CongestionCosts    = new EffectsValue();
            this.AccidentCosts      = new EffectsValue();
            this.NoiseCosts         = new EffectsValue();
            this.FatalityCosts      = new EffectsValue();
            this.EmissionsCostsCO   = new EffectsValue();
            this.EmissionsCostsVOC  = new EffectsValue();
            this.EmissionsCostsNOx  = new EffectsValue();
            this.EmissionsCostsSO2  = new EffectsValue();
            this.EmissionsCostsPM   = new EffectsValue();
            this.EmissionsCostsCO2  = new EffectsValue();
            this.EmissionsCostsCH4  = new EffectsValue();
            this.EmissionsCostsN2O  = new EffectsValue();
            this.TotalConsumerCosts = new EffectsValue();
            this.TotalSocialCosts   = new EffectsValue();
        }
        #endregion
        #region 
        #region 
        object ICloneable.Clone()
        {
            return this.Clone();
        }
        public OwnerAndSocietalCosts Clone()
        {
            return new OwnerAndSocietalCosts(this);
        }
        #endregion
        public void AppendValue(OwnerAndSocietalCosts value)
        {
            this.PreTaxFuelCosts   .AppendValue(value.PreTaxFuelCosts   );
            this.FuelTaxCosts      .AppendValue(value.FuelTaxCosts      );
            this.RetailFuelCosts   .AppendValue(value.RetailFuelCosts   );
            this.DriveSurplus      .AppendValue(value.DriveSurplus      );
            this.RefuelSurplus     .AppendValue(value.RefuelSurplus     );
            this.EconomicCosts     .AppendValue(value.EconomicCosts     );
            this.CongestionCosts   .AppendValue(value.CongestionCosts   );
            this.AccidentCosts     .AppendValue(value.AccidentCosts     );
            this.NoiseCosts        .AppendValue(value.NoiseCosts        );
            this.FatalityCosts     .AppendValue(value.FatalityCosts     );
            this.EmissionsCostsCO  .AppendValue(value.EmissionsCostsCO  );
            this.EmissionsCostsVOC .AppendValue(value.EmissionsCostsVOC );
            this.EmissionsCostsNOx .AppendValue(value.EmissionsCostsNOx );
            this.EmissionsCostsSO2 .AppendValue(value.EmissionsCostsSO2 );
            this.EmissionsCostsPM  .AppendValue(value.EmissionsCostsPM  );
            this.EmissionsCostsCO2 .AppendValue(value.EmissionsCostsCO2 );
            this.EmissionsCostsCH4 .AppendValue(value.EmissionsCostsCH4 );
            this.EmissionsCostsN2O .AppendValue(value.EmissionsCostsN2O );
            this.TotalConsumerCosts.AppendValue(value.TotalConsumerCosts);
            this.TotalSocialCosts  .AppendValue(value.TotalSocialCosts  );
        }
        public double GetDiscountRate(VehClass vehClass, RegClass regClass)
        {
            if (vehClass != VehClass.None && vehClass != VehicleClass.All) { return this.DiscountRate[vehClass]; }
            double dr = 0;
            foreach (VehicleClass vc in VCValue<object>.Classes)
            {
                dr += this.DiscountRate[vc];
            }
            return dr / VCValue<object>.Classes.Length;
        }
        public double GetPreTaxFuelCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.PreTaxFuelCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetFuelTaxCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.FuelTaxCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetRetailFuelCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.RetailFuelCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetDriveSurplus(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.DriveSurplus.GetValue(vehClass, regClass, fuelType);
        }
        public double GetRefuelSurplus(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.RefuelSurplus.GetValue(vehClass, regClass, fuelType);
        }
        public double GetEconomicCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.EconomicCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetCongestionCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.CongestionCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetAccidentCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.AccidentCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetNoiseCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.NoiseCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetFatalityCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.FatalityCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetEmissionsCosts(VehClass vehClass, RegClass regClass, FuelType fuelType, Pollutant pollutant)
        {
            return this.GetEmissionsCosts(pollutant).GetValue(vehClass, regClass, fuelType);
        }
        EffectsValue GetEmissionsCosts(Pollutant pollutant)
        {
            switch (pollutant)
            {
                case Pollutant.CO : return EmissionsCostsCO ;
                case Pollutant.VOC: return EmissionsCostsVOC;
                case Pollutant.NOx: return EmissionsCostsNOx;
                case Pollutant.SO2: return EmissionsCostsSO2;
                case Pollutant.PM : return EmissionsCostsPM ;
                case Pollutant.CO2: return EmissionsCostsCO2;
                case Pollutant.CH4: return EmissionsCostsCH4;
                case Pollutant.N2O: return EmissionsCostsN2O;
                default: throw new ArgumentOutOfRangeException("pollutant", "The specified pollutant is not supported.");
            }
        }
        public double GetTotalConsumerCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.TotalConsumerCosts.GetValue(vehClass, regClass, fuelType);
        }
        public double GetTotalSocialCosts(VehClass vehClass, RegClass regClass, FuelType fuelType)
        {
            return this.TotalSocialCosts.GetValue(vehClass, regClass, fuelType);
        }
        #endregion
        #region 
        public VCValue<double> DiscountRate { get; private set; }
        public EffectsValue PreTaxFuelCosts { get; private set; }
        public EffectsValue FuelTaxCosts { get; private set; }
        public EffectsValue RetailFuelCosts { get; private set; }
        public EffectsValue DriveSurplus { get; private set; }
        public EffectsValue RefuelSurplus { get; private set; }
        public EffectsValue EconomicCosts { get; private set; }
        public EffectsValue CongestionCosts { get; private set; }
        public EffectsValue AccidentCosts { get; private set; }
        public EffectsValue NoiseCosts { get; private set; }
        public EffectsValue FatalityCosts { get; private set; }
        public EffectsValue EmissionsCostsCO { get; private set; }
        public EffectsValue EmissionsCostsVOC { get; private set; }
        public EffectsValue EmissionsCostsNOx { get; private set; }
        public EffectsValue EmissionsCostsSO2 { get; private set; }
        public EffectsValue EmissionsCostsPM { get; private set; }
        public EffectsValue EmissionsCostsCO2 { get; private set; }
        public EffectsValue EmissionsCostsCH4 { get; private set; }
        public EffectsValue EmissionsCostsN2O { get; private set; }
        public EffectsValue TotalConsumerCosts { get; private set; }
        public EffectsValue TotalSocialCosts { get; private set; }
        #endregion
    }
    #endregion
    [Serializable]
    public sealed class EffectsData : ICloneable
    {
        #region 
        EffectsData() { }
        public EffectsData(int modelYear, bool initCosts, VCValue<double> discRate)
        {
            this.ModelYear       = modelYear;
            this.GeneralEffects  = new GeneralEffects();
            this.EnergyEffects   = new EnergyConsumptionEffects();
            this.EmissionEffects = new EmissionEffects();
            this.SafetyEffects   = new SafetyEffects();
            if (initCosts)
            {
                this.UndiscountedCosts = new OwnerAndSocietalCosts(new VCValue<double>(0, 0, 0, 0));
                this.DiscountedCosts   = new OwnerAndSocietalCosts(discRate);
            }
        }
        #endregion
        #region 
        #region 
        object ICloneable.Clone()
        {
            return this.Clone();
        }
        public EffectsData Clone()
        {
            EffectsData value = new EffectsData();
            value.ModelYear         = this.ModelYear;
            value.GeneralEffects    = this.GeneralEffects   .Clone();
            value.EnergyEffects     = this.EnergyEffects    .Clone();
            value.EmissionEffects   = this.EmissionEffects  .Clone();
            value.SafetyEffects     = this.SafetyEffects    .Clone();
            value.UndiscountedCosts = this.UndiscountedCosts.Clone();
            value.DiscountedCosts   = this.DiscountedCosts  .Clone();
            return value;
        }
        #endregion
        #endregion
        #region 
        public int ModelYear { get; private set; }
        public GeneralEffects GeneralEffects { get; private set; }
        public EnergyConsumptionEffects EnergyEffects { get; private set; }
        public EmissionEffects EmissionEffects { get; private set; }
        public SafetyEffects SafetyEffects { get; private set; }
        public OwnerAndSocietalCosts UndiscountedCosts { get; private set; }
        public OwnerAndSocietalCosts DiscountedCosts { get; private set; }
        #endregion
    }
}

