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

using System;

using Volpe.Cafe.Generic;
using Volpe.Cafe.Logging;
using Volpe.Cafe.MarketData.Design;
using Volpe.Cafe.ModelSettings;

namespace Volpe.Cafe.DfsSrModels.DfsModels
{
    /// <summary>
    /// Provides the base class for DFS models.
    /// </summary>
    [Serializable]
    public abstract class DfsModelBase : IDfsModel
    {
        /// <summary>
        /// Initializes a new instances of the <see cref="DfsModelBase"/> class, using the specified industry data,
        /// modeling settings, and log writer.
        /// </summary>
        /// <param name="data">The product plan industry data used for analysis.</param>
        /// <param name="settings">The modeling settings used for analysis.</param>
        /// <param name="logWriter">The object used for writing log files generated during compliance modeling.</param>
        /// <exception cref="ArgumentNullException">data, settings, or logWriter is null.</exception>
        public DfsModelBase(IIndustry data, ModelingSettings settings, ILogger logWriter)
        {
            ThrowHelper.ThrowIfInvalidArgs(data, settings, logWriter);

            this.Data = data;
            this.Settings = settings;
            this.LogWriter = logWriter;
            this.MinYear = settings.RuntimeSettings.AnalysisStartYear;
            this.MaxYear = settings.RuntimeSettings.AnalysisEndYear;
        }

        public IIndustry Data { get; }
        public ModelingSettings Settings { get; }
        public ILogger LogWriter { get; }

        public abstract AggregationMode PreferredAggregationMode { get; }
        public abstract int NumberOfLaggedYears { get; }

        /// <summary>
        /// Gets the first model year available for compliance modeling.
        /// </summary>
        public int MinYear { get; }
        /// <summary>
        /// Gets the last model year available for compliance modeling.
        /// </summary>
        public int MaxYear { get; }

        /// <summary>
        /// Gets the first model year available for compliance modeling, reduced by the number of lagged years.
        /// </summary>
        public int LaggedMinYear => this.MinYear - Math.Max(0, this.NumberOfLaggedYears);

        /// <summary>
        /// Gets the fleet shares computed during the evaluation of the baseline scenario.
        /// </summary>
        protected AnnualValue<FleetTypeValue> BaseShares { get; private set; }

        public FleetTypeValue CalcNewFleetShares(Scenario scen, int iteration, int year,
            AnnualValue<ForecastFleet> baseFleet, AnnualValue<ForecastFleet> scenFleet)
        {
            if (this.BaseShares == null) {
                this.BaseShares = new AnnualValue<FleetTypeValue>(this.LaggedMinYear, this.MaxYear, null, -1);
            }

            var newShares = this.CalcNewFleetSharesInternal(scen, iteration, year, baseFleet, scenFleet);

            // Baseline shares should be saved for the second to last iteration, because that
            // represents the **final** baseline data used for compliance. However, if the total
            // number of iterations is one (i.e., no feedback), save those results also.
            // When evaluating alternative scenarios, the saved baseline shares may be used as
            // reference for DFS models that propagate or adjust based on baseline shares.
            int iterationCap = Math.Max(1, this.Settings.RuntimeSettings.DfsSrIterations - 1);
            if (scen.IsBaseline && iteration == iterationCap) {
                this.BaseShares[year] = newShares;
            }

            return newShares;
        }

        /// <inheritdoc cref="CalcNewFleetShares"/>
        protected abstract FleetTypeValue CalcNewFleetSharesInternal(Scenario scen, int iteration, int year,
            AnnualValue<ForecastFleet> baseFleet, AnnualValue<ForecastFleet> scenFleet);

    }
}
