#region << Using Directives >>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

using Volpe.Cafe.EspressoUI.Charts;
using Volpe.Cafe.MarketData;
using Volpe.Cafe.MarketData.Design;
using Volpe.Cafe.Model;
using Volpe.Cafe.ModelSettings;
#endregion

namespace Volpe.Cafe.EspressoUI.ModelCharts
{
    [Serializable]
    class SessionSummaryChart : ModelChart
    {

        #region /*** Ctors ***/

        /// <summary>
        /// Initializes a new instance of the <see cref="SessionSummaryChart"/> class.
        /// </summary>
        public SessionSummaryChart() : base()
        {
            this.InitializeComponent();
            this.InitializeOther();
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="SessionSummaryChart"/> class, using the specified chart title.
        /// </summary>
        /// <param name="title">The title to display on the chart.</param>
        public SessionSummaryChart(string title) : this(title, false) { }
        /// <summary>
        /// Initializes a new instance of the <see cref="SessionSummaryChart"/> class, using the specified chart title.
        /// </summary>
        /// <param name="title">The title to display on the chart.</param>
        /// <param name="plotMfr">true, to plot manufacturer-specific data; false, to plot industry-specific data.</param>
        public SessionSummaryChart(string title, bool plotMfr) : this()
        {
            this.Title   = title;
            this.PlotMfr = plotMfr;
        }

        #endregion

        #region /*** Methods ***/

        //----- initialization -----
        void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // SessionSummaryChart
            // 
            this.CategoryAxisDiscreteValues = new string[] {
        "2016",
        "2017",
        "2018"};
            this.CategoryAxisLabel = "Model Year";
            this.Name = "SessionSummaryChart";
            this.PrimaryAxisLabel = "mpg";
            this.PrimaryAxisMaxValue = 30;
            this.PrimaryAxisMinValue = 0;
            this.PrimaryAxisNumberFormat = "0.0";
            this.SecondaryAxisLabel = "$ (m)";
            this.SecondaryAxisMaxValue = 300;
            this.SecondaryAxisMinValue = 0;
            this.SecondaryAxisNumberFormat = "0";
            this.ShowSecondaryAxis = true;
            this.ResumeLayout(false);

        }
        void InitializeOther()
        {
            this.InitializeAxis();
            this.InitializeSeries();
        }
        //
        protected override void InitializeChartInternal()
        {
            this.InitializeAxis();
            this.InitializeSeries();
        }
        void InitializeAxis()
        {
            int minYear   = (this._yearCount == 0) ? DateTime.Now.Year : this._minYear;
            int yearCount = (this._yearCount == 0) ? 1 : this._yearCount;
            string[] modelYears = new string[yearCount];
            for (int i = 0; i < modelYears.Length; i++)
            {
                modelYears[i] = (minYear + i).ToString();
            }
            this.CategoryAxisDiscreteValues = modelYears;
        }
        void InitializeSeries()
        {
            this.DataSeries.Clear();
            //
            this.AddDataSeries("Standard"    , AxisType.Primary  , ChartStyle.Default);
            this.AddDataSeries("CAFE"        , AxisType.Primary  , ChartStyle.Default);
            this.AddDataSeries("Shortfall"   , AxisType.Primary  , ChartStyle.Default);
            this.AddDataSeries("Fines"       , AxisType.Secondary, ChartStyle.Default);
            this.AddDataSeries("Costs"       , AxisType.Secondary, ChartStyle.Default);
            this.AddDataSeries("Fuel Savings", AxisType.Secondary, ChartStyle.Default);
            this.AddDataSeries("CO2 Savings" , AxisType.Secondary, ChartStyle.Default);
            //
            this.RefreshSeriesStyles(false);
        }

