using System;
using Volpe.Cafe.EffectsData;
using Volpe.Cafe.Generic;
using Volpe.Cafe.MarketData.Design;

namespace Volpe.Cafe.Reporting
{
    /// <summary>
    /// Represents an object that encapsulates modeling and effects data to report on.
    /// </summary>
    [Serializable]
    public class ReportingData
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ReportingData"/> class, using the specified compliance and
        /// effects modeling data for the baseline and alternative scenarios.
        /// </summary>
        /// <param name="baseData">The compliance modeling data for the baseline scenario.</param>
        /// <param name="baseEffectsData">The effects modeling data for the baseline scenario.</param>
        /// <param name="scenData">
        ///     The compliance modeling data for the action alternative scenario.<br/>
        ///     Specify <c>null</c> if the reporting data encapsulates the baseline scenario only.
        /// </param>
        /// <param name="scenEffectsData">
        ///     The effects modeling data for the action alternative scenario.<br/>
        ///     Specify <c>null</c> if the reporting data encapsulates the baseline scenario only.
        /// </param>
        /// <exception cref="ArgumentNullException">baseData or baseEffectsData is null.</exception>
        /// <exception cref="ArgumentException">
        ///     The range of years does not match between baseData and scenData.
        ///     -or-
        ///     The range of years does not match between baseEffectsData and scenEffectsData.
        ///     -or-
        ///     Either both of scenData and scenEffectsData must be null or neither.
        /// </exception>
        public ReportingData(IIndustry baseData, IAnnualValue<ModelingEffects> baseEffectsData,
                             IIndustry scenData, IAnnualValue<ModelingEffects> scenEffectsData)
            : this(baseData, baseEffectsData, scenData, scenEffectsData, null) { }

        /// <inheritdoc cref="ReportingData(IIndustry, IAnnualValue{ModelingEffects},
        ///                                 IIndustry, IAnnualValue{ModelingEffects})"/>
        /// <remarks>
        /// For <paramref name="dataFilters"/>, specify <c>null</c> to automatically calculate based on user provided
        /// <paramref name="baseData"/> and <paramref name="scenData"/>.
        /// </remarks>
        protected ReportingData(IIndustry baseData, IAnnualValue<ModelingEffects> baseEffectsData,
                                IIndustry scenData, IAnnualValue<ModelingEffects> scenEffectsData,
                                DataFilters dataFilters = null)
        {
            if (baseData == null) {
                throw new ArgumentNullException(nameof(baseData));
            }
            if (baseEffectsData == null) {
                throw new ArgumentNullException(nameof(baseEffectsData));
            }
            if (scenData != null && (baseData.MinYear != scenData.MinYear
                                  || baseData.MaxYear != scenData.MaxYear)) {
                throw new ArgumentException(
                    "The range of years does not match between baseData and scenData.");
            }
            if (scenEffectsData != null && (baseEffectsData.MinYear != scenEffectsData.MinYear
                                         || baseEffectsData.MaxYear != scenEffectsData.MaxYear)) {
                throw new ArgumentException(
                    "The range of years does not match between baseEffectsData and scenEffectsData.");
            }
            if (scenData == null ^ scenEffectsData == null) {
                throw new ArgumentException(
                    "Either both of scenData and scenEffectsData must be null or neither.");
            }

            this.BaselineData = baseData;
            this.BaselineEffectsData = baseEffectsData;
            this.ScenarioData = scenData ?? baseData;
            this.ScenarioEffectsData = scenEffectsData ?? baseEffectsData;

            this.MinYear = baseData.MinYear;
            this.MaxYear = baseData.MaxYear;
            this.MinEffectsYear = baseEffectsData.MinYear;
            this.MaxEffectsYear = baseEffectsData.MaxYear;

            this.DataFilters = dataFilters ?? new DataFilters(baseData, scenData);
        }

        /// <summary>
        /// Gets the compliance modeling data for the baseline scenario.
        /// </summary>
        public IIndustry BaselineData { get; }

        /// <summary>
        /// Gets the effects modeling data for the baseline scenario.
        /// </summary>
        public IAnnualValue<ModelingEffects> BaselineEffectsData { get; }

        /// <summary>
        /// Gets the compliance modeling data for the action alternative scenario, or the <see cref="BaselineData"/>
        /// if the reporting data encapsulates the baseline scenario only.
        /// </summary>
        public IIndustry ScenarioData { get; }

        /// <summary>
        /// Gets the effects modeling data for the action alternative scenario, or the <see cref="BaselineEffectsData"/>
        /// if the reporting data encapsulates the baseline scenario only.
        /// </summary>
        public IAnnualValue<ModelingEffects> ScenarioEffectsData { get; }

        /// <summary>
        /// Gets the lowest year for which <see cref="BaselineData"/> and <see cref="ScenarioData"/> is available.
        /// </summary>
        public int MinYear { get; }
        /// <summary>
        /// Gets the highest year for which <see cref="BaselineData"/> and <see cref="ScenarioData"/> is available.
        /// </summary>
        public int MaxYear { get; }

        /// <summary>
        /// Gets the lowest year for which <see cref="BaselineEffectsData"/> and <see cref="ScenarioEffectsData"/> is
        /// available.
        /// </summary>
        public int MinEffectsYear { get; }
        /// <summary>
        /// Gets the highest year for which <see cref="BaselineEffectsData"/> and <see cref="ScenarioEffectsData"/> is
        /// available.
        /// </summary>
        public int MaxEffectsYear { get; }

        /// <summary>
        /// Gets the data filters to use for reporting, based on the mix of simulated vehicles in the
        /// <see cref="BaselineData"/> and <see cref="ScenarioData"/>.
        /// </summary>
        public DataFilters DataFilters { get; }

    }
}
