using System;
using System.IO;
using Volpe.Cafe.DfsSrModels;

namespace Volpe.Cafe.ModelSettings
{
    /// <summary>
    /// Provides additional modeling options, as selected by the user during model's runtime, to further customize the
    /// model behavior, beyond what is available in the input files.
    /// </summary>
    [Serializable]
    public class RuntimeSettings
    {
        #region <<< Constructors >>>

        /// <summary>
        /// Initializes a new instance of the <see cref="RuntimeSettings"/> class, optionally setting the initial state
        /// to the default values.
        /// </summary>
        /// <param name="setDefaultSettings">
        ///     true, to initialize the new instance with default values; false, otherwise.
        /// </param>
        public RuntimeSettings(bool setDefaultSettings)
        {
            if (setDefaultSettings) {
                this.SetDefaultSettings();
            }
        }

        #endregion

        #region <<< Properties >>>

        /// <summary>
        /// Gets the <see cref="RuntimeSettings"/> object initialized using the default values.
        /// </summary>
        public static RuntimeSettings DefaultSettings => new RuntimeSettings(true);

        /// <summary>
        /// Gets the <see cref="RuntimeSettings"/> object initialized using the default values as appropriate for
        /// light-duty "EIS" analysis.
        /// </summary>
        public static RuntimeSettings DefaultLdEisSettings
        {
            get {
                var settings = new RuntimeSettings(true) {
                    AllowCreditTrading = true,
                    ExcludeBevFcvFromAnalysis = false
                };
                return settings;
            }
        }

        //----- compliance modeling settings -----
        /// <summary>
        /// Gets or sets the compliance program the model should enforce when evaluating a manufacturer's compliance
        /// state.
        /// </summary>
        public ComplianceProgram ComplianceProgram { get; set; }

        /// <summary>
        /// Gets or sets the first model year the system will evaluate for compliance. This should typically correlate
        /// to the model year for which the baseline input fleet is defined.
        /// </summary>
        public int AnalysisStartYear { get; set; }
        /// <summary>
        /// Gets or sets the first model year the system will evaluate for compliance for alternative (non-baseline)
        /// scenarios. Any fleet improvements made during analysis of the baseline scenario will be inherited during
        /// evaluation of alternative scenarios for each model year prior to the alternative start year.
        /// </summary>
        public int AlternativeStartYear { get; set; }
        /// <summary>
        /// Gets or sets the first model year when the system will begin evaluating technologies for application on
        /// vehicles. Prior to this year, the system will only determine manufacturers' compliance levels, generate
        /// available credits and fines owed, and use expiring credits (if credit trading is enabled) to offset
        /// compliance shortfalls as needed. Any non-expiring banked credits available prior to start of the analysis
        /// (which are specified as input for each manufacturer) will not be used for model years prior to this
        /// starting year.
        /// </summary>
        public int TechStartYear { get; set; }
        /// <summary>
        /// Gets or sets the last model year the system will evaluate for compliance.
        /// </summary>
        public int AnalysisEndYear { get; set; }

        /// <summary>
        /// Gets or sets whether the modeling system should allow manufacturers to trade or transfer credits between
        /// fleets, or to carry credits forward or back between model years.
        /// </summary>
        public bool AllowCreditTrading { get; set; }
        /// <summary>
        /// Gets or sets whether to the AC and off-cycle credit costs should be prevented from falling below zero.
        /// </summary>
        public bool CapOffCycleCosts { get; set; }
        /// <summary>
        /// Gets or sets whether the model should remove all BEVs and FCVs from the industry fleet prior to start of
        /// analysis and disallow application of new BEVs or FCVs during modeling (including for the ZEV mandate).
        /// </summary>
        public bool ExcludeBevFcvFromAnalysis { get; set; }

        //----- dynamic economic modeling settings -----
        /// <summary>
        /// Gets whether any of the dynamic economic models are enabled.
        /// </summary>
        public bool DynamicEconomicModeling { get; set; }

        //--- DFS/SR model settings ---
        /// <summary>
        /// Gets or sets the sales estimator that the DFS/SR model will use for projecting the total light-duty and
        /// HDPUV vehicle sales in each future model year.
        /// </summary>
        public SalesEstimator SalesEstimator { get; set; }
        /// <summary>
        /// Gets or sets a reference sales estimator that shall be used for projecting the total vehicle sales under
        /// the baseline scenario, whenever the <see cref="SalesEstimator.PriceElasticity"/> estimator is used. This
        /// value is not applicable for all other <see cref="DfsSrModels.SalesEstimator"/>s.
        /// </summary>
        public SalesEstimator ReferenceSalesEstimator { get; set; }
        /// <summary>
        /// Gets or sets the price elasticity multiplier to use with the <see cref="SalesEstimator.PriceElasticity"/>
        /// estimator. This value is not applicable for all other <see cref="DfsSrModels.SalesEstimator"/>s.
        /// </summary>
        public double PriceElasticitySalesEstimatorMultiplier { get; set; }

