using System;
using Volpe.Cafe.Generic;

namespace Volpe.Cafe.EffectsData.Collections
{
    /// <summary>
    /// Provides a collection of consumer costs arising from the various modeling effects, separated by regulatory
    /// class.
    /// <br/>
    /// This object supports and operates on all regulatory classes as defined by <see cref="RegClasses.AltList"/>.
    /// </summary>
    [Serializable]
    public sealed class ConsumerCostsCollection
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ConsumerCostsCollection"/> class.
        /// </summary>
        /// <param name="discRate">
        ///     The discount rate at which the consumer costs are discounted, or 0, if the costs are undiscounted.
        /// </param>
        public ConsumerCostsCollection(double discRate)
        {
            this.DiscountRate = discRate;
            this.Values = new RCAltValue<ConsumerCosts>(() => new ConsumerCosts());
        }

        /// <summary>
        /// Gets the consumer costs for the specified regulatory class. For aggregate classes, the payback and payback
        /// TCO values will be computed as sales-weighted averages based on the constituent regulatory classes.
        /// </summary>
        public ConsumerCosts this[RegulatoryClass regClass]
        {
            get {
                // "aggregate" classes
                if (regClass == RegulatoryClass.All || regClass == RegulatoryClass.AllLightDuty) {
                    var costs = this.Values.GetValue(regClass);

                    // paybacks are "averages" and are not auto computed
                    // we need to do that manually for any aggregate reg-class
                    double sales = 0;
                    double pbSum = 0;
                    double tcoSum = 0;

                    foreach (var rc in RegClasses.AltList) {
                        if (regClass.Contains(rc)) {
                            var rcCosts = this.Values[rc];
                            sales += rcCosts.Sales;
                            pbSum += rcCosts.Sales * rcCosts.Payback;
                            tcoSum += rcCosts.Sales * rcCosts.PaybackTCO;
                        }
                    }

                    costs.Payback = (sales == 0) ? 0 : pbSum / sales;
                    costs.PaybackTCO = (sales == 0) ? 0 : tcoSum / sales;

                    return costs;
                }

                // "individual" classes
                return this[regClass.GetAltListIndex()];
            }
        }

        /// <summary>
        /// Gets the consumer costs for the regulatory class associated with the specified
        /// <paramref name="regClassIndex"/>.
        /// </summary>
        public ConsumerCosts this[int regClassIndex] => this.Values.Items[regClassIndex];

        /// <summary>
        /// Gets the discount rate at which the consumer costs are discounted, or 0, if the costs are undiscounted.
        /// </summary>
        public double DiscountRate { get; }

        /// <summary>
        /// Gets the underlying values of consumer costs for each regulatory class.
        /// </summary>
        private RCAltValue<ConsumerCosts> Values { get; }

        /// <summary>
        /// Resets all consumer costs to zero.<br/>
        /// <b>NOTE</b>: The discount rate is not affected.
        /// </summary>
        public void Clear()
        {
            this.Values.Clear(item => item.Clear());
        }

        /// <summary>
        /// Appends all values from the specified <see cref="ConsumerCostsCollection"/> object to this instance.<br/>
        /// <b>NOTE</b>: The discount rate and payback periods are not appended.
        /// </summary>
        public void Append(ConsumerCostsCollection value)
        {
            this.Values.Append(value.Values);
        }

    }
}
