#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member

using System;
using System.Collections.Generic;
using Volpe.Cafe.Generic;
using Volpe.Cafe.ModelSettings;

namespace Volpe.Cafe.DfsSrModels.Aggregators
{
    /// <summary>
    /// Provides methods for aggregating forecast fleet data from individual forecast vehicles or from the historic
    /// fleet data based on regulatory classification.
    /// </summary>
    [Serializable]
    public sealed class ForecastFleetAggregatorRegClass : IForecastFleetAggregator
    {
        /// <summary>
        /// Initializes a new instances of the <see cref="ForecastFleetAggregatorRegClass"/> class, using the following
        /// modeling <paramref name="settings"/>.
        /// </summary>
        /// <param name="settings">The modeling settings used for analysis.</param>
        /// <param name="aggregateByBaseRegClass">
        ///     <c>true</c>, to aggregate forecast fleet data, for the analysis fleet, using a vehicle's baseline
        ///     <see cref="RegulatoryClass"/> assignment; <c>false</c>, to aggregate using a vehicle revision's
        ///     <see cref="RegulatoryClass"/> assignment.
        /// </param>
        public ForecastFleetAggregatorRegClass(ModelingSettings settings, bool aggregateByBaseRegClass)
        {
            this.Settings = settings;
            this._minYear = this.Settings.RuntimeSettings.AnalysisStartYear;
            this._aggregateByBaseRegClass = aggregateByBaseRegClass;
        }

        private int _minYear;
        private bool _aggregateByBaseRegClass;

        public ModelingSettings Settings { get; }

        public ForecastFleet AggregateFleetData(Scenario scen, int year, IList<ForecastVehicle> forecastVehs)
        {
            ForecastFleet forecastFleet = (year < this._minYear)
                ? this.AggregateHistoricFleetData(scen, year)
                : this.AggregateAnalysisFleetData(scen, year, forecastVehs);

            return forecastFleet;
        }

        public ForecastFleet AggregateAnalysisFleetData(Scenario scen, int year, IList<ForecastVehicle> forecastVehs)
        {
            var econValues = this.Settings.Parameters.EconomicValues;
            var forecastFleet = new RCAltValue<ForecastFleetData>(() => new ForecastFleetData());

            foreach (var veh in forecastVehs) {
                var vehData = veh.ForecastData[year];
                var fleetData = forecastFleet[DfsSrHelpers.GetBinningRegClass(vehData, this._aggregateByBaseRegClass)];

                ForecastFleetAggregatorUtils.AggregateVehicleData(vehData, fleetData, scen, year, econValues);
            }

            ForecastFleetAggregatorUtils.FinalizeFleetData(
                forecastFleet.PassengerCar, forecastFleet.LightTruck, forecastFleet.HDPUV);

            return ForecastFleet.FromRCAltValue(forecastFleet);
        }

        public ForecastFleet AggregateHistoricFleetData(Scenario scen, int year)
        {
            // NOTE:
            // Historic fleet data is defined by "generic" vehicle style, but we need to assemble
            // data by reg-class. We'll use the "PCShare" variable to weigh the fleet info for car
            // and truck fleets. For the HDPUV fleet, however, the PCShare is ignored.

            var historicFleetData = this.Settings.Parameters.HistoricFleetData;

            var carHfd = historicFleetData.GetHistoricFleetData(VehicleStyle.GenericCar, year);
            var vanSuvHfd = historicFleetData.GetHistoricFleetData(VehicleStyle.GenericVanSUV, year);
            var pickupHfd = historicFleetData.GetHistoricFleetData(VehicleStyle.GenericPickup, year);
            var hdpuvHfd = historicFleetData.GetHistoricFleetData(VehicleStyle.Generic2b3, year);

            // for cars, start with car data and add in others as needed
            var carData = ForecastFleetAggregatorUtils.FromHistoricFleetData(carHfd, carHfd.PCShare);
            if (vanSuvHfd.PCShare != 0.0d) {
                var other = ForecastFleetAggregatorUtils.FromHistoricFleetData(vanSuvHfd, vanSuvHfd.PCShare);
                carData = ForecastFleetData.Combine(carData, other);
            }
            if (pickupHfd.PCShare != 0.0d) {
                var other = ForecastFleetAggregatorUtils.FromHistoricFleetData(pickupHfd, pickupHfd.PCShare);
                carData = ForecastFleetData.Combine(carData, other);
            }

            // for trucks, start with pickup data and add in others as needed
            var truckData = ForecastFleetAggregatorUtils.FromHistoricFleetData(pickupHfd, 1.0d - pickupHfd.PCShare);
            if (vanSuvHfd.PCShare != 1.0d) {
                var other = ForecastFleetAggregatorUtils.FromHistoricFleetData(vanSuvHfd, 1.0d - vanSuvHfd.PCShare);
                truckData = ForecastFleetData.Combine(truckData, other);
            }
            if (carHfd.PCShare != 1.0d) {
                var other = ForecastFleetAggregatorUtils.FromHistoricFleetData(carHfd, 1.0d - carHfd.PCShare);
                truckData = ForecastFleetData.Combine(truckData, other);
            }

            // for hdpuv, the mapping remains 1:1
            var hdpuvData = ForecastFleetAggregatorUtils.FromHistoricFleetData(hdpuvHfd);

            // finalize fleet shares for cars and trucks
            double totalLdSales = carData.Sales + truckData.Sales;
            carData.Share = carData.Sales / totalLdSales;
            truckData.Share = truckData.Sales / totalLdSales;

            return new ForecastFleet(carData, truckData, hdpuvData);
        }

    }
}
