#region <<< Using Directives >>>
using System;
using System.Drawing;
using System.Windows.Forms;
using Volpe.Cafe.Model;
#endregion

namespace Volpe.Cafe.EspressoUI.ModelCharts
{
    [Serializable]
    class ModelingProgressChart : ManufacturerSummaryChart
    {

        #region <<< Ctors >>>

        /// <summary>
        /// Initializes a new instance of the <see cref="ModelingProgress"/> class.
        /// </summary>
        public ModelingProgressChart()
        {
            this.InitializeComponent();
            this.InitializeOther();
        }

        #endregion

        #region <<< Methods >>>

        //----- initialization -----
        void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // ModelingProgressChart
            // 
            this.DisableContextMenu = true;
            this.GridBorderStyle = Border3DStyle.RaisedInner;
            this.LMBTrackVTravel = false;
            this.Name = "ModelingProgressChart";
            this.RMBTrackHTravel = false;
            this.ShowLegend = false;
            this.ShowTitle = false;
            this.Size = new Size(480, 320);
            this.TabStop = true;
            this.Title = "Modeling Progress";
            this.ResumeLayout(false);

        }
        void InitializeOther()
        {
            this._progress = new string[Labels.Length];
            this._dials = new TachometerChart[Labels.Length];
            //
            for (int i = 0; i < this._dials.Length; i++)
            {
                var chart = new TachometerChart()
                {
                    GridBorderStyle = Border3DStyle.RaisedInner,
                    GridPadding = new Padding(0),
                    LMBTrackHTravel = false,
                    LMBTrackVTravel = false,
                    RMBTrackHTravel = false,
                    RMBTrackVTravel = false,
                    TrackArrowKeys = false,
                    SelectOnMouseClick = false,
                    DisableContextMenu = true,
                    ShowGridLines = false,
                    Font = new Font(this.Font.FontFamily, this.Font.Size + 2F),
                    TabStop = false,
                    TabIndex = i,
                    Visible = false
                };
                chart.SetBounds(0, 0, 0, 0);

                this._dials[i] = chart;
                this.Controls.Add(chart);
            }
        }
        protected override void InitializeChartInternal()
        {
            this._dials[0].InitializeChart("Base", (this._scenCount == 1) ? "Base" : "Alt" + (this._scenCount - 1));
            this._dials[1].InitializeChart("1", this._iterCount.ToString());
            this._dials[2].InitializeChart(this._minYear.ToString(), this._maxYear.ToString());
            this._dials[3].InitializeChart(this._data.Manufacturers[0].ProperName,
                                           this._data.Manufacturers[this._mfrCount - 1].ProperName);
        }

        //----- layout updating -----
        protected override void OnLayoutUpdated(EventArgs e)
        {
            this.UpdateLayout();
            base.OnLayoutUpdated(e);
        }
        void UpdateLayout()
        {
            using (Graphics g = this.CreateGraphics())
            {
                // compute title bounds first
                float left = 8F;
                float top  = 8F;
                float dy   = this.CalcLabelSpacing();
                SizeF sz   = g.MeasureString(TitleLabel, this.TitleFont);
                //
                this._titleBounds = new RectangleF(left, top, sz.Width, sz.Height);
                top += this._titleBounds.Height + dy;

                // compute label bounds (set to max size for all)
                sz = g.MeasureString(Labels[0], this.ChartFontLg);
                for (int i = 1; i < Labels.Length; i++)
                {
                    SizeF sz2 = g.MeasureString(Labels[i], this.ChartFontLg);
                    sz.Width  = Math.Max(sz.Width, sz2.Width);
                    sz.Height = Math.Max(sz.Height, sz2.Height);
                }
                for (int i = 0; i < Labels.Length; i++)
                {
                    this._labelBounds[i] = new RectangleF(left, top, sz.Width, sz.Height);
                    top += this._labelBounds[i].Height + dy;
                }

                // compute progress label bounds (set to max size for all)
                if (this._progress != null)
                {
                    sz = g.MeasureString(this._progress[0], this.ChartFontLg);
                    for (int i = 1; i < this._progress.Length; i++)
                    {
                        SizeF sz2 = g.MeasureString(this._progress[i], this.ChartFontLg);
                        sz.Width  = Math.Max(sz.Width, sz2.Width);
                        sz.Height = Math.Max(sz.Height, sz2.Height);
                    }
                    for (int i = 0; i < this._progress.Length; i++)
                    {
                        float progressLeft = this._labelBounds[i].Right + 4F + Math.Min(this.AltScaleFactor / 2, 4F);
                        float progressTop  = this._labelBounds[i].Top;
                        this._progressBounds[i] = new RectangleF(progressLeft, progressTop, sz.Width, sz.Height);
                    }
                }

                // update padding
                Padding pad = this.GridPadding;
                pad.Top = (int)(top + 1);
                this.GridPadding = pad;
            }
            //
            this.UpdateTachometerLayout();
            this.Invalidate();
        }
        void UpdateTachometerLayout()
        {
            if (this._dials == null) { return; }
            //
            float spacing = this.CalcLabelSpacing();
            float titleB  = this._titleBounds.Bottom;
            int   sz      = (int)Math.Floor(this.GridPadding.Top - titleB - spacing * 2);
            int   x       = this.GridBounds.Right - sz * this._dials.Length;
            int   y       = (int)Math.Floor(titleB + spacing * 2);
            //
            for (int i = 0; i < this._dials.Length; i++)
            {
                TachometerChart chart = this._dials[i];
                chart.Visible = (this.IsInitialized && this._progressBounds[0].Right < x);
                chart.SetBounds(x, y, sz, sz);
                x += sz;
                chart.Refresh();
            }
        }

