using System;

namespace Volpe.Cafe.EffectsData
{
    /// <summary>
    /// Provides modeling effects related to safety and adverse health impacts associated with vehicle operation.
    /// </summary>
    [Serializable]
    public sealed class SafetyEffects : IAppendable
    {
        /// <summary>
        /// Gets or sets the total fatalities due to the reduction in vehicle weight, additional VMT, and changes in
        /// fleet age distribution.
        /// </summary>
        public double FatalitiesOverall { get; set; }
        /// <summary>
        /// Gets or sets the vehicle fatalities due to increases in rebound VMT.
        /// </summary>
        public double FatalitiesRebound { get; set; }
        /// <summary>
        /// Gets or sets the vehicle fatalities due to reduction in vehicle curb weight.
        /// </summary>
        public double FatalitiesDeltaCW { get; set; }

        /// <summary>
        /// Gets or sets the total non-fatal injuries due to the reduction in vehicle weight, additional VMT, and
        /// changes in fleet age distribution.
        /// </summary>
        public double NonFatalInjuriesOverall { get; set; }
        /// <summary>
        /// Gets or sets the vehicle non-fatal injuries due to increases in rebound VMT.
        /// </summary>
        public double NonFatalInjuriesRebound { get; set; }
        /// <summary>
        /// Gets or sets the vehicle non-fatal injuries due to reduction in vehicle curb weight.
        /// </summary>
        public double NonFatalInjuriesDeltaCW { get; set; }

        /// <summary>
        /// Gets or sets the total property damage crashes due to the reduction in vehicle weight, additional VMT, and
        /// changes in fleet age distribution.
        /// </summary>
        public double PropertyDamageCrashesOverall { get; set; }
        /// <summary>
        /// Gets or sets the vehicle property damage crashes due to increases in rebound VMT.
        /// </summary>
        public double PropertyDamageCrashesRebound { get; set; }
        /// <summary>
        /// Gets or sets the vehicle property damage crashes due to reduction in vehicle curb weight.
        /// </summary>
        public double PropertyDamageCrashesDeltaCW { get; set; }

        /// <summary>
        /// Gets the health impacts associated with upstream emission damage arising from various pollutants (from all
        /// stages of fuel production and distribution).
        /// </summary>
        /// <remarks>
        /// Each element in the array is associated with one of the <see cref="EmissionHealthImpact"/>s, where each
        /// array index corresponds to the health impact defined by <see cref="HealthImpacts.List"/>.
        /// </remarks>
        public double[] UpstreamHealthImpacts { get; } = new double[HealthImpacts.List.Length];

        /// <summary>
        /// Gets the health impacts associated with tailpipe emission damage arising from various pollutants (from
        /// vehicle operation).
        /// </summary>
        /// <remarks>
        /// Each element in the array is associated with one of the <see cref="EmissionHealthImpact"/>s, where each
        /// array index corresponds to the health impact defined by <see cref="HealthImpacts.List"/>.
        /// </remarks>
        public double[] TailpipeHealthImpacts { get; } = new double[HealthImpacts.List.Length];

        /// <summary>
        /// Returns the total health impacts associated with upstream and tailpipe emission damage arising from various
        /// pollutants (upstream: all stages of fuel production and distribution; and tailpipe: from vehicle operation)
        /// for the specified emission health impact value.
        /// </summary>
        /// <param name="healthImpact">
        ///     The health impact value for which to obtain the total emission health impacts.
        /// </param>
        public double GetTotalHealthImpacts(EmissionHealthImpact healthImpact)
        {
            var upstream = this.UpstreamHealthImpacts[(int)healthImpact];
            var tailpipe = this.TailpipeHealthImpacts[(int)healthImpact];

            return upstream + tailpipe;
        }

        /// <summary>
        /// Resets all safety effects values to zero.
        /// </summary>
        public void Clear()
        {
            this.FatalitiesOverall = 0;
            this.FatalitiesRebound = 0;
            this.FatalitiesDeltaCW = 0;

            this.NonFatalInjuriesOverall = 0;
            this.NonFatalInjuriesRebound = 0;
            this.NonFatalInjuriesDeltaCW = 0;

            this.PropertyDamageCrashesOverall = 0;
            this.PropertyDamageCrashesRebound = 0;
            this.PropertyDamageCrashesDeltaCW = 0;

            Array.Clear(this.UpstreamHealthImpacts, 0, this.UpstreamHealthImpacts.Length);
            Array.Clear(this.TailpipeHealthImpacts, 0, this.TailpipeHealthImpacts.Length);
        }

        /// <summary>
        /// Appends all values from the specified <see cref="SafetyEffects"/> object to this instance.
        /// </summary>
        public void Append(SafetyEffects value)
        {
            this.FatalitiesOverall += value.FatalitiesOverall;
            this.FatalitiesRebound += value.FatalitiesRebound;
            this.FatalitiesDeltaCW += value.FatalitiesDeltaCW;

            this.NonFatalInjuriesOverall += value.NonFatalInjuriesOverall;
            this.NonFatalInjuriesRebound += value.NonFatalInjuriesRebound;
            this.NonFatalInjuriesDeltaCW += value.NonFatalInjuriesDeltaCW;

            this.PropertyDamageCrashesOverall += value.PropertyDamageCrashesOverall;
            this.PropertyDamageCrashesRebound += value.PropertyDamageCrashesRebound;
            this.PropertyDamageCrashesDeltaCW += value.PropertyDamageCrashesDeltaCW;

            for (int i = 0; i < this.UpstreamHealthImpacts.Length; i++) {
                this.UpstreamHealthImpacts[i] += value.UpstreamHealthImpacts[i];
                this.TailpipeHealthImpacts[i] += value.TailpipeHealthImpacts[i];
            }
        }

        //----- explicit interface implementation -----
        void IAppendable.Append(IAppendable value)
        {
            this.Append((SafetyEffects)value);
        }

    }
}
