#region << Using Directives >>
using System;
using Volpe.Cafe.Data;
using Volpe.Cafe.Settings;
using Volpe.Cafe.Utils;
using Volpe.Cafe.Model;
using Volpe.Cafe.Generic;
#endregion
namespace Volpe.Cafe.IO.Reporting.CSV
{
    [Serializable]
    public sealed class CsvSocietalEffectsByVehClassReport : CsvSocietalEffectsReport
    {
        #region 
        public CsvSocietalEffectsByVehClassReport(string path, bool append) : base(path, append, 1) { }
        #endregion
        #region 
        protected override string GetHeader() { return Header; }
        #endregion
        #region 
        public override string ReportName { get { return CsvReportName; } }
        #endregion
        #region 
        const string CsvReportName = "Societal Effects by Vehicle Class CSV Report";
        const string Header =
            "Scenario,Scenario Name,Model Year,Veh-Class,Fuel Type,Rated FE,On-road FE,Fuel Share," +
            "Curb Weight,Footprint,Work Factor,Sales,kVMT,Quads,kGallons,kUnits,Fatalities," +
            "CO (t),VOC (t),NOx (t),SO2 (t),PM (t),CO2 (mmt),CH4 (t),N2O (t)," +
            "Acetaldehyde (t),Acrolein (t),Benzene (t),Butadiene (t),Formaldehyde (t),DPM10 (t),MTBE (t)";
        #endregion
    }
    [Serializable]
    public sealed class CsvSocietalEffectsByRegClassReport : CsvSocietalEffectsReport
    {
        #region 
        public CsvSocietalEffectsByRegClassReport(string path, bool append) : base(path, append, 2) { }
        #endregion
        #region 
        protected override string GetHeader() { return Header; }
        #endregion
        #region 
        public override string ReportName { get { return CsvReportName; } }
        #endregion
        #region 
        const string CsvReportName = "Societal Effects by Regulatory Class CSV Report";
        const string Header =
            "Scenario,Scenario Name,Model Year,Reg-Class,Fuel Type,Rated FE,On-road FE,Fuel Share," +
            "Curb Weight,Footprint,Work Factor,Sales,kVMT,Quads,kGallons,kUnits,Fatalities," +
            "CO (t),VOC (t),NOx (t),SO2 (t),PM (t),CO2 (mmt),CH4 (t),N2O (t)," +
            "Acetaldehyde (t),Acrolein (t),Benzene (t),Butadiene (t),Formaldehyde (t),DPM10 (t),MTBE (t)";
        #endregion
    }
    [Serializable]
    public abstract class CsvSocietalEffectsReport : CsvReportingBase
    {
        #region 
        static CsvSocietalEffectsReport()
        {
            int vcCount = VCValue<object>.Classes.Length;
            VehClasses = new VehicleClass[vcCount + 1];
            Array.Copy(VCValue<object>.Classes, VehClasses, vcCount);
            VehClasses[vcCount] = VehicleClass.All;
            int rcCount = RCValue<object>.Classes.Length;
            RegClasses = new RegulatoryClass[rcCount + 1];
            Array.Copy(RCValue<object>.Classes, RegClasses, rcCount);
            RegClasses[rcCount] = RegulatoryClass.All;
            int fuelCount = FTValue<object>.Classes.Length;
            FuelTypes = new FuelType[fuelCount + 1];
            Array.Copy(FTValue<object>.Classes, FuelTypes, fuelCount);
            FuelTypes[fuelCount] = FuelType.All;
        }
        public CsvSocietalEffectsReport(string path, bool append, int mode) : base(path, append)
        {
            this._mode = mode;
        }
        #endregion
        #region 
        protected override void ParseData_Internal(Scenario scen, ModelYear year, ICompliance compliance)
        {
            EffectsData scenED = compliance.GetEffectsData(scen, year.Year);
            EffectsData baseED = (scen.IsBaseline) ? null : compliance.GetEffectsData(compliance.Settings.Scenarios[0], year.Year);
            this.WriteEffectsData(scen, year.Year, scenED, baseED);
            if (year.Year == compliance.MinYear)
            {
                this._scenEDSum = new EffectsData(-1, false, null);
                if (!scen.IsBaseline) { this._baseEDSum = new EffectsData(-1, false, null); }
            }
            this.AggregateEffectsData(scenED, this._scenEDSum);
            if (!scen.IsBaseline) { this.AggregateEffectsData(baseED, this._baseEDSum); }
            if (year.Year == compliance.MaxYear)
            {
                this.WriteEffectsData(scen, -1, this._scenEDSum, this._baseEDSum);
            }
        }
        void AggregateEffectsData(EffectsData ed, EffectsData edSum)
        {
            edSum.GeneralEffects .Sales               .AppendValue(ed.GeneralEffects .Sales               );
            edSum.GeneralEffects .Fleet               .AppendValue(ed.GeneralEffects .Fleet               );
            edSum.GeneralEffects .VMT                 .AppendValue(ed.GeneralEffects .VMT                 );
            edSum.EnergyEffects  .BTU                 .AppendValue(ed.EnergyEffects  .BTU                 );
            edSum.EnergyEffects  .Gallons             .AppendValue(ed.EnergyEffects  .Gallons             );
            edSum.EnergyEffects  .RatedGallons        .AppendValue(ed.EnergyEffects  .RatedGallons        );
            edSum.EnergyEffects  .NativeUnits         .AppendValue(ed.EnergyEffects  .NativeUnits         );
            edSum.SafetyEffects  .Fatalities          .AppendValue(ed.SafetyEffects  .Fatalities          );
            edSum.EmissionEffects.UpstreamCO          .AppendValue(ed.EmissionEffects.UpstreamCO          );
            edSum.EmissionEffects.UpstreamVOC         .AppendValue(ed.EmissionEffects.UpstreamVOC         );
            edSum.EmissionEffects.UpstreamNOx         .AppendValue(ed.EmissionEffects.UpstreamNOx         );
            edSum.EmissionEffects.UpstreamSO2         .AppendValue(ed.EmissionEffects.UpstreamSO2         );
            edSum.EmissionEffects.UpstreamPM          .AppendValue(ed.EmissionEffects.UpstreamPM          );
            edSum.EmissionEffects.UpstreamCO2         .AppendValue(ed.EmissionEffects.UpstreamCO2         );
            edSum.EmissionEffects.UpstreamCH4         .AppendValue(ed.EmissionEffects.UpstreamCH4         );
            edSum.EmissionEffects.UpstreamN2O         .AppendValue(ed.EmissionEffects.UpstreamN2O         );
            edSum.EmissionEffects.UpstreamAcetaldehyde.AppendValue(ed.EmissionEffects.UpstreamAcetaldehyde);
            edSum.EmissionEffects.UpstreamAcrolein    .AppendValue(ed.EmissionEffects.UpstreamAcrolein    );
            edSum.EmissionEffects.UpstreamBenzene     .AppendValue(ed.EmissionEffects.UpstreamBenzene     );
            edSum.EmissionEffects.UpstreamButadiene   .AppendValue(ed.EmissionEffects.UpstreamButadiene   );
            edSum.EmissionEffects.UpstreamFormaldehyde.AppendValue(ed.EmissionEffects.UpstreamFormaldehyde);
            edSum.EmissionEffects.UpstreamDPM10       .AppendValue(ed.EmissionEffects.UpstreamDPM10       );
            edSum.EmissionEffects.UpstreamMTBE        .AppendValue(ed.EmissionEffects.UpstreamMTBE        );
            edSum.EmissionEffects.TailpipeCO          .AppendValue(ed.EmissionEffects.TailpipeCO          );
            edSum.EmissionEffects.TailpipeVOC         .AppendValue(ed.EmissionEffects.TailpipeVOC         );
            edSum.EmissionEffects.TailpipeNOx         .AppendValue(ed.EmissionEffects.TailpipeNOx         );
            edSum.EmissionEffects.TailpipeSO2         .AppendValue(ed.EmissionEffects.TailpipeSO2         );
            edSum.EmissionEffects.TailpipePM          .AppendValue(ed.EmissionEffects.TailpipePM          );
            edSum.EmissionEffects.TailpipeCO2         .AppendValue(ed.EmissionEffects.TailpipeCO2         );
            edSum.EmissionEffects.TailpipeCH4         .AppendValue(ed.EmissionEffects.TailpipeCH4         );
            edSum.EmissionEffects.TailpipeN2O         .AppendValue(ed.EmissionEffects.TailpipeN2O         );
            edSum.EmissionEffects.TailpipeAcetaldehyde.AppendValue(ed.EmissionEffects.TailpipeAcetaldehyde);
            edSum.EmissionEffects.TailpipeAcrolein    .AppendValue(ed.EmissionEffects.TailpipeAcrolein    );
            edSum.EmissionEffects.TailpipeBenzene     .AppendValue(ed.EmissionEffects.TailpipeBenzene     );
            edSum.EmissionEffects.TailpipeButadiene   .AppendValue(ed.EmissionEffects.TailpipeButadiene   );
            edSum.EmissionEffects.TailpipeFormaldehyde.AppendValue(ed.EmissionEffects.TailpipeFormaldehyde);
            edSum.EmissionEffects.TailpipeDPM10       .AppendValue(ed.EmissionEffects.TailpipeDPM10       );
            edSum.EmissionEffects.TailpipeMTBE        .AppendValue(ed.EmissionEffects.TailpipeMTBE        );
        }
        void WriteEffectsData(Scenario scen, int year, EffectsData scenED, EffectsData baseED)
        {
            if (this._mode == 1)
            {
                int vcCount = 0;
                foreach (VehicleClass vehClass in VehClasses)
                {
                    this.WriteEffectsData(scen, year, vehClass, RegulatoryClass.None, scenED, baseED, ref vcCount);
                }
            }
            else if (this._mode == 2)
            {
                int rcCount = 0;
                foreach (RegulatoryClass regClass in RegClasses)
                {
                    this.WriteEffectsData(scen, year, VehicleClass.None, regClass, scenED, baseED, ref rcCount);
                }
            }
        }
        void WriteEffectsData(Scenario scen, int year, VehicleClass vehClass, RegulatoryClass regClass,
            EffectsData scenED, EffectsData baseED, ref int cCount)
        {
            FuelValue scenVMT = scenED.GeneralEffects.VMT.GetValue(vehClass, regClass);
            FuelValue baseVMT = (baseED == null) ? FuelValue.Zero : baseED.GeneralEffects.VMT.GetValue(vehClass, regClass);
            foreach (FuelType fuelType in FuelTypes)
            {
                if (scenVMT[fuelType] != 0 || baseVMT[fuelType] != 0)
                {
                    if (cCount > 1 || (vehClass != VehicleClass.All && regClass != RegulatoryClass.All))
                    {
                        this.WriteEffectsData(scen, year, vehClass, regClass, fuelType, scenED, baseED);
                    }
                }
            }
            if (scenVMT.Total != 0 || baseVMT.Total != 0) { cCount++; }
        }
        void WriteEffectsData(Scenario scen, int year, VehicleClass vehClass, RegulatoryClass regClass, FuelType fuelType,
            EffectsData scenED, EffectsData baseED)
        {
            this.Write(scen.Index);
            this.Write(Interaction.GetTitleCase(scen.Description));
            this.Write(year     == -1                  ? "TOTAL" : year    .ToString());
            this.Write(
                (this._mode == 1) ?
                      (vehClass == VehicleClass   .All ? "TOTAL" : vehClass.ToString()) :
                      (regClass == RegulatoryClass.All ? "TOTAL" : regClass.ToString()));
            this.Write(fuelType == FuelType       .All ? "TOTAL" : fuelType.ToString());
            var scenGE = scenED.GeneralEffects;
            var scenEE = scenED.EnergyEffects;
            var scenME = scenED.EmissionEffects;
            var scenSE = scenED.SafetyEffects;
            var baseGE = (baseED == null) ? EmptyGeneralEffects  : baseED.GeneralEffects;
            var baseEE = (baseED == null) ? EmptyEnergyEffects   : baseED.EnergyEffects;
            var baseME = (baseED == null) ? EmptyEmissionEffects : baseED.EmissionEffects;
            var baseSE = (baseED == null) ? EmptySafetyEffects   : baseED.SafetyEffects;
            if (year == -1) { this.WriteEmpty(6); }
            else
            {
                this.Write(scenGE.GetRatedFE   (vehClass, regClass, fuelType, scenEE));
                this.Write(scenGE.GetOnRoadFE  (vehClass, regClass, fuelType, scenEE));
                this.Write(scenGE.GetFuelShare (vehClass, regClass, fuelType));
                this.Write(scenGE.GetCurbWeight(vehClass, regClass, fuelType));
                this.Write(scenGE.GetFootprint (vehClass, regClass, fuelType));
                this.Write(scenGE.GetWorkFactor(vehClass, regClass, fuelType));
            }
            this.Write( scenGE.GetSales        (vehClass, regClass, fuelType));
            this.Write( scenGE.GetVMT          (vehClass, regClass, fuelType) - baseGE.GetVMT        (vehClass, regClass, fuelType));
            this.Write((scenEE.GetBTU          (vehClass, regClass, fuelType) - baseEE.GetBTU        (vehClass, regClass, fuelType)) / 1e12);
            this.Write( scenEE.GetGallons      (vehClass, regClass, fuelType) - baseEE.GetGallons    (vehClass, regClass, fuelType));
            this.Write( scenEE.GetNativeUnits  (vehClass, regClass, fuelType) - baseEE.GetNativeUnits(vehClass, regClass, fuelType));
            this.Write( scenSE.GetFatalities   (vehClass, regClass, fuelType) - baseSE.GetFatalities (vehClass, regClass, fuelType));
            for (int i = 0; i < Pollutants.Length; i++)
            {
                double emissions = scenME.GetTotalEmissions(vehClass, regClass, fuelType, Pollutants[i]) -
                                   baseME.GetTotalEmissions(vehClass, regClass, fuelType, Pollutants[i]);
                if (Pollutants[i] == Pollutant.CO2) { emissions /= 1e6; }
                this.Write(emissions);
            }
            this.NewRow();
        }
        #endregion
        #region 
        static readonly GeneralEffects           EmptyGeneralEffects  = new GeneralEffects();
        static readonly EnergyConsumptionEffects EmptyEnergyEffects   = new EnergyConsumptionEffects();
        static readonly EmissionEffects          EmptyEmissionEffects = new EmissionEffects();
        static readonly SafetyEffects            EmptySafetyEffects   = new SafetyEffects();
        static readonly VehicleClass   [] VehClasses;
        static readonly RegulatoryClass[] RegClasses;
        static readonly FuelType       [] FuelTypes;
        static readonly Pollutant      [] Pollutants = new Pollutant[] {
            Pollutant.CO, Pollutant.VOC, Pollutant.NOx, Pollutant.SO2, Pollutant.PM, Pollutant.CO2, Pollutant.CH4, Pollutant.N2O,
            Pollutant.Acetaldehyde, Pollutant.Acrolein, Pollutant.Benzene, Pollutant.Butadiene, Pollutant.Formaldehyde,
            Pollutant.DPM10, Pollutant.MTBE };
        int _mode;  
        EffectsData _scenEDSum, _baseEDSum;
        #endregion
    }
}

