#region <<< Using Directives >>>
using System;
using System.Collections.Generic;
using System.Linq;

using Volpe.Cafe.Generic;
using Volpe.Cafe.Initializers;
using Volpe.Cafe.Utils;
#endregion

namespace Volpe.Cafe.ModelSettings
{
    /// <summary>
    /// Provides information relating to a single compliance scenario.
    /// </summary>
    [Serializable]
    public sealed class Scenario
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Scenario"/> class.
        /// </summary>
        public Scenario(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);

            // calc additional read-only properties not defined in the values dict
            this.StandardSettingYears = new RCAltValue<ModelYearKey>();
            foreach (var regClass in RegClasses.AltList) {
                var scenInfo = this.ScenInfo[regClass];
                var standardSettingYears = ModelYearKey.BuildKey(
                    scenInfo.Where(item => item.StandardSettingYear)
                            .Select(item => item.ModelYear)
                            .ToArray());

                standardSettingYears.SetRange(endYear: this.MinMY, state: scenInfo[0].StandardSettingYear);
                standardSettingYears.SetRange(startYear: this.MaxMY, state: scenInfo.Last().StandardSettingYear);

                this.StandardSettingYears[regClass] = standardSettingYears;
            }
        }

        /// <summary>
        /// Gets the compliance scenario attributes for the specified regulatory class and model year.
        /// </summary>
        /// <remarks>
        /// If the specified <paramref name="regClass"/> is not supported, a value of <c>null</c> will be returned.
        /// If the specified model <paramref name="year"/> is out of bounds, the scenario info for either the first or
        /// the last available model year will be returned.
        /// </remarks>
        public ScenarioInfo this[RegulatoryClass regClass, int year]
        {
            get {
                int rcIndex = regClass.GetAltListIndex();
                var scenInfo = (rcIndex == -1) ? null : this.ScenInfo.Items[rcIndex];
                if (scenInfo == null) {
                    return null;
                }

                int myIndex = Math.Max(this.MinMY, Math.Min(this.MaxMY, year)) - this.MinMY;
                return this.ScenInfo[regClass][myIndex];
            }
        }

        /// <summary>Gets the index of the compliance scenario.</summary>
        public int Index { get; private set; }
        /// <summary>Gets whether the compliance scenario is the baseline.</summary>
        public bool IsBaseline => this.Index == 0;
        /// <summary>Gets the description of the compliance scenario.</summary>
        public string Description { get; private set; }

        /// <summary>Gets the lowest model year for which scenario attributes are defined.</summary>
        public int MinMY { get; private set; }
        /// <summary>Gets the highest model year for which scenario attributes are defined.</summary>
        public int MaxMY { get; private set; }

        /// <summary>
        /// Gets whether to assign secondary regulatory classification to the light duty vehicles during a given year.
        /// </summary>
        public IAnnualValue<bool> ReclassifyLightDutyVehicles { get; private set; }

        /// <summary>
        /// Gets the compliance scenario attributes for each regulatory class and model year.
        /// </summary>
        private RCAltValue<List<ScenarioInfo>> ScenInfo { get; set; }
        /// <summary>
        /// Gets the model years designated as standard setting years for each regulatory class.
        /// </summary>
        private RCAltValue<ModelYearKey> StandardSettingYears { get; }

        /// <summary>
        /// Returns the string representation of this <see cref="Scenario"/> instance.
        /// </summary>
        /// <returns>The string representation of the <see cref="Scenario"/> instance.</returns>
        public override string ToString()
        {
            return $"{this.Index}. {StringHelper.GetTitleCase(this.Description)}";
        }

        /// <summary>
        /// Returns the string representation of this <see cref="Scenario"/> instance.
        /// </summary>
        /// <param name="descrLength">
        ///     Specifies the maximum number of characters to return from the description field.
        /// </param>
        /// <param name="showIndex">true, to show the scenario index; false, otherwise.</param>
        /// <returns>The string representation of the <see cref="Scenario"/> instance.</returns>
        public string ToString(int descrLength, bool showIndex)
        {
            int len = this.Description.Length;
            bool outOfBounds = (descrLength < len);
            descrLength = (outOfBounds) ? descrLength - 3 : len;
            string descr = StringHelper.GetTitleCase(this.Description.Substring(0, descrLength));
            if (outOfBounds) {
                descr += " ...";
            }

            return (showIndex) ? $"{this.Index}. {descr}" : descr;
        }

        /// <summary>
        /// Returns the model years designated as standard setting years for the specified regulatory class.
        /// </summary>
        /// <remarks>
        /// If the specified <paramref name="regClass"/> is not supported, an <see cref="ModelYearKey.Empty"/> value
        /// will be returned.
        /// </remarks>
        public ModelYearKey GetStandardSettingYears(RegulatoryClass regClass)
        {
            int rcIndex = regClass.GetAltListIndex();
            return (rcIndex == -1)
                ? ModelYearKey.Empty
                : this.StandardSettingYears.Items[rcIndex];
        }

    }
}
