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

namespace Volpe.Cafe.MarketData
{
    /// <summary>
    /// Defines the aggregated vehicle-level statistics, stored by regulatory class.
    /// </summary>
    [Serializable]
    public sealed class RCVehicleStatistics : RCValue<VehicleStatistics>, ICloneable
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="RCVehicleStatistics"/> class.
        /// </summary>
        public RCVehicleStatistics() : base(() => new VehicleStatistics()) { }

        /// <summary>
        /// Gets or sets the compliance modeling data value based on the given regulatory class.
        /// </summary>
        /// <exception cref="IndexOutOfRangeException">
        ///     The value specified does not represent a valid regulatory class.
        /// </exception>
        public new VehicleStatistics this[RC regClass]
        {
            get {
                switch (regClass) {
                    case RC.All            : return this.Total;
                    case RC.AllLightDuty   : return this.TotalLD;
                    case RC.AllPassengerCar: return this.TotalPC;

                    default:
                        return base[regClass];
                }
            }
            set {
                base[regClass] = value;
            }
        }

        /// <summary>Gets the sum of the compliance modeling data of all passenger regulatory classes.</summary>
        private VehicleStatistics TotalPC => this.CombineVehStats(RC.DomesticCar, RC.ImportedCar);
        /// <summary>Gets the sum of the compliance modeling data of all light duty regulatory classes.</summary>
        private VehicleStatistics TotalLD => this.CombineVehStats(RC.DomesticCar, RC.ImportedCar, RC.LightTruck);
        /// <summary>Gets the sum of the compliance modeling data of all regulatory classes.</summary>
        private VehicleStatistics Total => this.CombineVehStats(this.Items);

        /// <summary>
        /// Resets all vehicle statistics to zero.
        /// </summary>
        public void Clear()
        {
            this.Clear(item => item.Clear());
        }

        /// <summary>
        /// Appends all values from the specified <see cref="RCVehicleStatistics"/> object to this instance.
        /// </summary>
        /// <param name="value">The <see cref="RCVehicleStatistics"/> object whose values to append to this
        ///     instance.</param>
        public void Append(RCVehicleStatistics value)
        {
            for (int i = 0; i < this.Items.Length; i++) {
                this.Items[i].Append(value.Items[i]);
            }
        }

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

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

        private VehicleStatistics CombineVehStats(params RC[] regClasses)
        {
            VehicleStatistics total = new VehicleStatistics();
            foreach (RC rc in regClasses) {
                total.Append(this[rc]);
            }
            return total;
        }
        private VehicleStatistics CombineVehStats(params VehicleStatistics[] stats)
        {
            VehicleStatistics total = new VehicleStatistics();
            for (int i = 0; i < stats.Length; i++) {
                total.Append(stats[i]);
            }
            return total;
        }

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

    }
}