        /// <summary>
        /// Gets or sets the fleet share model that the DFS/SR model will use for estimating the light-duty and HDPUV
        /// fleet shares in each future model year.
        /// </summary>
        public DfsModel DfsModel { get; set; }
        /// <summary>
        /// Gets or sets a reference DFS model that shall be used for estimating the fleet shares under the baseline
        /// scenario, whenever the <see cref="DfsModel.PriceElasticity"/> DFS model is used. This value is not
        /// applicable for all other <see cref="DfsSrModels.DfsModel"/>s.
        /// </summary>
        public DfsModel ReferenceDfsModel { get; set; }
        /// <summary>
        /// Gets or sets the price elasticity coefficient to use with the <see cref="DfsModel.PriceElasticity"/> DFS
        /// model. This value is not applicable for all other <see cref="DfsSrModels.DfsModel"/>s.
        /// </summary>
        public double PriceElasticityDfsModelCoefficient { get; set; }

        /// <summary>
        /// Gets or sets the aggregation mode that the DFS/SR model will use for aggregating vehicle fleet data and
        /// rescaling vehicle sales. Specify <see cref="AggregationMode.Auto"/> to select an aggregation mode preferred
        /// by the selected <see cref="DfsModel"/>.
        /// </summary>
        public AggregationMode DfsSrAggregationMode { get; set; }
        /// <summary>
        /// Gets or sets the number of iterations to examine in the convergence loop of the DFS/SR model.
        /// </summary>
        public int DfsSrIterations { get; set; }

        //--- advanced settings for other dynamic economic models ---
        /// <summary>Gets or sets whether to disable the Dynamic Scrappage model from running.</summary>
        public bool DisableDynamicScrappageModel { get; set; }
        /// <summary>Gets or sets whether to disable the Dynamic VMT model from running.</summary>
        public bool DisableDynamicVmtModel { get; set; }

        //----- assumed fuel savings settings -----
        /// <summary>
        /// Gets or sets the assumed number of payback miles, during which an added investment in fuel improving
        /// technology is expected to pay back.
        /// </summary>
        /// <remarks>
        /// This value is used for the Sales Response model, the Dynamic Scrappage model, and for the calculation of
        /// the foregone consumer sales surplus.
        /// </remarks>
        public double AssumedPaybackMiles { get; set; }

        //----- effects modeling settings -----
        /// <summary>
        /// Gets or sets whether to disable the Effects model from running.
        /// </summary>
        public bool DisableEffectsModel { get; set; }

        /// <summary>
        /// Gets or sets whether to use low, average, or high fatality rate estimates when calculating vehicular
        /// fatalities and non-fatal crashes.
        /// </summary>
        public Estimates FatalityRateEstimates { get; set; }

        /// <summary>
        /// Gets or sets the percentage by which to scale the private consumer benefits for the effects calculations.
        /// </summary>
        public double ConsumerBenefitsScale { get; set; }

        /// <summary>
        /// Gets or sets whether the model should calculate implicit opportunity costs during effects calculations.
        /// </summary>
        public bool CalcImplicitOpportunityCost { get; set; }

        /// <summary>
        /// Gets or sets whether the model should calculate commercial operator implicit opportunity costs during
        /// effects calculations.
        /// </summary>
        public bool CalcCommercialOpportunityCost { get; set; }
        /// <summary>
        /// Gets or sets the percentage of consumers that purchase a light-duty vehicle for commercial use.
        /// </summary>
        public double CommercialShareLD { get; set; }

        #endregion

        #region <<< Methods >>>

