#region << Using Directives >>
using System;
using System.Text;
using System.Collections.Generic;
using Volpe.Cafe.Data;
using Volpe.Cafe.IO;
using Volpe.Cafe.Settings;
using Volpe.Cafe.Utils;
using RC = Volpe.Cafe.RegulatoryClass;
using TI = Volpe.Cafe.TechnologyIndexes;
using Volpe.Cafe.Generic;
#endregion
namespace Volpe.Cafe.MonteCarlo.Data
{
    [Serializable]
    public class TrialData : ICloneable
    {
        #region 
        TrialData() { }
        public TrialData(TrialInfo info)
        {
            this.TrialInfo       = info;
            this._isBaseline     = false;
            this.HasData         = false;
            this.Sales           = null;
            this.Standard        = null;
            this.CAFE            = null;
            this.Footprint       = null;
            this.CurbWeight      = null;
            this.WorkFactor      = null;
            this.AvgTechCosts    = null;
            this.AvgIncrCosts    = null;
            this.AvgPayback      = null;
            this.TechCosts       = null;
            this.DiscCosts       = null;
            this.Fines           = null;
            this.TotalTechCosts  = null;
            this.VMT             = null;
            this.FuelConsumption = null;
            this.CO2Emissions    = null;
            this.Fatalities      = null;
            this.PreTaxFuelCosts = null;
            this.SocialCosts     = null;
            this.NetBenefits     = null;
            this.TechUseLGDI     = null;
            this.TechUseISG      = null;
            this.TechUseSHEV     = null;
            this.TechUseROLL1    = null;
            this.TechUseMR2      = null;
        }
        #endregion
        #region 
        #region 
        object ICloneable.Clone()
        {
            return this.Clone();
        }
        public TrialData Clone()
        {
            TrialData td = new TrialData();
            td._isBaseline = this._isBaseline;
            td.HasData     = this.HasData;
            td.TrialInfo   = this.TrialInfo.Clone();
            td.Sales           = (RCDouble[])Interaction.CloneArray(this.Sales          , typeof(RCDouble));
            td.Standard        = (RCDouble[])Interaction.CloneArray(this.Standard       , typeof(RCDouble));
            td.CAFE            = (RCDouble[])Interaction.CloneArray(this.CAFE           , typeof(RCDouble));
            td.Footprint       = (RCDouble[])Interaction.CloneArray(this.Footprint      , typeof(RCDouble));
            td.CurbWeight      = (RCDouble[])Interaction.CloneArray(this.CurbWeight     , typeof(RCDouble));
            td.WorkFactor      = (RCDouble[])Interaction.CloneArray(this.WorkFactor     , typeof(RCDouble));
            td.AvgTechCosts    = (RCDouble[])Interaction.CloneArray(this.AvgTechCosts   , typeof(RCDouble));
            td.AvgIncrCosts    = (RCDouble[])Interaction.CloneArray(this.AvgIncrCosts   , typeof(RCDouble));
            td.AvgPayback      = (RCDouble[])Interaction.CloneArray(this.AvgPayback     , typeof(RCDouble));
            td.TechCosts       = (RCDouble[])Interaction.CloneArray(this.TechCosts      , typeof(RCDouble));
            td.DiscCosts       = (RCDouble[])Interaction.CloneArray(this.DiscCosts      , typeof(RCDouble));
            td.Fines           = (RCDouble[])Interaction.CloneArray(this.Fines          , typeof(RCDouble));
            td.TotalTechCosts  = (RCDouble[])Interaction.CloneArray(this.TotalTechCosts , typeof(RCDouble));
            td.VMT             = (RCDouble[])Interaction.CloneArray(this.VMT            , typeof(RCDouble));
            td.FuelConsumption = (RCDouble[])Interaction.CloneArray(this.FuelConsumption, typeof(RCDouble));
            td.CO2Emissions    = (RCDouble[])Interaction.CloneArray(this.CO2Emissions   , typeof(RCDouble));
            td.Fatalities      = (RCDouble[])Interaction.CloneArray(this.Fatalities     , typeof(RCDouble));
            td.PreTaxFuelCosts = (RCDouble[])Interaction.CloneArray(this.PreTaxFuelCosts, typeof(RCDouble));
            td.SocialCosts     = (RCDouble[])Interaction.CloneArray(this.SocialCosts    , typeof(RCDouble));
            td.NetBenefits     = (RCDouble[])Interaction.CloneArray(this.NetBenefits    , typeof(RCDouble));
            td.TechUseLGDI     = (RCDouble[])Interaction.CloneArray(this.TechUseLGDI    , typeof(RCDouble));
            td.TechUseISG      = (RCDouble[])Interaction.CloneArray(this.TechUseISG     , typeof(RCDouble));
            td.TechUseSHEV     = (RCDouble[])Interaction.CloneArray(this.TechUseSHEV    , typeof(RCDouble));
            td.TechUseROLL1    = (RCDouble[])Interaction.CloneArray(this.TechUseROLL1   , typeof(RCDouble));
            td.TechUseMR2      = (RCDouble[])Interaction.CloneArray(this.TechUseMR2     , typeof(RCDouble));
            return td;
        }
        #endregion
        public void SaveTrialData(Scenario scen, int minYearIndex, int yearCount, TrialData baseTd, LogWriter writer,
            Industry[] modelData, EffectsData[] effectsData, ModelingSettings settings)
        {
            this._isBaseline     = scen.IsBaseline;
            this.Sales           = new RCDouble[yearCount];
            this.Standard        = new RCDouble[yearCount];
            this.CAFE            = new RCDouble[yearCount];
            this.Footprint       = new RCDouble[yearCount];
            this.CurbWeight      = new RCDouble[yearCount];
            this.WorkFactor      = new RCDouble[yearCount];
            this.AvgTechCosts    = new RCDouble[yearCount]; 
            this.AvgIncrCosts    = new RCDouble[yearCount];
            this.AvgPayback      = new RCDouble[yearCount];
            this.TechCosts       = new RCDouble[yearCount];
            this.DiscCosts       = new RCDouble[yearCount];
            this.Fines           = new RCDouble[yearCount];
            this.TotalTechCosts  = new RCDouble[yearCount];
            this.VMT             = new RCDouble[yearCount]; 
            this.FuelConsumption = new RCDouble[yearCount];
            this.CO2Emissions    = new RCDouble[yearCount];
            this.Fatalities      = new RCDouble[yearCount];
            this.PreTaxFuelCosts = new RCDouble[yearCount];
            this.SocialCosts     = new RCDouble[yearCount];
            this.NetBenefits     = new RCDouble[yearCount];
            this.TechUseLGDI     = new RCDouble[yearCount]; 
            this.TechUseISG      = new RCDouble[yearCount];
            this.TechUseSHEV     = new RCDouble[yearCount];
            this.TechUseROLL1    = new RCDouble[yearCount];
            this.TechUseMR2      = new RCDouble[yearCount];
            for (int j = 0; j < yearCount; j++)
            {
                this.Sales          [j] = new RCDouble();
                this.Standard       [j] = new RCDouble();
                this.CAFE           [j] = new RCDouble();
                this.Footprint      [j] = new RCDouble();
                this.CurbWeight     [j] = new RCDouble();
                this.WorkFactor     [j] = new RCDouble();
                this.AvgTechCosts   [j] = new RCDouble();  
                this.AvgIncrCosts   [j] = new RCDouble();
                this.AvgPayback     [j] = new RCDouble();
                this.TechCosts      [j] = new RCDouble();
                this.DiscCosts      [j] = new RCDouble();
                this.Fines          [j] = new RCDouble();
                this.TotalTechCosts [j] = new RCDouble();
                this.VMT            [j] = new RCDouble();  
                this.FuelConsumption[j] = new RCDouble();
                this.CO2Emissions   [j] = new RCDouble();
                this.Fatalities     [j] = new RCDouble();
                this.PreTaxFuelCosts[j] = new RCDouble();
                this.SocialCosts    [j] = new RCDouble();
                this.NetBenefits    [j] = new RCDouble();
                this.TechUseLGDI    [j] = new RCDouble();  
                this.TechUseISG     [j] = new RCDouble();
                this.TechUseSHEV    [j] = new RCDouble();
                this.TechUseROLL1   [j] = new RCDouble();
                this.TechUseMR2     [j] = new RCDouble();
                ModelYear year = ModelYear.NewModelYearFromIndex(minYearIndex + j);
                Industry ind = modelData[minYearIndex + j];
                RCDouble pbSales = new RCDouble();
                for (int k = 0, mfrCount = ind.ManufacturerCount; k < mfrCount; k++)
                {
                    Manufacturer            mfr = ind.Manufacturers[k];
                    Manufacturer.CModelData mmd = mfr.ModelData;
                    this.Sales         [j] += mmd.Sales;
                    this.Standard      [j] += this.SafeDivide(mmd.Sales, mmd.Standard);
                    this.CAFE          [j] += this.SafeDivide(mmd.Sales, mmd.CAFE    );
                    this.Footprint     [j] += mmd.Sales * mfr.CalcAverageFootprint (scen, year);
                    this.CurbWeight    [j] += mmd.Sales * mfr.CalcAverageCurbWeight(scen, year);
                    this.WorkFactor    [j] += mmd.Sales * mfr.CalcAverageWorkFactor(scen, year);
                    this.AvgTechCosts  [j] += mmd.TechCost;
                    this.TechCosts     [j] += mmd.TechCost         / 1e6; 
                    this.DiscCosts     [j] += mmd.DiscCost         / 1e6;
                    this.Fines         [j] += mmd.Fines            / 1e6;
                    this.TotalTechCosts[j] += mmd.TotalSocialCosts / 1e6;
                    foreach (Vehicle veh in mfr.Vehicles)
                    {
                        Vehicle.CModelData vmd      = veh.ModelData;
                        RegulatoryClass    regClass = veh.RegClass;
                        double             vehSales = veh.Description.Sales[year.Index];
                        if (vmd.TechUsed[TI.LGDI ] && !vmd.TechSuperseded[TI.LGDI ]) { this.TechUseLGDI [j][regClass] += vehSales; }
                        if (vmd.TechUsed[TI.ISG  ] && !vmd.TechSuperseded[TI.ISG  ]) { this.TechUseISG  [j][regClass] += vehSales; }
                        if (vmd.TechUsed[TI.SHEV1] && !vmd.TechSuperseded[TI.SHEV1]) { this.TechUseSHEV [j][regClass] += vehSales; }
                        if (vmd.TechUsed[TI.ROLL1] && !vmd.TechSuperseded[TI.ROLL1]) { this.TechUseROLL1[j][regClass] += vehSales; }
                        if (vmd.TechUsed[TI.MR2  ] && !vmd.TechSuperseded[TI.MR2  ]) { this.TechUseMR2  [j][regClass] += vehSales; }
                        if (vmd.TechCost > 0)
                        {
                            double payback = this.CalcPayback(veh, year, settings);
                            if (!double.IsNaN(payback))
                            {
                                this.AvgPayback[j][regClass] += (vehSales * payback);
                                pbSales           [regClass] +=  vehSales;
                            }
                        }
                    }
                }
                foreach (RegulatoryClass regClass in RCDouble.Classes)
                {
                    if (this.Standard[j][regClass] != 0) { this.Standard[j][regClass] = this.Sales[j][regClass] / this.Standard[j][regClass]; }
                    if (this.CAFE    [j][regClass] != 0) { this.CAFE    [j][regClass] = this.Sales[j][regClass] / this.CAFE    [j][regClass]; }
                    if (this.Sales   [j][regClass] != 0)
                    {
                        this.Footprint   [j][regClass] /= this.Sales[j][regClass];
                        this.CurbWeight  [j][regClass] /= this.Sales[j][regClass];
                        this.WorkFactor  [j][regClass] /= this.Sales[j][regClass];
                        this.AvgTechCosts[j][regClass] /= this.Sales[j][regClass];
                        this.AvgIncrCosts[j][regClass]  = 0;
                        this.TechUseLGDI [j][regClass] /= this.Sales[j][regClass];
                        this.TechUseISG  [j][regClass] /= this.Sales[j][regClass];
                        this.TechUseSHEV [j][regClass] /= this.Sales[j][regClass];
                        this.TechUseROLL1[j][regClass] /= this.Sales[j][regClass];
                        this.TechUseMR2  [j][regClass] /= this.Sales[j][regClass];
                    }
                    if (pbSales[regClass] != 0)
                    {
                        this.AvgPayback[j][regClass] /= pbSales[regClass];
                    }
                }
                EffectsData ied = effectsData[j];
                foreach (RegulatoryClass regClass in RCDouble.Classes)
                {
                    this.VMT            [j][regClass] = ied.GeneralEffects .GetVMT             (VehicleClass.None, regClass, FuelType.All) / 1e6;
                    this.FuelConsumption[j][regClass] = ied.EnergyEffects  .GetGallons         (VehicleClass.None, regClass, FuelType.All) / 1e6;
                    this.CO2Emissions   [j][regClass] = ied.EmissionEffects.GetTotalEmissions  (VehicleClass.None, regClass, FuelType.All, Pollutant.CO2) / 1e6; 
                    this.Fatalities     [j][regClass] = ied.SafetyEffects  .GetFatalities      (VehicleClass.None, regClass, FuelType.All);
                    this.PreTaxFuelCosts[j][regClass] = ied.DiscountedCosts.GetPreTaxFuelCosts (VehicleClass.None, regClass, FuelType.All) / 1e3; 
                    this.SocialCosts    [j][regClass] = ied.DiscountedCosts.GetTotalSocialCosts(VehicleClass.None, regClass, FuelType.All) / 1e3; 
                    this.NetBenefits    [j][regClass] = 0;
                }
                if (!this._isBaseline)
                {
                    this.AvgIncrCosts   [j] =  this.AvgTechCosts   [j] - baseTd.AvgTechCosts   [j];
                    this.TechCosts      [j] =  this.TechCosts      [j] - baseTd.TechCosts      [j];
                    this.DiscCosts      [j] =  this.DiscCosts      [j] - baseTd.DiscCosts      [j];
                    this.Fines          [j] =  this.Fines          [j] - baseTd.Fines          [j];
                    this.TotalTechCosts [j] =  this.TotalTechCosts [j] - baseTd.TotalTechCosts [j];
                    this.VMT            [j] =  this.VMT            [j] - baseTd.VMT            [j];
                    this.FuelConsumption[j] =  this.FuelConsumption[j] - baseTd.FuelConsumption[j];
                    this.CO2Emissions   [j] =  this.CO2Emissions   [j] - baseTd.CO2Emissions   [j];
                    this.Fatalities     [j] =  this.Fatalities     [j] - baseTd.Fatalities     [j];
                    this.PreTaxFuelCosts[j] =  this.PreTaxFuelCosts[j] - baseTd.PreTaxFuelCosts[j];
                    this.SocialCosts    [j] =  this.SocialCosts    [j] - baseTd.SocialCosts    [j];
                    this.NetBenefits    [j] = -this.SocialCosts    [j] - this  .TotalTechCosts [j];
                }
            }
            this.HasData = true;
        }
        double CalcPayback(Vehicle veh, ModelYear year, ModelingSettings settings)
        {
            double techCost    = veh.ModelData.TechCost;
            double fuelSavings = 0;
            int    payback     = 0;
            while (techCost > 0)
            {
                double bFC = CalcFuelCost(veh, true , year, payback, settings);
                double nFC = CalcFuelCost(veh, false, year, payback, settings);
                fuelSavings = bFC - nFC;
                techCost -= fuelSavings;
                payback++;
                if (payback == 20)
                {
                    techCost = 0;
                    break;
                }
            }
            if (techCost < 0)
            {
                return (double)payback + (techCost / fuelSavings);
            }
            return (double)payback;
        }
        double CalcFuelCost(Vehicle veh, bool baseFuelCost, ModelYear year, int age, ModelingSettings settings)
        {
            Parameters    parameters      = settings.Parameters;
            Estimates     priceEstimates  = settings.OperatingModes.FuelPriceEstimates;
            FuelPrices    fuelPrices      = parameters.FuelPrices;
            FuelValue     gap             = parameters.EconomicValues.OnRoadGap[veh.VehicleClass];
            FuelValue     ed              = parameters.FuelProperties.EnergyDensity;
            VMTGrowthRate vmtGrowthRate   = parameters.EconomicValues.VMTGrowthRate[veh.VehicleClass];
            double        vmtGrowthFactor = vmtGrowthRate.GetVMTGrowthFactor(priceEstimates, year.Year, age);
            double        vmt             = parameters.MilesDriven.GetVehicleAgeData(veh)[age] * vmtGrowthFactor;
            FuelValue     fe              = (baseFuelCost) ? veh.Description.BaseFE : veh.Description.FuelEconomy;
            FuelValue     fs              = veh.Description.FuelShare;
            double fuelCost = 0;
            foreach (FuelType fuel in FTValue<object>.Classes)
            {
                if (fs[fuel] == 0) { continue; }
                double fuelPrice = fuelPrices.GetFuelPrice(priceEstimates, fuel, year.Year);
                if      (fuel == FuelType.Electricity) { fuelPrice *= (ed.Gasoline / ed.Electricity); }
                else if (fuel == FuelType.Hydrogen   ) { fuelPrice *= (ed.Gasoline / ed.Hydrogen   ); }
                else if (fuel == FuelType.CNG        ) { fuelPrice *= (ed.Gasoline / ed.CNG        ); }
                fuelCost += fuelPrice * vmt / (fe[fuel] * (1 - gap[fuel])) * fs[fuel];
            }
            return fuelCost;
        }
        RCDouble SafeDivide(RCDouble numerator, RCDouble denominator)
        {
            RCDouble result = new RCDouble();
            for (int i = 0; i < RCDouble.Classes.Length; i++)
            {
                if (denominator.Items[i] != 0)
                {
                    result.Items[i] = numerator.Items[i] / denominator.Items[i];
                }
            }
            return result;
        }
        #endregion
        #region 
        bool _isBaseline;
        public bool HasData;
        public TrialInfo TrialInfo;
        public RCDouble[] Sales;
        public RCDouble[] Standard;
        public RCDouble[] CAFE;
        public RCDouble[] Footprint;
        public RCDouble[] CurbWeight;
        public RCDouble[] WorkFactor;
        public RCDouble[] AvgTechCosts;
        public RCDouble[] AvgIncrCosts;
        public RCDouble[] AvgPayback;
        public RCDouble[] TechCosts;
        public RCDouble[] DiscCosts;
        public RCDouble[] Fines;
        public RCDouble[] TotalTechCosts;
        public RCDouble[] VMT;
        public RCDouble[] FuelConsumption;
        public RCDouble[] CO2Emissions;
        public RCDouble[] Fatalities;
        public RCDouble[] PreTaxFuelCosts;
        public RCDouble[] SocialCosts;
        public RCDouble[] NetBenefits;
        public RCDouble[] TechUseLGDI;
        public RCDouble[] TechUseISG;
        public RCDouble[] TechUseSHEV;
        public RCDouble[] TechUseROLL1;
        public RCDouble[] TechUseMR2;
        #endregion
    }
}

