//////////////////////////////
//		C# Biorythm.cs
// Biorythm Control Component
//////////////////////////////
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Biorythm
{
	public partial class BrmBox : PictureBox
	{
		//バイオリズムグラフ表示関係
		private Bitmap Canvas = null;	//仮想画面ビットマップ
		private Graphics hGraph = null;	//仮想画面のグラフィック
		//バイオリズム計算関係
		DateTime DOB;					//誕生日（Date of Birth）
		DateTime DOT;					//指定日（Date of Traget）
		//コンテキストメニュー関係
		ContextMenu contextMenu;
		MenuItem mi1, mi2, mi3;

		//コンストラクター
		public BrmBox()
		{
			this.ClientSize = new Size(320, 320);
			this.BackColor = SystemColors.Control;
			this.SizeChanged += Control_SizeChanged;
			//同サイズのビットマップ仮想画面の作成
			Canvas = new Bitmap(this.Width, this.Height);
			//Graphicsクラス（アンチエイリアス）
			hGraph = Graphics.FromImage(Canvas);
			hGraph.SmoothingMode = SmoothingMode.AntiAlias;
			hGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
			//コンテキストメニュー関連
			mi1 = new MenuItem();
			mi1.Index = 0;
			mi1.Text = "生年月日入力";
			mi2 = new MenuItem();
			mi2.Index = 0;
			mi2.Text = "指定日入力";
 			mi3 = new MenuItem();
			mi3.Index = 0;
			mi3.Text = "グラフ表示";
 			contextMenu = new ContextMenu();
			contextMenu.MenuItems.Add(mi1);
			contextMenu.MenuItems.Add(mi2);
			contextMenu.MenuItems.Add(mi3);
 			this.ContextMenu = contextMenu;
			mi1.Click += new EventHandler(SetDOB);
			mi2.Click += new EventHandler(SetDOT);
			mi3.Click += new EventHandler(Display);
		}

		//デストラクター
		~BrmBox()
		{
			if(hGraph != null)
				hGraph.Dispose();					//グラフィックリソースの開放
			if(Canvas != null)
				Canvas.Dispose();					//ビットマップリソースの開放
		}

		//コントロールがサイズ変更された場合
		private void Control_SizeChanged(object sender, EventArgs e)
		{
			//幅または高さが0の場合、「使用されたパラメーターが有効ではありません」エラーとなる為
			if(this.Width * this.Height == 0)
				return;
			Rectangle srcRect;
			Rectangle desRect;
			Image img = (Bitmap)Canvas.Clone();				//旧画像をBitmapで保存
			Canvas.Dispose();								//Canvasを一旦開放
			Canvas = new Bitmap(this.Width, this.Height);	//新しいサイズで作成
			if(this.Width > img.Width)
			{
				if(this.Height > img.Height)
				{
					srcRect = new Rectangle(0, 0, img.Width, img.Height);
					desRect = new Rectangle(0, 0, img.Width, img.Height);
				}
				else
				{
					srcRect = new Rectangle(0, 0, img.Width, this.Height);
					desRect = new Rectangle(0, 0, img.Width, this.Height);
				}
			}
			else
			{
				if(this.Height > img.Height)
				{
					srcRect = new Rectangle(0, 0, this.Width, img.Height);
					desRect = new Rectangle(0, 0, this.Width, img.Height);
				}
				else
				{
					srcRect = new Rectangle(0, 0, this.Width, this.Height);
					desRect = new Rectangle(0, 0, this.Width, this.Height);
				}
			}
			//CanvasのGraphicsクラスの再生成
			hGraph.Dispose();												//旧いサイズのCanvasのものを廃棄
			hGraph = Graphics.FromImage(Canvas);							//新しいサイズのCanvasのものを生成
			hGraph.SmoothingMode = SmoothingMode.AntiAlias;					//アンチエイリアス
			hGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;			//高品質画面
			hGraph.DrawImage(img, desRect, srcRect, GraphicsUnit.Pixel);	//旧いCanvasイメージ（img）のコピー
			this.Image = Canvas;											//Canvasに張り付け
			img.Dispose();													//使い終わったimgを開放
		}

		public void SetDOB(object sender = null, EventArgs e = null)
		{
			DateDialog dd = new DateDialog("生年月日を入力してください。");
			//OKボタンが押された場合
			if(dd.ShowDialog() == DialogResult.OK)
				DOB = dd.date;
			dd.Dispose();
		}

		public void SetDOT(object sender = null, EventArgs e = null)
		{
			DateDialog dd = new DateDialog("バイオリズムを見る年月日を指定してください。");
			//OKボタンが押された場合
			if(dd.ShowDialog() == DialogResult.OK)
				DOT = dd.date;
			dd.Dispose();
		}

		public void Display(object sender = null, EventArgs e = null)
		{
			//初期設定
			TimeSpan ts = DOT - DOB;		//誕生から指定日までの経過日数
			Color oldcol = this.ForeColor;	//現在の前景色を退避
			hGraph.Clear(this.BackColor);	//画面消去
			//使用するブラシの生成
			Brush RBrush = new SolidBrush(Color.Red);
			Brush GBrush = new SolidBrush(Color.Green);
			Brush BBrush = new SolidBrush(Color.Blue);
			Brush VBrush = new SolidBrush(Color.BlueViolet);
			//身体、感情、知性の色定義を表示する
			hGraph.DrawString("身体周期(Physical Cycle):", System.Drawing.SystemFonts.DefaultFont, RBrush, 10, 10, new StringFormat(StringFormatFlags.NoWrap));
			hGraph.DrawString("感情周期(Emotional Cycle):", System.Drawing.SystemFonts.DefaultFont, GBrush, 10, 30, new StringFormat(StringFormatFlags.NoWrap));
			hGraph.DrawString("知性周期(Intellectual Cycle):", System.Drawing.SystemFonts.DefaultFont, BBrush, 10, 50, new StringFormat(StringFormatFlags.NoWrap));
			//バイオリズムグラフ表示領域の画面設定
			//黒枠と基準線
			hGraph.DrawRectangle(Pens.Black, 10, 80, this.Width - 20, this.Height - 90);
			int VCenter = (this.Height - 70) / 2 + 70;	//バイオリズムグラフ描画領域の水平中央線のy座標
			hGraph.DrawLine(Pens.Black, 10, VCenter, this.Width - 10, VCenter);
			//日を表す縦線	Color.Silver or Color.DarkGray
			for(int i = 1; i < 15; i++)
				hGraph.DrawLine(Pens.Silver, 10 + (int)(i * (double)(this.Width - 20) / 30.0), 80, 10 + (int)(i * (double)(this.Width - 20) / 30.0), this.Height - 10);
			for(int i = 16; i < 30; i++)
				hGraph.DrawLine(Pens.Silver, 10 + (int)(i * (double)(this.Width - 20) / 30.0), 80, 10 + (int)(i * (double)(this.Width - 20) / 30.0), this.Height - 10);
			//指定日のみ青紫で表示
			hGraph.DrawLine(Pens.BlueViolet, this.Width / 2, 80, this.Width / 2, this.Height - 10);
			hGraph.DrawString(DOT.ToString("yyyy年MM月dd日"), System.Drawing.SystemFonts.DefaultFont, VBrush, this.Width / 2 - 40, 64, new StringFormat(StringFormatFlags.NoWrap));
			//正弦波グラフを横（10～this.Width - 10）、縦（80～this.Height - 10）に表示する
			int oldx = 0, oldPy = 0, oldEy = 0, oldIy = 0;
			string Pstr = "", Estr = "", Istr = "";	//評価表示用文字列
			for(double i = 0; i < 30; i += 0.1)		//指定日を中央に30日のデータを表示
			{	//グラフのx座標の範囲：10 ～ this.Width - 10
				//バイオリズム計算（グラフy座標）：sin(2πt/T): tは経過日数、Tは23(P), 28(S), 33(I)日
				double t = (double)(ts.Days + i - 15);
				int x = 10 + (int)(i * (double)(this.Width - 20) / 30.0);
				int Py = (int)(-Math.Sin(Math.PI * 2 * t / 23.0) * (double)(this.Height - 100) / 2) + this.Height / 2 + 40;
				int Ey = (int)(-Math.Sin(Math.PI * 2 * t / 28.0) * (double)(this.Height - 100) / 2) + this.Height / 2 + 40;
				int Iy = (int)(-Math.Sin(Math.PI * 2 * t / 33.0) * (double)(this.Height - 100) / 2) + this.Height / 2 + 40;
				if((int)i * 10 == 150)	//基準日処理－if(i == 15.0 または 15d)やif(i.Equal(15.0 または 15d))は失敗する。Why?
				/* <<<< ChatGPTの回答 >>>>
				提供されたコードは、for ループを使用して変数 i を0から30まで0.1ずつ増加させながら処理を行っています。
				ループ内の条件式は、変数 i の整数部分が15と等しい場合に条件を満たします。
				問題が発生する理由は、浮動小数点数の計算における精度の問題にあります。浮動小数点数は2進数で表現されるため、
				十進数の値を正確に表現することができない場合があります。したがって、 double 型の変数 i の値が厳密に15となる
				ことはありません。
				具体的には、ループ内で i に0.1を加えていくプロセスにおいて、小数点以下の値を2進数で正確に表現することができ
				ません。その結果、 i の値が理想的な値とは微妙に異なることがあります。
				このため、(int)i == 15 の条件式が厳密に成立しない場合があります。代わりに、浮動小数点数の比較を行う場合は、
				許容誤差を考慮する必要があります。*/
				{
					if(Py < VCenter)
						Pstr = "高調期";
					else if(Py == VCenter)
						Pstr = "不安定日";
					else
						Pstr = "低調期";
					if(Ey < VCenter)
						Estr = "高調期";
					else if(Ey == VCenter)
						Estr = "不安定日";
					else
						Estr = "低調期";
					if(Iy < VCenter)
						Istr = "高調期";
					else if(Iy == VCenter)
						Istr = "不安定日";
					else
						Istr = "低調期";
				}
				if(i > 0)	//最初の始点は描画しない
				{
					hGraph.DrawLine(Pens.Red, oldx, oldPy, x, Py);
					hGraph.DrawLine(Pens.Green, oldx, oldEy, x, Ey);
					hGraph.DrawLine(Pens.Blue, oldx, oldIy, x, Iy);
				}
				oldx = x;
				oldPy = Py;
				oldEy = Ey;
				oldIy = Iy;
			}
			//身体、感情、知性の評価を表示する
			hGraph.DrawString(Pstr, System.Drawing.SystemFonts.DefaultFont, RBrush, 160, 10, new StringFormat(StringFormatFlags.NoWrap));
			hGraph.DrawString(Estr, System.Drawing.SystemFonts.DefaultFont, GBrush, 160, 30, new StringFormat(StringFormatFlags.NoWrap));
			hGraph.DrawString(Istr, System.Drawing.SystemFonts.DefaultFont, BBrush, 160, 50, new StringFormat(StringFormatFlags.NoWrap));
			//バイオリズムグラフを表示する
			this.Image = Canvas;
			//ブラシの開放
			RBrush.Dispose();
			GBrush.Dispose();
			BBrush.Dispose();
			VBrush.Dispose();
			this.ForeColor = oldcol;	//前景色を元に戻す
		}
	}

	/////////////////////
	//日付入力ダイアログ
	/////////////////////
	class DateDialog : Form
	{
		//日付出力用DateTImeクラスオブジェクト
		private DateTime _date;	
		public DateTime date
		{
			get
			{
				return _date;
			}
		}
		string message;						//ダイアログ表示メッセージ
		ComboBox yyList, mmList, ddList;	//年月日入力用コンボボックス
		Button OkBtn, CancelBtn;			//ボタンコントロール

		public DateDialog(string str)
	 	{
			//メッセージを記録
			message = str;
			// ダイアログボックス用の設定
			this.Text = "日付入力";
			this.MaximizeBox = false;			//最大化ボタン
			this.MinimizeBox = false;			//最小化ボタン
			this.FormBorderStyle = FormBorderStyle.FixedDialog;		//境界のスタイル
			this.StartPosition = FormStartPosition.CenterParent;	// 親フォームの中央に配置
			this.Size = new Size(240, 200);
			this.Load += Dlg_Load;
			this.AcceptButton = OkBtn;		// Enter キーで選択できるボタン
			this.CancelButton = CancelBtn;	// Esc キーで選択できるボタン
		}

		private void Dlg_Load(object sender, EventArgs e)
		{
			//メッセージラベル
			Label label = new Label();
			label.Location = new Point(10, 10);
			label.AutoSize = true;
			label.Text = message;
			this.Controls.Add(label);
			//コンボボックス（yyList）
			Label yylabel = new Label();
			yylabel.Location = new Point(10, label.Height + 20);
			yylabel.AutoSize = true;
			yylabel.Text = "年（西暦）";
			this.Controls.Add(yylabel);
			yyList = new ComboBox();
			yyList.Location = new Point(yylabel.Width + 20, label.Height + 20);
			for(int i = DateTime.Today.Year; i >= 1923; i--)	//100年分リスト
			{
				yyList.Items.Add(i.ToString("D4"));
			}
			this.Controls.Add(yyList);
			//コンボボックス（mmList)
			Label mmlabel = new Label();
			mmlabel.Location = new Point(10, label.Height + yylabel.Height + 30);
			mmlabel.AutoSize = true;
			mmlabel.Text = "月";
			this.Controls.Add(mmlabel);
			mmList = new ComboBox();
			mmList.Location = new Point(yylabel.Width + 20, label.Height + yyList.Height + 30);
			for(int i = 1; i <= 12; i++)	//12月	分リスト
			{
				mmList.Items.Add(i.ToString("D2"));
			}
			this.Controls.Add(mmList);
			//コンボボックス（ddList）
			Label ddlabel = new Label();
			ddlabel.Location = new Point(10, label.Height + yylabel.Height + mmlabel.Height + 40);
			ddlabel.AutoSize = true;
			ddlabel.Text = "日";
			this.Controls.Add(ddlabel);
			ddList = new ComboBox();
			ddList.Location = new Point(yylabel.Width + 20, label.Height + yyList.Height + mmList.Height + 40);
			for(int i = 1; i <= 31; i++)	//31日分リスト
			{
				ddList.Items.Add(i.ToString("D2"));
			}
			this.Controls.Add(ddList);
			//Cancelボタン
			CancelBtn = new Button();
			CancelBtn.Location = new Point(10, ClientSize.Height - CancelBtn.Height - 10);
			CancelBtn.Text = "Cancel";
			CancelBtn.AutoSize = true;
			CancelBtn.Click += CancelButton_Click;
			this.Controls.Add(CancelBtn);
			//OKボタン
			OkBtn = new Button();
			OkBtn.Location = new Point(ClientSize.Width - OkBtn.Width - 10, ClientSize.Height - OkBtn.Height - 10);
			OkBtn.Text = "OK";
			OkBtn.AutoSize = true;
			OkBtn.Click += OkButton_Click;
			this.Controls.Add(OkBtn);
		}

		private void OkButton_Click(object sender, EventArgs e)
		{
			//OKボタン
			try
			{
				_date = DateTime.Parse(yyList.Text + "/" + mmList.Text + "/" + ddList.Text);
				//_date = DateTime.Parse(yyList.SelectedItem.ToString() + "/" + mmList.SelectedItem.ToString() + "/" + ddList.SelectedItem.ToString());
			}
			catch
			{
				MessageBox.Show("選択された日付が不正です", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
				this.DialogResult = DialogResult.None;	// Meaning false
				this.Close();
				return;
			}
			this.DialogResult = DialogResult.OK;		// Meaning true
			this.Close();
		}

		private void CancelButton_Click(object sender, EventArgs e)
		{
			this.DialogResult = DialogResult.Cancel;	// Meaning true
			this.Close();
		}
	}
}