        /// <summary>
        /// Creates a new object that is a copy of the current <see cref="RuntimeSettings"/> instance.
        /// </summary>
        /// <returns>A new object that is a copy of this <see cref="RuntimeSettings"/>.</returns>
        public RuntimeSettings Clone()
        {
            return new RuntimeSettings(false) {
                // compliance modeling settings
                ComplianceProgram = this.ComplianceProgram,
                AnalysisStartYear = this.AnalysisStartYear,
                AlternativeStartYear = this.AlternativeStartYear,
                TechStartYear = this.TechStartYear,
                AnalysisEndYear = this.AnalysisEndYear,
                AllowCreditTrading = this.AllowCreditTrading,
                CapOffCycleCosts = this.CapOffCycleCosts,
                ExcludeBevFcvFromAnalysis = this.ExcludeBevFcvFromAnalysis,

                // dynamic economic modeling settings
                DynamicEconomicModeling = this.DynamicEconomicModeling,
                SalesEstimator = this.SalesEstimator,
                ReferenceSalesEstimator = this.ReferenceSalesEstimator,
                PriceElasticitySalesEstimatorMultiplier = this.PriceElasticitySalesEstimatorMultiplier,
                DfsModel = this.DfsModel,
                ReferenceDfsModel = this.ReferenceDfsModel,
                PriceElasticityDfsModelCoefficient = this.PriceElasticityDfsModelCoefficient,
                DfsSrAggregationMode = this.DfsSrAggregationMode,
                DfsSrIterations = this.DfsSrIterations,
                DisableDynamicScrappageModel = this.DisableDynamicScrappageModel,
                DisableDynamicVmtModel = this.DisableDynamicVmtModel,

                // assumed fuel savings settings
                AssumedPaybackMiles = this.AssumedPaybackMiles,

                // effects modeling settings
                DisableEffectsModel = this.DisableEffectsModel,
                FatalityRateEstimates = this.FatalityRateEstimates,
                ConsumerBenefitsScale = this.ConsumerBenefitsScale,
                CalcImplicitOpportunityCost = this.CalcImplicitOpportunityCost,
                CalcCommercialOpportunityCost = this.CalcCommercialOpportunityCost,
                CommercialShareLD = this.CommercialShareLD,
            };
        }

        /// <summary>
        /// Sets the initial state of the current <see cref="RuntimeSettings"/> object to the default values.
        /// </summary>
        public void SetDefaultSettings()
        {
            // compliance modeling settings
            this.ComplianceProgram = ComplianceProgram.CAFE;
            this.AnalysisStartYear = 2024;
            this.AlternativeStartYear = 2027;
            this.TechStartYear = 2025;
            this.AnalysisEndYear = 2050;
            this.AllowCreditTrading = false;
            this.CapOffCycleCosts = true;
            this.ExcludeBevFcvFromAnalysis = true;

            // dynamic economic modeling settings
            this.DynamicEconomicModeling = true;
            this.SalesEstimator = SalesEstimator.PriceElasticity;
            this.ReferenceSalesEstimator = SalesEstimator.UserDefinedForecast;
            this.PriceElasticitySalesEstimatorMultiplier = -0.4;
            this.DfsModel = DfsModel.PriceElasticity;
            this.ReferenceDfsModel = DfsModel.UserDefinedShares;
            this.PriceElasticityDfsModelCoefficient = -0.0000420;
            this.DfsSrAggregationMode = AggregationMode.ByBaselineRegClass;
            this.DfsSrIterations = 4;
            this.DisableDynamicScrappageModel = false;
            this.DisableDynamicVmtModel = false;

            // assumed fuel savings settings
            this.AssumedPaybackMiles = 48000;

            // effects modeling settings
            this.DisableEffectsModel = false;
            this.FatalityRateEstimates = Estimates.Average;
            this.ConsumerBenefitsScale = 1.0;
            this.CalcImplicitOpportunityCost = true;
            this.CalcCommercialOpportunityCost = false;
            this.CommercialShareLD = 0.0;
        }