        //----- chart refreshing -----
        public override void RefreshChartData(IModelingProgress progress)
        {
            try
            {
                if (!this.IsInitialized || progress == null || progress.ModelYear == null) { return; }
                this.RefreshComplianceProgram();
                this.RefreshChartDataInternal(progress.Scenario, progress.ModelYear, progress.Manufacturer);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
        void RefreshComplianceProgram()
        {
            double primaryAxisMaxValue = this.PrimaryAxis.MaxValue;
            var program = this._settings.RuntimeSettings.ComplianceProgram;
            if (program.IsCO2())
            {
                this.DataSeries[0].Name = "CO2 Standard";
                this.DataSeries[1].Name = "CO2 Rating";
                this.PrimaryAxis.Label = "g/mi";
                primaryAxisMaxValue = 300;
            }
            else
            {
                this.DataSeries[0].Name = "Standard";
                this.DataSeries[1].Name = "CAFE";
                this.PrimaryAxis.Label = "mpg";
                primaryAxisMaxValue = 30;
            }
            if (!this._compliance.Running)
            {
                this.PrimaryAxis.MaxValue = primaryAxisMaxValue;
            }
        }
        void RefreshChartDataInternal(Scenario scen, ModelYear year, IManufacturer mfr)
        {
            int mfrFilter = -1;
            if (this._plotMfr)
            {
                if (mfr == null) { return; }
                mfrFilter = (this._mfrFilter == -1) ? mfr.Index : this._mfrFilter;
                if (mfrFilter >= this._mfrCount) { return; }
            }

            this.Title = (this._plotMfr ?
                this._data.Manufacturers[mfrFilter].ProperName + (this._mfrFilter == -1 ? "*" : "") :
                "Industry Summary") + ChartFilters.RegClassSuffix[this._rcFilter];

            // update data
            var program = this._settings.RuntimeSettings.ComplianceProgram;
            var metrics = (program.IsCO2() ? ComplianceMetrics.CO2Values : ComplianceMetrics.CAFEValues) |
                          ComplianceMetrics.ComplianceCosts | ComplianceMetrics.EffectsCosts;

            int      yearCount = year.Year - this._minYear + 1;
            double[] standard  = new double[yearCount];
            double[] rating    = new double[yearCount];
            double[] shortfall = new double[yearCount];
            double[] fines     = new double[yearCount];
            double[] costs     = new double[yearCount];
            double[] fuelSav   = new double[yearCount];
            double[] co2Sav    = new double[yearCount];

            for (int i = 0; i < yearCount; i++)
            {
                IIndustry data = this._compliance.GetData(scen);
                if (data == null) { continue; }

                // get stats for the current year and "filtered" list of mfrs
                var mfrs = (this._plotMfr) ? new List<IManufacturer> { data.Manufacturers[mfrFilter] } : data.Manufacturers;
                var statistics = ChartHelper.CalculateStatistics(mfrs, this._rcFilter, this._minYear + i, metrics);

                if (program.IsCO2())
                {
                    standard [i] = statistics.CO2Standard;
                    rating   [i] = statistics.CO2Rating;
                    shortfall[i] = statistics.CO2Shortfall;
                }
                else
                {
                    standard [i] = statistics.Standard;
                    rating   [i] = statistics.CAFE;
                    shortfall[i] = statistics.Shortfall;
                }
                fines  [i] = statistics.Fines;
                costs  [i] = statistics.Costs;
                fuelSav[i] = statistics.FuelSavings;
                co2Sav [i] = statistics.CO2Savings;
            }

            double mpgMax = Math.Max(standard.Max(), rating.Max());
            double scalar = (program.IsCO2()) ? 50D : 15D;
            this.PrimaryAxisMaxValue = Math.Ceiling(mpgMax / scalar) * scalar;
            this.SecondaryAxisMaxValue = double.NaN;

            this.UpdateDataSeriesValues(0, standard);
            this.UpdateDataSeriesValues(1, rating);
            this.UpdateDataSeriesValues(2, shortfall);
            this.UpdateDataSeriesValues(3, fines);
            this.UpdateDataSeriesValues(4, costs);
            this.UpdateDataSeriesValues(5, fuelSav);
            this.UpdateDataSeriesValues(6, co2Sav);
        }
        void RefreshSeriesStyles(bool refreshChart)
        {
            if (this.HasDataSeries)
            {
                for (int i = 0; i < 3; i++) { this.DataSeries[i].Style = this.PrimaryAxisSeriesStyle; }
                for (int i = 3; i < 7; i++) { this.DataSeries[i].Style = this.SecondaryAxisSeriesStyle; }
                //
                if (refreshChart)
                {
                    this.RefreshChartData();
                }
            }
        }

        /// <summary>
        /// Cycles the chart's view in the vertical direction.
        /// </summary>
        /// <param name="direction">The direction of the view change. If direction is -1, the previous view should be selected; if
        ///   direction is 1, the next view should be selected.</param>
        protected override void CycleVertical(int direction)
        {
            // find current mfr index: -1 = auto-select mfr, index = mfr-index, !plotMfr => mfrCount = industry
            int mfrFilter = (this._plotMfr) ? this._mfrFilter : this._mfrCount;

            // advance mfr filter and adjust for overflow
            mfrFilter += direction;
            if (mfrFilter == -2) { mfrFilter = this._mfrCount; }
            else if (mfrFilter > this._mfrCount) { mfrFilter = -1; }

            if (mfrFilter > this._mfrCount)
            {   // advancing past industry to auto-mfr
                this._plotMfr = true;
                this._mfrFilter = -1;
            }
            else if (mfrFilter == -2 || mfrFilter == this._mfrCount)
            {   // advancing to plotting industry
                this._plotMfr = false;
                this._mfrFilter = -1;
            }
            else
            {   // cycling through mfrs
                this._plotMfr = true;
                this._mfrFilter = mfrFilter;
            }
        }

        /// <summary>
        /// Resets the chart to its initial state by clearing the associated resources using during the most recent compliance run.
        /// </summary>
        public override void ResetChart()
        {
            this._plotMfr = false;
            base.ResetChart();
        }

        #endregion

        #region /*** Properties ***/

        /// <summary>Gets or set the series style to use for the primary values axis.</summary>
        [Browsable(true)]
        [DefaultValue(true)]
        [Category("Grid Axis")]
        [Description("Indicates the series style to use for the primary values axis.")]
        public ChartStyle PrimaryAxisSeriesStyle
        {
            get { return this._primaryAxisSeriesStyle; }
            set
            {
                if (this._primaryAxisSeriesStyle != value)
                {
                    this._primaryAxisSeriesStyle = value;
                    this.RefreshSeriesStyles(true);
                }
            }
        }
        /// <summary>Gets or set the series style to use for the secondary values axis.</summary>
        [Browsable(true)]
        [DefaultValue(true)]
        [Category("Grid Axis")]
        [Description("Indicates the series style to use for the secondary values axis.")]
        public ChartStyle SecondaryAxisSeriesStyle
        {
            get { return this._secondaryAxisSeriesStyle; }
            set
            {
                if (this._secondaryAxisSeriesStyle != value)
                {
                    this._secondaryAxisSeriesStyle = value;
                    this.RefreshSeriesStyles(true);
                }
            }
        }

        /// <summary>Gets or sets the index corresponding the the manufacturer-specific data to plot, or -1, to automatically select
        ///   from progress information.</summary>
        [Browsable(false)]
        public int MfrFilter
        {
            get { return this._mfrFilter; }
            set
            {
                if (value >= this._mfrCount || value < -1) { throw new ArgumentOutOfRangeException("value"); }
                //
                if (this._mfrFilter != value)
                {
                    this._mfrFilter = value;
                    this.RefreshChartData();
                }
            }
        }

        /// <summary>Gets or sets whether to plot manufacturer-specific data (true) or industry-specific data (false).</summary>
        [Browsable(false)]
        public bool PlotMfr
        {
            get { return this._plotMfr = true; }
            set
            {
                if (this._plotMfr != value)
                {
                    this._plotMfr = value;
                    this.RefreshChartData();
                }
            }
        }

        #endregion

        #region /*** Variables ***/

        // data series styles
        ChartStyle _primaryAxisSeriesStyle   = ChartStyle.Line;
        ChartStyle _secondaryAxisSeriesStyle = ChartStyle.Bar;

        // data filters
        bool _plotMfr = false;

        #endregion

    }
}
