#region << Using Directives >>
using System;
using System.Collections.Generic;
using System.Text;
using Volpe.Cafe.Data;
using Volpe.Cafe.Generic;
using Volpe.Cafe.Model;
using Volpe.Cafe.Settings;
using Volpe.Cafe.Utils;
using TI = Volpe.Cafe.TechnologyIndexes;
#endregion
namespace Volpe.Cafe.IO.Reporting.CSV
{
    [Serializable]
    public sealed class CsvTechnologyUtilizationReport : CsvReportingBase
    {
        #region 
        static CsvTechnologyUtilizationReport()
        {
            int rcCount = RCValue<object>.Classes.Length;
            RegClasses = new RegulatoryClass[rcCount + 1];
            Array.Copy(RCValue<object>.Classes, RegClasses, rcCount);
            RegClasses[rcCount] = RegulatoryClass.All;
        }
        public CsvTechnologyUtilizationReport(string path, bool append) : base(path, append) { }
        #endregion
        #region 
        protected override string GetHeader()
        {
            return Header;
        }
        protected override void ParseData_Internal(Scenario scen, ModelYear year, ICompliance compliance)
        {
            List<Manufacturer> scenMfrs = compliance.GetData(scen                            , year).Manufacturers;
            List<Manufacturer> baseMfrs = compliance.GetData(compliance.Settings.Scenarios[0], year).Manufacturers;
            RCDouble   scenIndSales        = RCDouble.Zero;
            RCDouble[] scenIndAppliedSales = new RCDouble[TI.TechnologyCount + 1];  
            RCDouble[] scenIndUsedSales    = new RCDouble[TI.TechnologyCount + 1];
            RCDouble[] scenMfrAppliedSales = new RCDouble[TI.TechnologyCount + 1];
            RCDouble[] scenMfrUsedSales    = new RCDouble[TI.TechnologyCount + 1];
            RCDouble   baseIndSales        = RCDouble.Zero;
            RCDouble[] baseIndAppliedSales = new RCDouble[TI.TechnologyCount + 1];
            RCDouble[] baseIndUsedSales    = new RCDouble[TI.TechnologyCount + 1];
            RCDouble[] baseMfrAppliedSales = new RCDouble[TI.TechnologyCount + 1];
            RCDouble[] baseMfrUsedSales    = new RCDouble[TI.TechnologyCount + 1];
            for (int i = 0; i < TI.TechnologyCount + 1; i++)
            {
                scenIndAppliedSales[i] = new RCDouble();
                scenIndUsedSales   [i] = new RCDouble();
                baseIndAppliedSales[i] = new RCDouble();
                baseIndUsedSales   [i] = new RCDouble();
            }
            int rcCount = 0;
            for (int i = 0; i < scenMfrs.Count; i++)
            {
                RCDouble scenMfrSales = scenMfrs[i].ModelData.Sales;
                RCDouble baseMfrSales = baseMfrs[i].ModelData.Sales;
                for (int j = 0; j < scenMfrUsedSales.Length; j++)
                {
                    this.CalcUtilization(year, scenMfrs[i], j, out scenMfrAppliedSales[j], out scenMfrUsedSales[j]);
                    this.CalcUtilization(year, baseMfrs[i], j, out baseMfrAppliedSales[j], out baseMfrUsedSales[j]);
                    scenIndAppliedSales[j] += scenMfrAppliedSales[j];
                    scenIndUsedSales   [j] += scenMfrUsedSales   [j];
                    baseIndAppliedSales[j] += baseMfrAppliedSales[j];
                    baseIndUsedSales   [j] += baseMfrUsedSales   [j];
                }
                scenIndSales += scenMfrSales;
                baseIndSales += baseMfrSales;
                rcCount = 0;
                foreach (RegulatoryClass regClass in RegClasses)
                {
                    if (scenMfrSales.GetValue(regClass) != 0 || baseMfrSales.GetValue(regClass) != 0)
                    {
                        if (rcCount > 1 || regClass != RegulatoryClass.All)
                        {
                            this.WriteTechUtilizationData(scen, year, scenMfrs[i], regClass,
                                scenMfrSales, scenMfrAppliedSales, scenMfrUsedSales, baseMfrSales, baseMfrAppliedSales, baseMfrUsedSales);
                            rcCount++;
                        }
                    }
                }
            } 
            rcCount = 0;
            foreach (RegulatoryClass regClass in RegClasses)
            {
                if (scenIndSales.GetValue(regClass) != 0 || baseIndSales.GetValue(regClass) != 0)
                {
                    if (rcCount > 1 || regClass != RegulatoryClass.All)
                    {
                        this.WriteTechUtilizationData(scen, year, null, regClass,
                            scenIndSales, scenIndAppliedSales, scenIndUsedSales, baseIndSales, baseIndAppliedSales, baseIndUsedSales);
                        rcCount++;
                    }
                }
            }
        }
        void WriteTechUtilizationHeader(Scenario scen, ModelYear year, Manufacturer mfr, RegulatoryClass regClass, string techAbbr)
        {
            this.Write(scen.Index);
            this.Write(Interaction.GetTitleCase(scen.Description));
            this.Write(year.ToString());
            this.Write(mfr      == null                ? "TOTAL" : mfr.Description.ProperName);
            this.Write(regClass == RegulatoryClass.All ? "TOTAL" : regClass.ToString());
            this.Write(techAbbr);
        }
        void WriteTechUtilizationData(Scenario scen, ModelYear year, Manufacturer mfr, RegulatoryClass regClass,
            RCDouble scenSales, RCDouble[] scenAppliedSales, RCDouble[] scenUsedSales,
            RCDouble baseSales, RCDouble[] baseAppliedSales, RCDouble[] baseUsedSales)
        {
            double sSales = scenSales.GetValue(regClass);
            double bSales = baseSales.GetValue(regClass);
            for (int i = 0; i < TI.TechnologyCount; i++)
            {
                this.WriteTechUtilizationHeader(scen, year, mfr, regClass, TI.GetAbbr(i));
                this.Write(                     scenAppliedSales[i].GetValue(regClass) / sSales);   
                this.Write(                     scenUsedSales   [i].GetValue(regClass) / sSales);
                this.Write(((sSales == 0) ? 0 : scenAppliedSales[i].GetValue(regClass) / sSales) -  
                           ((bSales == 0) ? 0 : baseAppliedSales[i].GetValue(regClass) / bSales));
                this.Write(((sSales == 0) ? 0 : scenUsedSales   [i].GetValue(regClass) / sSales) -
                           ((bSales == 0) ? 0 : baseUsedSales   [i].GetValue(regClass) / bSales));
                this.NewRow();
                if (i == TI.DDOWN)
                {   
                    this.WriteTechUtilizationHeader(scen, year, mfr, regClass, "E85 FFV");
                    this.Write(                     scenAppliedSales[TI.TechnologyCount].GetValue(regClass) / sSales);  
                    this.Write(                     scenUsedSales   [TI.TechnologyCount].GetValue(regClass) / sSales);
                    this.Write(((sSales == 0) ? 0 : scenAppliedSales[TI.TechnologyCount].GetValue(regClass) / sSales) - 
                               ((bSales == 0) ? 0 : baseAppliedSales[TI.TechnologyCount].GetValue(regClass) / bSales));
                    this.Write(((sSales == 0) ? 0 : scenUsedSales   [TI.TechnologyCount].GetValue(regClass) / sSales) -
                               ((bSales == 0) ? 0 : baseUsedSales   [TI.TechnologyCount].GetValue(regClass) / bSales));
                    this.NewRow();
                }
            } 
        }
        void CalcUtilization(ModelYear year, Manufacturer mfr, int techIndex, out RCDouble appliedSales, out RCDouble usedSales)
        {
            appliedSales = new RCDouble();
            usedSales    = new RCDouble();
            List<Vehicle> vehs = mfr.Vehicles;
            for (int i = 0, vehCount = vehs.Count; i < vehCount; i++)
            {
                Vehicle.CModelData vmd       = vehs[i].ModelData;
                RegulatoryClass    regClass  = vehs[i].RegClass;
                FuelValue          fuelShare = vehs[i].Description.FuelShare;
                double             vehSales  = vehs[i].Description.Sales[year.Index];
                if (techIndex >= TI.TechnologyCount)
                {   
                    if (techIndex == TI.TechnologyCount)
                    {   
                        if (fuelShare.Ethanol85 > 0) { usedSales[regClass] += vehSales; }
                    }
                }
                else
                {
                    if (vmd.TechUsed[techIndex] && !vmd.TechSuperseded[techIndex])
                    {
                        if (vmd.TechApplied[techIndex]) { appliedSales[regClass] += vehSales; }
                        usedSales[regClass] += vehSales;
                    }
                }
            } 
        }
        #endregion
        #region 
        public override string ReportName { get { return CsvReportName; } }
        #endregion
        #region 
        static readonly RegulatoryClass[] RegClasses;
        public const string CsvReportName = "Technology Utilization CSV Report";
        const string Header = "Scenario,Scenario Name,Model Year,Manufacturer,Reg-Class,Technology,App-Rate,Pen-Rate,Incr.AR,Incr.PR";
        #endregion
    }
}