        /// <summary>
        /// Compares the current <see cref="RuntimeSettings"/> instance to the specified <paramref name="settings"/>.
        /// </summary>
        /// <returns>
        /// <c>true</c>, if the current instance equals the specified <paramref name="settings"/>;
        /// <c>false</c>, otherwise.
        /// </returns>
        public bool CompareTo(RuntimeSettings settings)
        {
            // compliance modeling settings
            return settings.ComplianceProgram == this.ComplianceProgram
                && settings.AnalysisStartYear == this.AnalysisStartYear
                && settings.AlternativeStartYear == this.AlternativeStartYear
                && settings.TechStartYear == this.TechStartYear
                && settings.AnalysisEndYear == this.AnalysisEndYear
                && settings.AllowCreditTrading == this.AllowCreditTrading
                && settings.CapOffCycleCosts == this.CapOffCycleCosts
                && settings.ExcludeBevFcvFromAnalysis == this.ExcludeBevFcvFromAnalysis

                // dynamic economic modeling settings
                && settings.DynamicEconomicModeling == this.DynamicEconomicModeling
                && settings.SalesEstimator == this.SalesEstimator
                && settings.ReferenceSalesEstimator == this.ReferenceSalesEstimator
                && settings.PriceElasticitySalesEstimatorMultiplier == this.PriceElasticitySalesEstimatorMultiplier
                && settings.DfsModel == this.DfsModel
                && settings.ReferenceDfsModel == this.ReferenceDfsModel
                && settings.PriceElasticityDfsModelCoefficient == this.PriceElasticityDfsModelCoefficient
                && settings.DfsSrAggregationMode == this.DfsSrAggregationMode
                && settings.DfsSrIterations == this.DfsSrIterations
                && settings.DisableDynamicScrappageModel == this.DisableDynamicScrappageModel
                && settings.DisableDynamicVmtModel == this.DisableDynamicVmtModel

                // assumed fuel savings settings
                && settings.AssumedPaybackMiles == this.AssumedPaybackMiles

                // effects modeling settings
                && settings.DisableEffectsModel == this.DisableEffectsModel
                && settings.FatalityRateEstimates == this.FatalityRateEstimates
                && settings.ConsumerBenefitsScale == this.ConsumerBenefitsScale
                && settings.CalcImplicitOpportunityCost == this.CalcImplicitOpportunityCost
                && settings.CalcCommercialOpportunityCost == this.CalcCommercialOpportunityCost
                && settings.CommercialShareLD == this.CommercialShareLD;
        }

        /// <summary>
        /// Summarizes the information about this <see cref="RuntimeSettings"/> instance to the specified
        /// <see cref="TextWriter"/>.
        /// </summary>
        public void WriteSummaryInfo(TextWriter writer)
        {
            if (writer == null) {
                return;
            }

            writer.WriteLine("--- Runtime Settings ---");

            // compliance modeling settings
            writer.WriteLine("  ComplianceProgram=" + this.ComplianceProgram);
            writer.WriteLine("  AnalysisStartYear=" + this.AnalysisStartYear);
            writer.WriteLine("  AlternativeStartYear=" + this.AlternativeStartYear);
            writer.WriteLine("  TechStartYear=" + this.TechStartYear);
            writer.WriteLine("  AnalysisEndYear=" + this.AnalysisEndYear);
            writer.WriteLine("  AllowCreditTrading=" + this.AllowCreditTrading);
            writer.WriteLine("  CapOffCycleCosts=" + this.CapOffCycleCosts);
            writer.WriteLine("  ExcludeBevFcvFromAnalysis=" + this.ExcludeBevFcvFromAnalysis);

            // dynamic economic modeling settings
            writer.WriteLine("  DynamicEconomicModeling=" + this.DynamicEconomicModeling);
            writer.WriteLine("  SalesEstimator=" + this.SalesEstimator);
            writer.WriteLine("  ReferenceSalesEstimator=" + this.ReferenceSalesEstimator);
            writer.WriteLine("  PriceElasticitySalesEstimatorMultiplier=" + this.PriceElasticitySalesEstimatorMultiplier);
            writer.WriteLine("  DfsModel=" + this.DfsModel);
            writer.WriteLine("  ReferenceDfsModel=" + this.ReferenceDfsModel);
            writer.WriteLine("  PriceElasticityDfsModelCoefficient=" + this.PriceElasticityDfsModelCoefficient);
            writer.WriteLine("  DfsSrAggregationMode=" + this.DfsSrAggregationMode);
            writer.WriteLine("  DfsSrIterations=" + this.DfsSrIterations);
            writer.WriteLine("  DisableDynamicScrappageModel=" + this.DisableDynamicScrappageModel);
            writer.WriteLine("  DisableDynamicVmtModel=" + this.DisableDynamicVmtModel);

            // assumed fuel savings settings
            writer.WriteLine("  AssumedPaybackMiles=" + this.AssumedPaybackMiles);

            // effects modeling settings
            writer.WriteLine("  DisableEffectsModel=" + this.DisableEffectsModel);
            writer.WriteLine("  FatalityRateEstimates=" + this.FatalityRateEstimates);
            writer.WriteLine("  ConsumerBenefitsScale=" + this.ConsumerBenefitsScale);
            writer.WriteLine("  CalcImplicitOpportunityCost=" + this.CalcImplicitOpportunityCost);
            writer.WriteLine("  CalcCommercialOpportunityCost=" + this.CalcCommercialOpportunityCost);
            writer.WriteLine("  CommercialShareLD=" + this.CommercialShareLD);

            writer.WriteLine();
        }

        #endregion
    }
}