        //----- chart refreshing -----
        public override void RefreshChartData(IModelingProgress progress)
        {
            this.RefreshPorgressLabels();
            if (this.IsInitialized && progress != null)
            {
                // refresh progress labels
                if (progress.Scenario != null)
                {
                    this._scenFilter = progress.Scenario.Index;
                    this._progress[0] = (this._scenFilter == 0) ? "Baseline" : "Alternative " + this._scenFilter.ToString();
                    this._dials[0].Value = (this._scenFilter + 1D) / this._scenCount;
                }
                if (progress.Iteration != null)
                {
                    this._iterFilter = progress.Iteration.Value;
                    this._progress[1] = $"{this._iterFilter} {(this._iterFilter == 1 ? "(Ref)" : "(Alt)")}";
                    this._dials[1].Value = (double)this._iterFilter / this._iterCount;
                }
                if (progress.ModelYear != null)
                {
                    this._yearFilter = progress.ModelYear.Year;
                    this._progress[2] = this._yearFilter.ToString();
                    this._dials[2].Value = (this._yearFilter - this._minYear + 1D) / this._yearCount;
                }
                if (progress.Manufacturer != null)
                {
                    this._mfrFilter = progress.Manufacturer.Index;
                    this._progress[3] = progress.Manufacturer.ProperName + ChartFilters.RegClassSuffix[this._rcFilter];
                    this._dials[3].Value = (this._mfrFilter + 1D) / this._mfrCount;
                }

                // refresh chart data
                base.RefreshChartData(progress);
            }
        }

        //----- custom paint support -----
        protected override void OnPaint(PaintEventArgs e)
        {
            this.PaintProgressLabels(e.Graphics);
            base.OnPaint(e);
        }
        void PaintProgressLabels(Graphics g)
        {
            using (Brush brush = new SolidBrush(this.ForeColor))
            {
                g.DrawString(TitleLabel, this.TitleFont, brush, this._titleBounds);
                for (int i = 0; i < Labels.Length; i++)
                {
                    g.DrawString(Labels        [i], this.ChartFontLg, brush, this._labelBounds[i]);
                    g.DrawString(this._progress[i], this.ChartFontLg, brush, this._progressBounds[i]);
                }
            }
        }

        //----- support methods -----
        void RefreshPorgressLabels()
        {
            for (int i = 0; i < this._progress.Length; i++)
            {
                this._progress[i] = string.Empty;
            }
        }

        float CalcLabelSpacing()
        {
            // horizontal spacing between labels
            return Math.Min(this.AltScaleFactor / 3F, 4F);
        }

        #endregion

        #region <<< Variables >>>

        // cached strings for static labels
        static readonly string TitleLabel = "Modeling Progress";
        static readonly string[] Labels = { "Scenario:", "Iteration:", "Model Year:", "Manufacturer:" };

        //--- layout ---
        RectangleF   _titleBounds;
        RectangleF[] _labelBounds = new RectangleF[Labels.Length];
        RectangleF[] _progressBounds = new RectangleF[Labels.Length];

        // progress labels and dials
        string[] _progress = null;
        TachometerChart[] _dials = null;

        #endregion

    }
}
