using System;
using System.Linq;
using RC = Volpe.Cafe.RegulatoryClass;

namespace Volpe.Cafe.MarketData
{
    /// <summary>
    /// Defines the manufacturer-level (or industry-level) compliance statistics.
    /// </summary>
    [Serializable]
    public class ComplianceStatistics : ICloneable
    {
        #region <<< Constructors >>>

        /// <summary>
        /// Initializes a new instance of the <see cref="ComplianceStatistics"/> class.
        /// </summary>
        public ComplianceStatistics() { }

        #endregion

        #region <<< Properties >>>

        //----- running sums -----
        /// <summary>Gets the aggregated vehicle-level statistics.</summary>
        public RCVehicleStatistics VehicleStats { get; private set; } = new RCVehicleStatistics();

        //----- CAFE values by reg-class (DC,IC,LT,HDPUV) -----
        /// <summary>
        /// Gets the preliminary CAFE standard, in miles per gallon (mpg), for the manufacturer.
        /// </summary>
        public RCDouble PStandard { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the CAFE standard, in miles per gallon (mpg), for the manufacturer.
        /// </summary>
        public RCDouble Standard { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the CAFE rating for the manufacturer, aggregated from unadjusted 2-cycle vehicle fuel economy values.
        /// </summary>
        public RCDouble CAFE2Cycle { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the CAFE rating for the manufacturer (the Corporate Average Fuel Economy rating).
        /// </summary>
        public RCDouble CAFERating { get; private set; } = new RCDouble();

        /// <summary>
        /// Gets the adjustment factor associated with improvements in air conditioning efficiency accrued by a
        /// manufacturer toward compliance with NHTSA's CAFE standards.
        /// </summary>
        public RCDouble AcEfficiency { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the amount of off-cycle credits accrued by a manufacturer toward compliance with NHTSA's CAFE
        /// standards.
        /// </summary>
        public RCDouble OffCycle { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the amount of full-size pickup credits accrued by a manufacturer toward compliance with NHTSA's CAFE
        /// standards.
        /// </summary>
        public RCDouble FSPCredits { get; private set; } = new RCDouble();

        /// <summary>
        /// Gets the CAFE credits accumulated by the manufacturer in each regulatory class.
        /// </summary>
        public RCDouble Credits { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the CAFE credits (denominated in thousands of gallons) accumulated by the manufacturer in each
        /// regulatory class.
        /// </summary>
        public RCDouble CreditsKGal { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the value of CAFE credits accumulated by the manufacturer in each regulatory class.
        /// </summary>
        public RCDouble CreditValue { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the CAFE fines owed by the manufacturer in each regulatory class.
        /// </summary>
        public RCDouble Fines { get; private set; } = new RCDouble();

        /// <summary>
        /// Gets the total transferable credits transferred into the manufacturer, per regulatory class, which are
        /// subject to transfer caps (for example, fleet transfers are subject to transfer caps; however, carry forward
        /// and carry backward are not).
        /// </summary>
        public RCDouble CreditsInCapped { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the total transferable credits transferred into the manufacturer, per regulatory class.
        /// </summary>
        public RCDouble CreditsIn { get; private set; } = new RCDouble();
        /// <summary>
        /// Gets the total transferable credits transferred out of the manufacturer, per regulatory class.
        /// </summary>
        public RCDouble CreditsOut { get; private set; } = new RCDouble();

        //----- CO2 values by combined reg-class (PC,LT,HDPUV) -----
        /// <summary>
        /// Gets the CO-2 standard, in grams per mile of CO-2, for the manufacturer.
        /// </summary>
        public RCAltDouble CO2Standard { get; private set; } = new RCAltDouble();
        /// <summary>
        /// Gets the CO-2 rating for the manufacturer.
        /// </summary>
        public RCAltDouble CO2Rating { get; private set; } = new RCAltDouble();

        /// <summary>
        /// Gets the adjustment factor associated with improvements in air conditioning efficiency accrued by a
        /// manufacturer toward compliance with EPA's CO2 standards. 
        /// </summary>
        public RCAltDouble CO2AcEfficiency { get; private set; } = new RCAltDouble();
        /// <summary>
        /// Gets the adjustment factor associated with improvements in air conditioning leakage accrued by a
        /// manufacturer toward compliance with EPA's CO2 standards. 
        /// </summary>
        public RCAltDouble CO2AcLeakage { get; private set; } = new RCAltDouble();
        /// <summary>
        /// Gets the amount of off-cycle credits accrued by a manufacturer toward compliance with EPA's CO-2 standards.
        /// </summary>
        public RCAltDouble CO2OffCycle { get; private set; } = new RCAltDouble();
        /// <summary>
        /// Gets the amount of full-size pickup credits accrued by a manufacturer toward compliance with NHTSA's CAFE
        /// standards.
        /// </summary>
        public RCAltDouble CO2FSPCredits { get; private set; } = new RCAltDouble();
        
        /// <summary>
        /// Gets the CO-2 credits accumulated by the manufacturer in each regulatory class.
        /// </summary>
        public RCAltDouble CO2Credits { get; private set; } = new RCAltDouble();
        /// <summary>
        /// Gets the value of CO-2 credits accumulated by the manufacturer in each regulatory class.
        /// </summary>
        public RCAltDouble CO2CreditValue { get; private set; } = new RCAltDouble();

        /// <summary>
        /// Gets the total transferable credits transferred into the manufacturer, per regulatory class.
        /// </summary>
        public RCAltDouble CO2CreditsIn { get; private set; } = new RCAltDouble();
        /// <summary>
        /// Gets the total transferable credits transferred out of the manufacturer, per regulatory class.
        /// </summary>
        public RCAltDouble CO2CreditsOut { get; private set; } = new RCAltDouble();

        #endregion

        #region <<< Methods >>>

        /// <summary>
        /// Sets the CAFE-related compliance values for the specified regulatory class.
        /// </summary>
        /// <exception cref="IndexOutOfRangeException">
        ///     Specified <paramref name="regClass"/> is not applicable for setting CAFE-related values.
        /// </exception>
        public void SetCAFEComplianceValues(RC regClass,
                                            double pStandard, double standard, double cafe2Cycle, double cafeRating,
                                            double credits, double creditsKGal, double creditValue, double fines,
                                            OffCycleValues offCycleCredits)
        {
            this.PStandard   [regClass] = pStandard;
            this.Standard    [regClass] = standard;
            this.CAFE2Cycle  [regClass] = cafe2Cycle;
            this.CAFERating  [regClass] = cafeRating;
            this.AcEfficiency[regClass] = offCycleCredits.AcEfficiency;
            this.OffCycle    [regClass] = offCycleCredits.OffCycle;
            this.FSPCredits  [regClass] = offCycleCredits.FullSizePickup; 
            this.Credits     [regClass] = credits;
            this.CreditsKGal [regClass] = creditsKGal;
            this.CreditValue [regClass] = creditValue;
            this.Fines       [regClass] = fines;
        }

        /// <summary>
        /// Sets the CO2-related compliance values for the specified regulatory class.
        /// </summary>
        /// <exception cref="IndexOutOfRangeException">
        ///     Specified <paramref name="regClass"/> is not applicable for setting CO2-related values.
        /// </exception>
        public void SetCO2ComplianceValues(RC regClass,
                                           double co2Standard, double co2Rating,
                                           double co2Credits, double co2CreditValue,
                                           OffCycleValues offCycleCredits)
        {
            this.CO2Standard    [regClass] = co2Standard;
            this.CO2Rating      [regClass] = co2Rating;
            this.CO2AcEfficiency[regClass] = offCycleCredits.AcEfficiency;
            this.CO2AcLeakage   [regClass] = offCycleCredits.AcLeakage;
            this.CO2OffCycle    [regClass] = offCycleCredits.OffCycle;
            this.CO2FSPCredits  [regClass] = offCycleCredits.FullSizePickup;
            this.CO2Credits     [regClass] = co2Credits;
            this.CO2CreditValue [regClass] = co2CreditValue;
        }

        /// <summary>
        /// Returns an <see cref="RCDouble"/> instance that contains the accumulated sales volumes for one or more
        /// vehicle models.
        /// </summary>
        public RCDouble GetSales()
        {
            RCDouble sales = RCDouble.Zero;
            for (int i = 0; i < this.VehicleStats.Items.Length; i++) {
                sales.Items[i] = this.VehicleStats.Items[i].Sales;
            }
            return sales;
        }
        /// <summary>
        /// Returns the accumulated sales volumes for one or more vehicle models for the specified regulatory class.
        /// </summary>
        /// <param name="regClass">The regulatory class for which to obtain the sales volume.</param>
        /// <returns>
        /// The sales volume for the specified regulatory class.
        /// </returns>
        public double GetSales(RC regClass)
        {
            return this.GetSales()[regClass];
        }

        /// <summary>
        /// Returns the net CAFE credit balance for the specified regulatory class.
        /// </summary>
        /// <param name="regClass">The regulatory class for which to obtain the net CAFE credit balance.</param>
        /// <remarks>
        /// Net credits (for either CAFE or CO-2) are defined as the sum of credits earned by and transferred into the
        /// manufacturer, minus the credits transferred out of the manufacturer.
        /// </remarks>
        public double GetNetCredits(RC regClass)
        {
            return this.Credits[regClass] + this.CreditsIn[regClass] - this.CreditsOut[regClass];
        }
        /// <summary>
        /// Returns an <see cref="RCDouble"/> that contains the net CAFE credit balance for each regulatory class.
        /// </summary>
        /// <inheritdoc cref="GetNetCredits(RC)" path="/remarks"/>
        public RCDouble GetNetCredits()
        {
            return this.Credits + this.CreditsIn - this.CreditsOut;
        }

        /// <summary>
        /// Returns the net CO-2 credit balance for the specified regulatory class.
        /// </summary>
        /// <param name="regClass">The regulatory class for which to obtain the net CO-2 credit balance.</param>
        /// <inheritdoc cref="GetNetCredits(RC)" path="/remarks"/>
        public double GetNetCO2Credits(RC regClass)
        {
            return this.CO2Credits[regClass] + this.CO2CreditsIn[regClass] - this.CO2CreditsOut[regClass];
        }
        /// <summary>
        /// Returns an <see cref="RCAltDouble"/> that contains the net CO-2 credit balance for each regulatory class.
        /// </summary>
        /// <inheritdoc cref="GetNetCredits(RC)" path="/remarks"/>
        public RCAltDouble GetNetCO2Credits()
        {
            return this.CO2Credits + this.CO2CreditsIn - this.CO2CreditsOut;
        }

        /// <summary>
        /// Copies the elements of this <see cref="ComplianceStatistics"/> instance to the specified value. 
        /// </summary>
        /// <param name="value">The value that is the destination of the copy operation.</param>
        public void CopyTo(ComplianceStatistics value)
        {
            // vehicle stats
            for (int i = 0; i < RegClasses.List.Length; i++) {
                this.VehicleStats.Items[i].CopyTo(value.VehicleStats.Items[i]);
            }

            // CAFE values
            for (int i = 0; i < RegClasses.List.Length; i++) {
                value.PStandard   .Items[i] = this.PStandard   .Items[i];
                value.Standard    .Items[i] = this.Standard    .Items[i];
                value.CAFE2Cycle  .Items[i] = this.CAFE2Cycle  .Items[i];
                value.CAFERating  .Items[i] = this.CAFERating  .Items[i];
                value.AcEfficiency.Items[i] = this.AcEfficiency.Items[i];
                value.OffCycle    .Items[i] = this.OffCycle    .Items[i];
                value.FSPCredits  .Items[i] = this.FSPCredits  .Items[i];
                value.Credits     .Items[i] = this.Credits     .Items[i];
                value.CreditsKGal .Items[i] = this.CreditsKGal .Items[i];
                value.CreditValue .Items[i] = this.CreditValue .Items[i];
                value.Fines       .Items[i] = this.Fines       .Items[i];

                // credit trades
                value.CreditsInCapped.Items[i] = this.CreditsInCapped.Items[i];
                value.CreditsIn      .Items[i] = this.CreditsIn      .Items[i];
                value.CreditsOut     .Items[i] = this.CreditsOut     .Items[i];
            }

            // CO-2 values
            for (int i = 0; i < RegClasses.AltList.Length; i++) {
                value.CO2Standard    .Items[i] = this.CO2Standard    .Items[i];
                value.CO2Rating      .Items[i] = this.CO2Rating      .Items[i];
                value.CO2AcEfficiency.Items[i] = this.CO2AcEfficiency.Items[i];
                value.CO2AcLeakage   .Items[i] = this.CO2AcLeakage   .Items[i];
                value.CO2OffCycle    .Items[i] = this.CO2OffCycle    .Items[i];
                value.CO2FSPCredits  .Items[i] = this.CO2FSPCredits  .Items[i];
                value.CO2Credits     .Items[i] = this.CO2Credits     .Items[i];
                value.CO2CreditValue .Items[i] = this.CO2CreditValue .Items[i];

                // credit trades
                value.CO2CreditsIn .Items[i] = this.CO2CreditsIn .Items[i];
                value.CO2CreditsOut.Items[i] = this.CO2CreditsOut.Items[i];
            }
        }

        /// <summary>
        /// Creates a new object that is a copy of the current <see cref="ComplianceStatistics"/> instance.
        /// </summary>
        /// <returns>A new object that is a copy of this <see cref="ComplianceStatistics"/>.</returns>
        public ComplianceStatistics Clone()
        {
            var value = new ComplianceStatistics();
            this.CopyTo(value);
            return value;
        }

        //----- explicit interface implementation -----
        object ICloneable.Clone()
        {
            return this.Clone();
        }

        #endregion
    }
}
