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

using Volpe.Cafe.Initializers;
using Volpe.Cafe.MarketData.Design;

using TC = Volpe.Cafe.TechnologyClasses;
using TI = Volpe.Cafe.TechnologyIndexes;
#endregion

namespace Volpe.Cafe.ModelSettings
{
    /// <summary>
    /// Provides information relating to a single vehicle technology.
    /// </summary>
    [Serializable]
    public sealed class TechnologyInfo
    {
        #region <<< Constructors >>>

        /// <summary>
        /// Initializes a new instance of the <see cref="TechnologyInfo"/> class.
        /// </summary>
        public TechnologyInfo(Dictionary<string, object> values)
        {
            DataObjectInitializer.SetPropertyValues(this, values);

            // assign index manually (not auto-initialized)
            this.Index = TI.GetIndex(this.Name);
        }

        #endregion

        #region <<< Variables >>>

        private int _minYearAvailable = -1;
        private int _maxYearRetired = -1;

        #endregion

        #region <<< Properties >>>

        //----- general tech info -----
        /// <summary>Gets the name of the technology.</summary>
        public string Name { get; private set; }
        /// <summary>Gets the index of the technology.</summary>
        public int Index { get; }

        //----- phase-in info -----
        /// <summary>
        /// Gets the percentage of the entire fleet to which the current technology can be applied, per model year.
        /// </summary>
        public double PhaseInCap { get; private set; }
        /// <summary>Gets the reference year for accumulating phase-in caps.</summary>
        public int PhaseInStartYear { get; private set; }

        //----- zev credits -----
        /// <summary>
        /// Gets the amount of ZEV credits that a vehicle generates as a result of applying the current technology,
        /// applicable to the light duty fleet up to and including MY-2025.
        /// </summary>
        public double ZevCreditsMY2025 { get; private set; }
        /// <summary>
        /// Gets the amount of ZEV credits that a vehicle generates as a result of applying the current technology,
        /// applicable to the light duty fleet starting in MY-2026.
        /// </summary>
        public double ZevCreditsMY2026 { get; private set; }
        /// <summary>
        /// Gets the amount of ZEV credits that a vehicle generates as a result of applying the current technology,
        /// applicable to the HDPUV fleet in all model years.
        /// </summary>
        public double ZevCreditsHDPUV { get; private set; }

        //----- aggregated tech info across all tech classes -----        
        /// <summary>Gets whether the current technology can be used with any technology class.</summary>
        public bool IsApplicableToAny => this.Attributes.Any(item => item.Applicable);

        /// <summary>
        /// Gets the lowest model year when the technology becomes available for applicability across all technology
        /// classes.
        /// </summary>
        public int MinYearAvailable
        {
            get {
                if (this._minYearAvailable == -1) {
                    var filteredList = this.Attributes.Where(item => item.YearAvailable > 0);
                    this._minYearAvailable = (filteredList.Count() == 0)
                                           ? 0
                                           : filteredList.Min(item => item.YearAvailable);
                }
                return this._minYearAvailable;
            }
        }

        /// <summary>
        /// Gets the highest model year when the technology is last available (or retired) for applicability across all
        /// technology classes. If at least one technology class does not define a year retired value, this property
        /// returns zero. A return value of zero indicates that the technology never retires.
        /// </summary>
        public int MaxYearRetired
        {
            get {
                if (this._maxYearRetired == -1) {
                    var filteredList = this.Attributes.Where(item => item.YearRetired > 0);
                    this._maxYearRetired = (filteredList.Count() != TC.List.Length)
                                         ? 0
                                         : filteredList.Max(item => item.YearRetired);
                }
                return this._maxYearRetired;
            }
        }

        //----- class-specific tech info -----
        private TechnologyAttributes[] Attributes { get; set; }
        private TechnologyCosts[] VehicleCosts { get; set; }
        private TechnologyCosts[] EngineCosts { get; set; }

        #endregion

        #region <<< Methods >>>

        /// <summary>
        /// Returns the string representation of this <see cref="TechnologyInfo"/> instance.
        /// </summary>
        /// <returns>The string representation of the <see cref="TechnologyInfo"/> instance.</returns>
        public override string ToString()
        {
            return $"{{{this.Index:00}-{this.Name}}}";
        }

        /// <summary>
        /// Returns the total phase-in cap value for the specified model year.
        /// </summary>
        public double GetPhaseIn(int year)
        {
            return Math.Min(this.PhaseInCap * Math.Max(year - this.PhaseInStartYear + 1, 0), 1);
        }

        /// <summary>
        /// Returns the amount of ZEV credits associated with the current technology, applicable to the specified
        /// vehicle and in a given model year.
        /// </summary>
        public double GetZevCredits(IVehicleInfo vehInfo, int year)
        {
            return vehInfo.RegClass.IsHDPUV() ? this.ZevCreditsHDPUV
                 : (year <= 2025) ? this.ZevCreditsMY2025
                 : this.ZevCreditsMY2026;
        }

        //----- get attributes/costs -----
        /// <summary>
        /// Returns the attributes associated with the current technology, applicable to the specified vehicle.
        /// </summary>
        public TechnologyAttributes GetAttributes(IVehicleInfo vehInfo)
        {
            return this.Attributes[vehInfo.TechnologyClass.GetArrayIndex()];
        }

        /// <summary>
        /// Returns the total cost associated with the current technology, applicable to the specified vehicle and in a
        /// given model year.
        /// </summary>
        public double GetTechCost(IVehicleInfo vehInfo, int year)
        {
            return this.GetVehicleCost(vehInfo, year) + this.GetEngineCost(vehInfo, year);
        }
        /// <summary>
        /// Returns the non-engine portion of the cost associated with the current technology, applicable to the
        /// specified vehicle and in a given model year.
        /// </summary>
        public double GetVehicleCost(IVehicleInfo vehInfo, int year)
        {
            return this.VehicleCosts[vehInfo.TechnologyClass.GetArrayIndex()].GetCost(year);
        }
        /// <summary>
        /// Returns the engine specific portion of the cost associated with the current technology, applicable to the
        /// specified vehicle and in a given model year.
        /// </summary>
        public double GetEngineCost(IVehicleInfo vehInfo, int year)
        {
            return this.EngineCosts[vehInfo.EngineTechnologyClass.GetArrayIndex()].GetCost(year);
        }

        /// <summary>
        /// Returns the maintenance and repair cost associated with the current technology, applicable to the specified
        /// vehicle and in a given model year.
        /// </summary>
        public double GetMaintRepairCost(IVehicleInfo vehInfo, int year)
        {
            return this.VehicleCosts[vehInfo.TechnologyClass.GetArrayIndex()].GetMaintRepairCost(year)
                 + this.EngineCosts[vehInfo.EngineTechnologyClass.GetArrayIndex()].GetMaintRepairCost(year);
        }

        /// <summary>
        /// Returns the battery learning rate associated with the current technology, applicable to the specified
        /// vehicle and in a given model year.
        /// </summary>
        public double GetBatteryLearningRate(IVehicleInfo vehInfo, int year)
        {
            return this.VehicleCosts[vehInfo.TechnologyClass.GetArrayIndex()].GetBatteryLearningRate(year);
        }

        #endregion
    }
}
