#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 CsvSocietalCostsByVehClassReport : CsvSocietalCostsReport
    {
        #region 
        public CsvSocietalCostsByVehClassReport(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 Costs by Vehicle Class CSV Report";
        const string Header = "Scenario,Scenario Name,Model Year,Veh-Class,Fuel Type,Disc-Rate," +
            "Pre-Tax Fuel Cost,Fuel Tax Cost,Retail Fuel Costs,Drive Surplus,Refuel Surplus," +
            "Market Externalities,Congestion Costs,Accident Costs,Noise Costs,Fatality Costs," +
            "CO Damage Costs,VOC Damage Costs,NOx Damage Costs,SO2 Damage Costs,PM Damage Costs,CO2 Damage Costs,CH4 Damage Costs,N2O Damage Costs," +
            "Total Consumer Costs,Total Social Costs";
        #endregion
    }
    [Serializable]
    public sealed class CsvSocietalCostsByRegClassReport : CsvSocietalCostsReport
    {
        #region 
        public CsvSocietalCostsByRegClassReport(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 Costs by Regulatory Class CSV Report";
        const string Header = "Scenario,Scenario Name,Model Year,Reg-Class,Fuel Type,Disc-Rate," +
            "Pre-Tax Fuel Cost,Fuel Tax Cost,Retail Fuel Costs,Drive Surplus,Refuel Surplus," +
            "Market Externalities,Congestion Costs,Accident Costs,Noise Costs,Fatality Costs," +
            "CO Damage Costs,VOC Damage Costs,NOx Damage Costs,SO2 Damage Costs,PM Damage Costs,CO2 Damage Costs,CH4 Damage Costs,N2O Damage Costs," +
            "Total Consumer Costs,Total Social Costs";
        #endregion
    }
    [Serializable]
    public abstract class CsvSocietalCostsReport : CsvReportingBase
    {
        #region 
        static CsvSocietalCostsReport()
        {
            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;
        }
        protected CsvSocietalCostsReport(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.WriteCostsData(scen, year, scenED.UndiscountedCosts, (baseED == null) ? EmptyCosts : baseED.UndiscountedCosts);
            this.WriteCostsData(scen, year, scenED.DiscountedCosts  , (baseED == null) ? EmptyCosts : baseED.DiscountedCosts  );
            if (year.Year == compliance.MinYear)
            {
                this._scenUSum = new OwnerAndSocietalCosts(scenED.UndiscountedCosts.DiscountRate);
                this._scenDSum = new OwnerAndSocietalCosts(scenED.DiscountedCosts  .DiscountRate);
                this._baseUSum = new OwnerAndSocietalCosts(new VCValue<double>());
                this._baseDSum = new OwnerAndSocietalCosts(new VCValue<double>());
            }
            this.AggregateCostsData(scenED, this._scenUSum, this._scenDSum);
            if (!scen.IsBaseline) { this.AggregateCostsData(baseED, this._baseUSum, this._baseDSum); }
            if (year.Year == compliance.MaxYear)
            {
                this.WriteCostsData(scen, null, this._scenUSum, this._baseUSum);
                this.WriteCostsData(scen, null, this._scenDSum, this._baseDSum);
            }
        }
        void AggregateCostsData(EffectsData ed, OwnerAndSocietalCosts uSum, OwnerAndSocietalCosts dSum)
        {
            uSum.AppendValue(ed.UndiscountedCosts);
            dSum.AppendValue(ed.DiscountedCosts  );
        }
        void WriteCostsData(Scenario scen, ModelYear year, OwnerAndSocietalCosts scenCosts, OwnerAndSocietalCosts baseCosts)
        {
            if (this._mode == 1)
            {
                int vcCount = 0;
                foreach (VehicleClass vehClass in VehClasses)
                {
                    this.WriteCostsData(scen, year, vehClass, RegulatoryClass.None, scenCosts, baseCosts, ref vcCount);
                }
            }
            else if (this._mode == 2)
            {
                int rcCount = 0;
                foreach (RegulatoryClass regClass in RegClasses)
                {
                    this.WriteCostsData(scen, year, VehicleClass.None, regClass, scenCosts, baseCosts, ref rcCount);
                }
            }
        }
        void WriteCostsData(Scenario scen, ModelYear year, VehicleClass vehClass, RegulatoryClass regClass,
            OwnerAndSocietalCosts scenCosts, OwnerAndSocietalCosts baseCosts, ref int cCount)
        {
            FuelValue scenTtl = scenCosts.TotalSocialCosts.GetValue(vehClass, regClass);
            FuelValue baseTtl = baseCosts.TotalSocialCosts.GetValue(vehClass, regClass);
            foreach (FuelType fuelType in FuelTypes)
            {
                if (scenTtl[fuelType] != 0 || baseTtl[fuelType] != 0)
                {
                    if (cCount > 1 || (vehClass != VehicleClass.All && regClass != RegulatoryClass.All))
                    {
                        this.WriteCostsData(scen, year, vehClass, regClass, fuelType, scenCosts, baseCosts);
                    }
                }
            }
            if (scenTtl.Total != 0 || baseTtl.Total != 0) { cCount++; }
        }
        void WriteCostsData(Scenario scen, ModelYear year, VehicleClass vehClass, RegulatoryClass regClass, FuelType fuelType,
            OwnerAndSocietalCosts scenCosts, OwnerAndSocietalCosts baseCosts)
        {
            this.Write(scen.Index);
            this.Write(Interaction.GetTitleCase(scen.Description));
            this.Write(year     == null                ? "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());
            this.Write(scenCosts.GetDiscountRate      (vehClass, regClass));
            this.Write(scenCosts.GetPreTaxFuelCosts(vehClass, regClass, fuelType) - baseCosts.GetPreTaxFuelCosts(vehClass, regClass, fuelType));
            this.Write(scenCosts.GetFuelTaxCosts   (vehClass, regClass, fuelType) - baseCosts.GetFuelTaxCosts   (vehClass, regClass, fuelType));
            this.Write(scenCosts.GetRetailFuelCosts(vehClass, regClass, fuelType) - baseCosts.GetRetailFuelCosts(vehClass, regClass, fuelType));
            this.Write(scenCosts.GetDriveSurplus   (vehClass, regClass, fuelType) - baseCosts.GetDriveSurplus   (vehClass, regClass, fuelType));
            this.Write(scenCosts.GetRefuelSurplus  (vehClass, regClass, fuelType) - baseCosts.GetRefuelSurplus  (vehClass, regClass, fuelType));
            this.Write(scenCosts.GetEconomicCosts  (vehClass, regClass, fuelType) - baseCosts.GetEconomicCosts  (vehClass, regClass, fuelType));
            this.Write(scenCosts.GetCongestionCosts(vehClass, regClass, fuelType) - baseCosts.GetCongestionCosts(vehClass, regClass, fuelType));
            this.Write(scenCosts.GetAccidentCosts  (vehClass, regClass, fuelType) - baseCosts.GetAccidentCosts  (vehClass, regClass, fuelType));
            this.Write(scenCosts.GetNoiseCosts     (vehClass, regClass, fuelType) - baseCosts.GetNoiseCosts     (vehClass, regClass, fuelType));
            this.Write(scenCosts.GetFatalityCosts  (vehClass, regClass, fuelType) - baseCosts.GetFatalityCosts  (vehClass, regClass, fuelType));
            for (int i = 0; i < Pollutants.Length; i++)
            {
                this.Write(scenCosts.GetEmissionsCosts(vehClass, regClass, fuelType, Pollutants[i]) -
                           baseCosts.GetEmissionsCosts(vehClass, regClass, fuelType, Pollutants[i]));
            }
            this.Write(scenCosts.GetTotalConsumerCosts(vehClass, regClass, fuelType) - baseCosts.GetTotalConsumerCosts(vehClass, regClass, fuelType));
            this.Write(scenCosts.GetTotalSocialCosts  (vehClass, regClass, fuelType) - baseCosts.GetTotalSocialCosts  (vehClass, regClass, fuelType));
            this.NewRow();
        }
        #endregion
        #region 
        static readonly OwnerAndSocietalCosts EmptyCosts = new OwnerAndSocietalCosts(new VCValue<double>());
        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 };
        int _mode;  
        OwnerAndSocietalCosts _scenUSum, _scenDSum, _baseUSum, _baseDSum;
        #endregion
    }
}

