//******************************************************************************
//  タイトル  : Analog wave monitor
//  ファイル  : DrawWave.cs
//  機能概要  : 波形表示クラス
//------------------------------------------------------------------------------
//  使用言語  : C#
//  ランタイム: Microsoft .NET framework Version 4.5.51641
//  ビルド環境: Microsoft Visual Studio Express 2013 for Windows Desktop
//              Version 12.0.21005.1 REL
//------------------------------------------------------------------------------
//  変更履歴  :
//  Ver 1.0.0.0  2015/02/23  Tetsuro Tatsuoka  新規作成
//******************************************************************************
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace AnalogWaveMonitor
{
    public partial class DrawWave : System.Windows.Forms.PictureBox
    {
		#region 定数
		//***********************************************************************************

		private const int labelLength = 20;     // 波形表示領域の左上に表示する文字列の最大数
        private const int labelMargin = 3;      // 表示する文字列の上端、左端からのオフセット
        private const int frameLineWidth = 1;   // フレームの線幅
        private const int waveLineWidth = 1;    // 波形の線幅
        private const int updateDispRes = 100;  // 波形表示の更新分解能

		//***********************************************************************************
		#endregion

		#region フィールド
		//***********************************************************************************

        private string waveLabel;               // 波形表示領域の左上に表示する文字列
        private Color backColor;                // 背景色
        private Color frameColor;               // 枠線色
        private Color strColor;                 // 文字色
        private Color waveColor;                // 波形色

        private Bitmap bgBitmap;                // 背景ビットマップ

        private int basePos;                    // 基線位置
        private Point[] waveDot;                // 波形描画データ
        private int wavePtr;                    // 波形描画位置ポインタ
        private bool dividerFlag;               // 描画更新位置のブランク表示フラグ

		//***********************************************************************************
		#endregion

		#region プロパティ
		//***********************************************************************************

		/// <summary>
        /// 左上に表示される項目ラベルを取得または設定します。
        /// labelLengthで指定した文字数より長い文字列を設定しようとした場合には制限されます。
        /// </summary>
        public string WaveformLabel
        {
            get { return waveLabel; }
            set 
            {
                if (value.Length > labelLength)
                {
                    waveLabel = value.Substring(0, labelLength);
                }
                else
                {
                    waveLabel = value;
                }
                bgSetup();
            }
        }

        /// <summary>
        /// 背景色
        /// </summary>
        public Color BackgroundColor
        {
            get { return backColor; }
            set { backColor = value; }
        }

        /// <summary>
        /// 枠線色
        /// </summary>
        public Color FrameColor
        {
            get { return frameColor; }
            set { frameColor = value; }
        }

        /// <summary>
        /// 文字色
        /// </summary>
        public Color StringColor
        {
            get { return strColor; }
            set { strColor = value; }
        }

        /// <summary>
        /// 波形色
        /// </summary>
        public Color WaveformColor
        {
            get { return waveColor; }
            set { waveColor = value; }
        }

        /// <summary>
        /// 描画更新位置のブランク表示フラグ
        /// </summary>
        public bool DividerFlag
        {
            get { return dividerFlag; }
            set { dividerFlag = value; }
        }

        //***********************************************************************************
		#endregion

		#region メソッド
		//***********************************************************************************

		/// <summary>
        /// コンストラクタ
		/// </summary>
		/// <param name="width">横幅</param>
		/// <param name="height">高さ</param>
        /// <param name="unsignedMode">符号なしモード</param>
        public DrawWave(int width, int height, bool unsignedMode)
        {
            this.Width = width;
            this.Height = height;
            this.DoubleBuffered = true;     // ダブルバッファリング描画
            
            waveLabel = "Label";            // 波形表示領域の左上に表示する文字列
            backColor = Color.Black;        // 背景色
            frameColor = Color.White;       // 枠線色
            strColor = Color.Yellow;        // 文字色
            waveColor = Color.LightBlue;    // 波形色

            bgSetup();                      // 背景の設定

            basePos = (unsignedMode) ? (this.Height - frameLineWidth - 1) : (this.Height / 2);  // 基線の位置

            // 波形描画データ
            waveDot = new Point[width * 2];
            for (int i = 0; i < waveDot.Length; i++)
            {
                Point pt = new Point(i / 2 + frameLineWidth / 2, basePos);
                waveDot[i] = pt;
            }
            wavePtr = 0;                    // 波形描画位置ポインタ

            dividerFlag = false;            // 描画更新位置のブランク表示フラグ
        }

		/// <summary>
		/// 背景bitmapの作成
		/// </summary>
        private void bgSetup()
        {
            // 背景画像の用意
            bgBitmap = new Bitmap(Width, Height);
            Graphics gr = Graphics.FromImage(bgBitmap);

            // 枠を描画
            Pen framePen = new Pen(frameColor, (float)frameLineWidth);
            SolidBrush brush = new SolidBrush(backColor);
            gr.FillRectangle(brush, this.ClientRectangle);
            gr.DrawRectangle(framePen, frameLineWidth - 1, frameLineWidth - 1, this.Width - frameLineWidth, this.Height - frameLineWidth);
            framePen.Dispose();
            brush.Dispose();

            // ラベルを描画	
            Brush letterBrush = new SolidBrush(strColor);
			Font fnt = new Font("MS ゴシック", 9); // Font("Times New Roman", 10, FontStyle.Bold);
            SizeF szF = gr.MeasureString(waveLabel, fnt);
			gr.DrawString(waveLabel, fnt, letterBrush, frameLineWidth + labelMargin, frameLineWidth + labelMargin);
            letterBrush.Dispose();

            // ベースラインを描画
            Pen basePen = new Pen(Color.Silver, (float)1.0);
            //gr.DrawLine(basePen, 0, Height / 2, Width, Height / 2);	// 見にくいのでやめ
            basePen.Dispose();

            gr.Dispose();
        }

		/// <summary>
		/// OnPaintメソッド
		/// </summary>
		/// <param name="pe"></param>
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs pe)
        {
            base.OnPaint(pe);

            Graphics gr = pe.Graphics;

            // 背景を描画
            gr.DrawImage(bgBitmap, this.ClientRectangle);

            // 波形を描画
            Pen wavePen = new Pen(waveColor, (float)waveLineWidth);
            gr.DrawLines(wavePen, waveDot);

            // 波形の切れ目（描画更新位置のブランク）を描画
            if (dividerFlag)
            {
                Rectangle redrawRect = new Rectangle(wavePtr - 1, 0, 10, Height);
                gr.DrawImage(bgBitmap, redrawRect, redrawRect, GraphicsUnit.Pixel);
            }

            wavePen.Dispose();
        }

        /// <summary>
        /// 波形の描画位置を手動で設定する
        /// </summary>
        /// <param name="pos"></param>
        public void SetDrawPos(int pos)
        {
            wavePtr = pos;
        }

        /// <summary>
        /// 波形データ値をセットする
        /// </summary>
        /// <param name="dataMax">波形データの最大値</param>
        /// <param name="dataMin">波形データの最小値</param>
        public void SetWavePoint(int dataMax, int dataMin)
        {
            if (this.IsDisposed == true)
            {
                return;
            }
            // データ格納
            waveDot[wavePtr * 2] = new Point(wavePtr, basePos - (Height * dataMin / 65536));
            waveDot[wavePtr * 2 + 1] = new Point(wavePtr, basePos - (Height * dataMax / 65536));

            // 波形ポインタを更新
            wavePtr = (wavePtr + 1) % this.Width;

            // 描画を更新
            int updateDispWidth = this.Width / updateDispRes;
            if ((wavePtr % updateDispWidth) == 0)
            {
                Invalidate(true);
            }
        }

		//***********************************************************************************
		#endregion

    }
}
