#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member

using System;
using System.Collections.Generic;

namespace Volpe.Cafe.Generic
{
    /// <summary>
    /// Represents a read-only object that stores modeling data as a continuous stream of annual values.
    /// </summary>
    /// <typeparam name="T">The type of elements in the stream of annual values.</typeparam>
    [Serializable]
    public class ReadOnlyAnnualValue<T> : IAnnualValue<T>
    {
        #region <<< Constructors >>>

        /// <summary>
        /// Initializes a new instance of the <see cref="ReadOnlyAnnualValue{T}"/> class using the specified
        /// parameters.
        /// </summary>
        /// <param name="minYear">The lowest year for which annual values are defined.</param>
        /// <param name="maxYear">The highest year for which annual values are defined.</param>
        /// <param name="values">
        ///     An list of annual values corresponding to the specified minYear and maxYear. If values is null, it will
        ///     be initialized based on the range of years defined by minYear and maxYear.
        /// </param>
        /// <param name="outOfBoundsMode">
        ///     Defines the way this instance will handle requested years that are out-of-bounds of the
        ///     <see cref="MinYear"/> and <see cref="MaxYear"/> range. See <see cref="OutOfBoundsMode"/> for more
        ///     information.
        /// </param>
        /// <exception cref="ArgumentException">
        ///     The specified minYear and maxYear do not correspond to the length of the values array.
        ///     -or-
        ///     maxYear cannot be less than minYear.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">maxYear and minYear cannot be less than zero.</exception>
        public ReadOnlyAnnualValue(int minYear, int maxYear, IList<T> values = null, int outOfBoundsMode = 0)
            : this(new AnnualValue<T>(minYear, maxYear, values, outOfBoundsMode)) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="ReadOnlyAnnualValue{T}"/> class that is a read-only wrapper
        /// around the specified annual values.
        /// </summary>
        /// <param name="values">The annual values to wrap.</param>
        /// <exception cref="ArgumentNullException">values is null.</exception>
        public ReadOnlyAnnualValue(IAnnualValue<T> values)
        {
            if (values == null) {
                throw new ArgumentNullException(nameof(values));
            }
            while (values is ReadOnlyAnnualValue<T> readOnly) {
                values = readOnly.Values;
            }
            this.Values = values;
        }

        #endregion

        #region <<< Properties >>>

        public T this[int year] => this.Values[year];

        public int MinYear => this.Values.MinYear;
        public int MaxYear => this.Values.MaxYear;

        public T MinValue => this.Values.MinValue;
        public T MaxValue => this.Values.MaxValue;

        public int OutOfBoundsMode => this.Values.OutOfBoundsMode;

        // wrapped annual values instance
        private IAnnualValue<T> Values { get; set; }

        #endregion

        #region <<< Methods >>>

        /// <summary>
        /// Returns the string representation of this <see cref="ReadOnlyAnnualValue{T}"/> instance.
        /// </summary>
        /// <returns>The string representation of the <see cref="ReadOnlyAnnualValue{T}"/> instance.</returns>
        public override string ToString()
        {
            return this.Values.ToString();
        }

        public bool HasValue(int year)
        {
            return this.Values.HasValue(year);
        }

        public IAnnualValue<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter, int? newOutOfBoundsMode)
        {
            var newValues = this.Values.ConvertAll(converter, newOutOfBoundsMode);
            return new ReadOnlyAnnualValue<TOutput>(newValues);
        }

        #endregion
    }
}
