//--------------------------------------------------------------
//	オシロスコープで使うメソッド
//
//		作成者：三上直樹，2022/04/29 作成，©三上直樹 2022
//--------------------------------------------------------------

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using Mikami.WindowsApplication;

namespace CQ_Oscilloscope
{
	public partial class Form1 : Form
	{
		// 座標軸などの表示
		private void DrawAxis(Graphics gr, Color color)
		{
			gr.TranslateTransform(pictureBox1.Location.X, pictureBox1.Location.Y);
			gr.FillRectangle(new SolidBrush(color) , -50, -27,
							 pictureBox1.Width+116, pictureBox1.Height+56);

			float dv = (pictureBox1.Width - 1)/10.0f;
			float dv5 = dv/5.0f;
			Pen penGray = Pens.LightGray;
			// 縦方向
			for (int n=0; n<=10; n++)
				gr.DrawLine(penGray, n*dv, 0, n*dv, pictureBox1.Height-1);
			float y01 = 4*dv - 3;
			float y02 = 4*dv + 3;
			for (int n=0; n<50; n++)
				gr.DrawLine(penGray, n*dv5, y01, n*dv5, y02);
			// 横方向
			for (int n=0; n<=8; n++)
				gr.DrawLine(penGray, 0, n*dv, pictureBox1.Width-1, n*dv);
			float x01 = 5*dv - 3;
			float x02 = 5*dv + 3;
			for (int n=0; n<40; n++)
				gr.DrawLine(penGray, x01, n*dv5, x02, n*dv5);
		}

		// 波形表示の準備
		void PreDisplay()
		{
			nData_ = rxData_.Length/4;
			// 送信されたデータ数が規定の数になっていない場合は直ちに戻る
			if (nData_ != N_T_) return;

			// 受信データを取り出しバッファに格納する
			ExtractData(y1Buf_, 0, mag1_);
			ExtractData(y2Buf_, nData_, mag2_);

			// トリガの処理
			float[] buf = (chTrg_ == 1) ? y1Buf_ : y2Buf_;
			float th = (chTrg_ == 1) ?
					   trgCh1_ + orgY1_ - 2*unit_ :
					   trgCh2_ + orgY2_ - 2*unit_;

			bool triggered = false;
			int index;				// トリガ位置に対応するインデックス
			for (index=1; index<N_DISP_; index++)
				if ((buf[index-1] < th) && (buf[index] >= th))
				{
					triggered = true;
					break;
				}

			if (triggered && singleOk_) Invoke(() => CommonProc.Enable(label16, button3) );

			// 表示用バッファの更新
			if ( !single_ || (single_ && triggered && singleOk_) )
			{
				currentFrame_ = Inc(currentFrame_, maxDisp_);
				for (int n=0; n<N_DISP_; n++)
				{
					pYn1_[currentFrame_][n] = new PointF(n, orgY1_-y1Buf_[n+index]);
					pYn2_[currentFrame_][n] = new PointF(n, orgY2_-y2Buf_[n+index]);
				}

				singleOk_ = false;		// Single モードのとき波形の表示を１回で止めるため
				pictureBox1.Invalidate();
			}

			if (readOk_) RequestXfer();	// データ転送を要求
		}

		// 波形の表示
		private void DrawWaveform(Graphics gr, Pen myPen, PointF[] pt)
		{
			gr.SmoothingMode = SmoothingMode.AntiAlias;
			gr.DrawCurve(myPen, pt);
			gr.SmoothingMode = SmoothingMode.Default;
		}

		// 0 ～ 10,000 の範囲の数を 2 文字で表したコードを float 型に変換する
		// 10,000 を 1.0f, 0 を -1.0f に対応させる
		private float Decode(string s)
		{
			uint x = (((uint)s[0] - 0x10) << 7) | ((uint)s[1] - 0x10);
			#if DEBUG
				if (x > 10000) MessageBox.Show(x.ToString());
			#endif
			return ((int)x - 5000.0f)/5000.0f;
		}

		// 受信データを取り出してバッファに格納する
		private void ExtractData(float[] buf, int offset, int mag)
		{
			for (int n=0; n<N_T_; n++)
				buf[n] = unit_*mag*
						 Decode(rxData_.Substring(2*(n+offset), 2));
		}

		// 標本化周波数設定と横軸の目盛値描画
		private void FsXAxis()
		{
			fs_ = GetFs();
			label7.Text = comboBox1.Text + "/div";
			float dv2 = 100.0f/fs_;
			for (int n=0; n<labels_.Length; n++)
				labels_[n].Text = (n*dv2).ToString("0.0");
		}

		// comboBox1 の選択された項目から標本化周波数を取得する
		private int GetFs()
		{
			int us = int.Parse(comboBox1.Text.Trim(" μms".ToCharArray()));
			if (comboBox1.Text.Contains("m")) us = us*1000;
			return 50000/us;	// kHz 単位
		}

		// 垂直方向の表示位置の変更のためのメソッド
		private void VerticalPos(TrackBar tb, RadioButton rb,
								 ref float org, ref float orgOld, ref PointF[][] pYn)
		{
			org = pictureBox1.Size.Height - 1 - tb.Value;
			if (rb.Checked) TrackBar1Limit(org, orgOld);
			// 以下は Single モードでも表示位置を変更できるようにするため
			for (int n=0; n<N_DISP_; n++)
				pYn[currentFrame_][n].Y = pYn[currentFrame_][n].Y
											+ org - orgOld;
			orgOld = org;
			pictureBox1.Invalidate();
		}

		// trackBar1 のツマミの位置を trackBar2, trackBar3 の
		// ツマミの位置と連動させる
		private void TrackBar1Limit(float y, float yOld)
		{
			int pos = trackBar1.Value + (int)(yOld - y);
			// ツマミの位置の制限
			if (pos > trackBar1.Maximum) pos = trackBar1.Maximum;
			if (pos < trackBar1.Minimum) pos = trackBar1.Minimum;
			trackBar1.Value = pos;
			trackBar1_Scroll(null, null);
		}

		// 波形の多重表示で使用
		private void MultiDisplay(Graphics gr, Pen myPen, PointF[][] pt)
		{
			int k = currentFrame_;
			for (int n=0; n<traceN_; n++)
			{
				DrawWaveform(gr, myPen, pt[k]);
				k = Dec(k, maxDisp_);
			}
		}

		// データの転送を要求する
		private void RequestXfer()
			=> sp_.SendTo("GO" + fs_.ToString("0000"));	// 標本化周波数，単位： kHz

		// comboBox から選択された項目の倍率を取得する
		private int GetMagnification(ComboBox comboBox) => int.Parse(comboBox.Text);

		// modulo m のインクリメント
		private int Inc(int x, int m) => (++x >= m) ? 0 : x;

		// modulo m のデクリメント
		private int Dec(int x, int m) => (--x < 0) ? m-1 : x;

		// 別のスレッドを使う場合に使用
		private void Invoke(Action action) => base.Invoke(action);
	}
}
