using System;
using System.Collections.Generic;

using Volpe.Cafe.EffectsModels.Data;
using Volpe.Cafe.Generic;
using Volpe.Cafe.MarketData;
using Volpe.Cafe.MarketData.Design;

using RC = Volpe.Cafe.RegulatoryClass;

namespace Volpe.Cafe.EspressoUI.ModelCharts
{
    [Serializable]
    static class ChartHelper
    {
        /// <summary>
        /// Calculates statistics for all manufacturers within the industry, using the specified reg-class filter.
        /// </summary>
        /// <param name="data">The industry for which to calculate statistics.</param>
        /// <param name="rcFilter">The regulatory class filter for which to calculate statistics.</param>
        /// <param name="yearFilter">The model year filter for which to calculate statistics.</param>
        /// <param name="metrics">The compliance metrics for which to calculate statistics.</param>
        /// <returns>The calculated compliance statistics.</returns>
        public static ComplianceStatistics CalculateStatistics(
            IIndustry data, int rcFilter, int yearFilter, ComplianceMetrics metrics)
        {
            return CalculateStatistics(data.Manufacturers, rcFilter, yearFilter, metrics);
        }
        /// <summary>
        /// Calculates statistics for the specified manufacturer, using the specified reg-class filter.
        /// </summary>
        /// <param name="mfr">The manufacturer for which to calculate statistics.</param>
        /// <param name="rcFilter">The regulatory class filter for which to calculate statistics.</param>
        /// <param name="yearFilter">The model year filter for which to calculate statistics.</param>
        /// <param name="metrics">The compliance metrics for which to calculate statistics.</param>
        /// <returns>The calculated compliance statistics.</returns>
        public static ComplianceStatistics CalculateStatistics(
            IManufacturer mfr, int rcFilter, int yearFilter, ComplianceMetrics metrics)
        {
            return CalculateStatistics(new List<IManufacturer> { mfr }, rcFilter, yearFilter, metrics);
        }
        /// <summary>
        /// Calculates statistics for one or more manufacturers, using the specified regulatory class filter.
        /// </summary>
        /// <param name="mfrs">One or more manufacturers for which to calculate statistics.</param>
        /// <param name="rcFilter">The regulatory class filter for which to calculate statistics.</param>
        /// <param name="yearFilter">The model year filter for which to calculate statistics.</param>
        /// <param name="metrics">The compliance metrics for which to calculate statistics.</param>
        /// <returns>The calculated compliance statistics.</returns>
        public static ComplianceStatistics CalculateStatistics(
            IReadOnlyList<IManufacturer> mfrs, int rcFilter, int yearFilter, ComplianceMetrics metrics)
        {
            try
            {
                var statistics  = new ComplianceStatistics();
                RC  regClass    = ChartFilters.RegClasses[rcFilter];
                var salesSum    = RCDouble.Zero;
                var stndSum     = RCDouble.Zero;
                var cafeSum     = RCDouble.Zero;
                var cafe2BagSum = RCDouble.Zero;
                var co2SalesSum = RCAltDouble.Zero;
                var co2StndSum  = RCAltDouble.Zero;
                var co2RtngSum  = RCAltDouble.Zero;

                foreach (var mfr in mfrs)
                {
                    var revision = mfr.GetRevision(yearFilter);
                    if (revision == null) {
                        continue;
                    }
                    var stats = revision.ComplianceStats;
                    var costs = revision.ComplianceCosts.GetValue(regClass);

                    // aggregate sales
                    statistics.Sales += stats.GetSales(regClass);

                    if (metrics.HasCAFEValues())
                    {   // aggregate "sum" values first, then divide after all mfrs were processed
                        foreach (RC rc in RegClasses.List)
                        {
                            double sales = stats.VehicleStats[rc].Sales;
                            if (sales == 0) { continue; }
                            //
                            salesSum   [rc] += sales;
                            stndSum    [rc] += sales / stats.Standard  [rc];
                            cafeSum    [rc] += sales / stats.CAFERating[rc];
                            cafe2BagSum[rc] += sales / stats.CAFE2Cycle[rc];
                        }
                    }

                    if (metrics.HasCO2Values())
                    {   // aggregate "sum" values first, then divide after all mfrs were processed
                        foreach (RC rc in RegClasses.AltList)
                        {
                            double sales = stats.VehicleStats[rc].Sales;
                            if (sales == 0) { continue; }
                            //
                            co2SalesSum[rc] += sales;
                            co2StndSum [rc] += sales * stats.CO2Standard[rc];
                            co2RtngSum [rc] += sales * stats.CO2Rating  [rc];
                        }
                    }

                    if (metrics.HasComplianceCosts())
                    {
                        statistics.Fines += stats.Fines[regClass] / 1e6; // convert $ to $m
                        statistics.Costs += costs.TechCost / 1e6;
                    }

                    // **note: if effects model hasn't had a chance to run yet, effectsData will be null
                    mfr.Tag.TryGetValue($"EffectsData_{yearFilter}", out var effectsDataObj);
                    var effetsData = (EffectsDataLite)effectsDataObj;
                    if (metrics.HasEffectsCosts() && effetsData != null)
                    {
                        var initState = effetsData.InitialState;
                        var finalState = effetsData.FinalState;

                        statistics.FuelSavings +=
                            (initState.RetailFuelCosts.CalcSum(regClass).Total -
                             finalState.RetailFuelCosts.CalcSum(regClass).Total) / 1e3; // convert $k to $m
                        statistics.CO2Savings +=
                            (initState.EmissionCosts.CO2.CalcSum(regClass).Total -
                             finalState.EmissionCosts.CO2.CalcSum(regClass).Total) / 1e3; // convert $k to $m

                        statistics.FuelCosts += finalState.RetailFuelCosts.CalcSum(regClass).Total / 1e3; // convert $k to $m
                        statistics.EmissionsDamage +=
                            finalState.GetTotalEmissionDamage(regClass, FuelType.All) / 1e3; // convert $k to $m
                    }
                }

                // finalize CAFE metrics
                double totalSales = salesSum[regClass];
                if (metrics.HasCAFEValues() && totalSales != 0)
                {
                    statistics.Standard   = Math.Round(totalSales / stndSum    [regClass], 4);
                    statistics.CAFE       = Math.Round(totalSales / cafeSum    [regClass], 4);
                    statistics.CAFE2Cycle = Math.Round(totalSales / cafe2BagSum[regClass], 4);
                    statistics.Shortfall  = Math.Max(0, statistics.Standard - statistics.CAFE);
                }

                // finalize CO-2 metrics
                double totalCO2Sales = co2SalesSum[regClass];
                if (metrics.HasCO2Values() && totalCO2Sales != 0)
                {
                    statistics.CO2Standard  = Math.Round(co2StndSum[regClass] / totalCO2Sales, 4);
                    statistics.CO2Rating    = Math.Round(co2RtngSum[regClass] / totalCO2Sales, 4);
                    statistics.CO2Shortfall = Math.Max(0, statistics.CO2Rating - statistics.CO2Standard);
                }

                return statistics;
            }
            catch
            {
                return ComplianceStatistics.Empty;
            }
        }

    }
}
