#region << Using Directives >>
using System;
using System.Drawing;

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 ModelChart : Chart
    {

        #region /*** Ctors ***/

        /// <summary>
        /// Initializes a new instance of the <see cref="ModelChart"/> class.
        /// </summary>
        public ModelChart() : base()
        {
            this.InitializeComponent();
            this.IsInitialized = false;
        }

        #endregion

        #region /*** Methods ***/

        //----- chart initialization -----
        void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // ModelChart
            // 
            this.GridBorderStyle = System.Windows.Forms.Border3DStyle.RaisedInner;
            this.Name = "ModelChart";
            this.Size = new System.Drawing.Size(480, 320);
            this.ResumeLayout(false);

        }
        /// <summary>
        /// Initializes the chart with the current compliance model, model data, and settings.
        /// </summary>
        /// <param name="compliance">The compliance model, which will be executed when modeling begins.</param>
        /// <param name="data">The initial model data used for analysis.</param>
        /// <param name="settings">The modeling settings being used for analysis.</param>
        public void InitializeChart(ICompliance compliance, IIndustry data, ModelingSettings settings)
        {
            this.IsInitialized = false;
            if (compliance == null)
            {
                throw new ArgumentNullException("compliance", "A valid compliance model should be specified.");
            }

            this._compliance = compliance;
            this._data       = data;
            this._settings   = settings;

            var runtimeSettings = settings.RuntimeSettings;
            this._scenCount = settings.Scenarios.ScenarioCount;
            this._iterCount = runtimeSettings.DynamicEconomicModeling ? runtimeSettings.DfsSrIterations : 1;
            this._minYear   = runtimeSettings.AnalysisStartYear;
            this._maxYear   = runtimeSettings.AnalysisEndYear;
            this._yearCount = this._maxYear - this._minYear + 1;
            this._mfrCount  = data.ManufacturerCount;

            this.InitializeChartInternal();
            this.IsInitialized = true;
        }
        /// <summary>
        /// When overridden in a derived class, performs additional initialization of the chart.
        /// </summary>
        protected virtual void InitializeChartInternal() { }

        //----- chart refreshing -----
        /// <summary>
        /// Refreshes the chart's data, obtaining latest progress information from the compliance model.
        /// </summary>
        public void RefreshChartData()
        {
            if (this.IsInitialized)
            {
                this.RefreshChartData(this._compliance.Progress);
            }
        }
        /// <summary>
        /// Refreshes the chart's data, using the specified progress information.
        /// </summary>
        /// <param name="progress">Modeling progress information to use for refreshing the chart.</param>
        public virtual void RefreshChartData(IModelingProgress progress) { }

        //----- cycle chart data -----
        /// <summary>
        /// Raises the <see cref="Chart.ChangeView"/> event.
        /// </summary>
        /// <param name="e">Event data to pass to the event.</param>
        protected override void OnChangeView(ChangeViewEventArgs e)
        {
            if (this.IsInitialized)
            {
                // determine direction of view change
                int direction = e.Direction;
                if (direction == 0 && e.IsMouseInput)
                {
                    PointF pt = this.MousePercentTraveled;
                    direction = ((e.IsVertical ? pt.Y : pt.X) > 0) ? -1 : 1;
                }
                if (direction != 0)
                {   // cycle view, then refresh data for new view
                    if (e.IsVertical) { this.CycleVertical(direction); } else { this.CycleHorizontal(direction); }
                    this.RefreshChartData();
                }
            }

            // raise change view event
            base.OnChangeView(e);
        }
        /// <summary>
        /// When overridden in a derived class, cycles the chart's view in the horizontal direction.
        /// By default, this method cycles through regulatory classes.
        /// </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 virtual void CycleHorizontal(int direction)
        {
            int scenFilter = (this._scenFilter == -1) ? 0 : this._scenFilter;
            int? yearFilter = (this._yearFilter == -1) ? null : (int?)this._yearFilter;
            var data = this._compliance.GetData(this._settings.Scenarios[scenFilter]);
            ChartFilters.CycleRegClasses(ref this._rcFilter, direction, data.CalcRegClassMix(yearFilter));
        }
        /// <summary>
        /// When overridden in a derived class, cycles the chart's view in the vertical direction.
        /// By default, this method does not cycle through any filters.
        /// </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 virtual void CycleVertical(int direction) { }

        /// <summary>
        /// Resets the chart to its initial state by clearing the associated resources using during the most recent compliance run.
        /// </summary>
        /// <remarks>
        /// When overriding, extending classes should call this method to ensure that this <see cref="ModelChart"/> instance is cleaned up correctly.
        /// </remarks>
        public virtual void ResetChart()
        {
            // reset data filters
            this._scenFilter = -1;
            this._iterFilter = -1;
            this._yearFilter = -1;
            this._mfrFilter  = -1;
            this._rcFilter   =  3;
            //
            this.RefreshChartData(); // refresh chart data after filters were cleared

            // reset data
            this.IsInitialized = false;
            this._compliance   = null;
            this._data         = null;
            this._settings     = null;

            // clear chart's visuals and user interaction
            base.Collapse();
            base.ClearDataSeriesValues();
        }

        #endregion

        #region /*** Properties ***/

        /// <summary>Gets whether the chart has been initialized.</summary>
        public bool IsInitialized { get; private set; }

        #endregion

        #region /*** Variables ***/

        // support variables extracted from model/data/settings
        protected ICompliance _compliance;
        protected IIndustry _data;
        protected ModelingSettings _settings;

        protected int _scenCount = 0;
        protected int _iterCount = 0;
        protected int _yearCount = 0;
        protected int _minYear   = 0;
        protected int _maxYear   = 0;
        protected int _mfrCount  = 0;

        // data filters
        protected int _scenFilter = -1;
        protected int _iterFilter = -1;
        protected int _yearFilter = -1;
        protected int _mfrFilter  = -1;
        protected int _rcFilter   = 3; // index 3 = overal RC

        #endregion

    }
}
