//////////////////////////////
//		C# Biorythm.cs
// Biorythm Control Component
//////////////////////////////
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;				//Assemblyを使う為
using Atelier;							//Atelier.dllを使う
//using System.Web.UI.WebControls;		//DropDownListを使う為

namespace Biorythm
{
	public partial class MyForm : Form
	{
		PictureBox picBox;					//ピクチャーボックスコントロール
		Button btn1, btn2, btn3, extbtn;	//ボタンコントロール
		Easel easel;						//描画を行うEaselオブジェクト
		//Biorythmで使用する変数
		DateTime DOB;						//誕生日（Date of Birth）
		DateTime DOT;						//指定日（Date of Traget）

		[STAThread]
		public static void Main()
		{
			MyForm mf = new MyForm();
			Application.Run(mf);
		}

		public MyForm()
		{
			Assembly myOwn = Assembly.GetEntryAssembly();
			this.Icon = Icon.ExtractAssociatedIcon(myOwn.Location);	//プログラムアイコンをフォームにつける
			this.Size = new Size(640, 540);
			this.MinimumSize = new Size(320, 190);
			this.Text = "Biorythm Cycle";
			this.Load += MyForm_Load;
			this.SizeChanged += MyForm_SizeChanged;
		}

		private void MyForm_Load(object sender, EventArgs e)
		{
			//ボタン1
			btn1 = new Button();
			btn1.Location = new Point(ClientSize.Width - btn1.Width - 10, 10);
			btn1.Text = "生年月日";
			btn1.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			btn1.Click += Button1_Click;
			this.Controls.Add(btn1);

			//ボタン2ボタン
			btn2 = new Button();
			btn2.Location = new Point(ClientSize.Width - btn2.Width - 10, btn1.Height + 20);
			btn2.Text = "指定日";
			btn2.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			btn2.Click += Button2_Click;
			this.Controls.Add(btn2);

			//ボタン3ボタン
			btn3 = new Button();
			btn3.Location = new Point(ClientSize.Width - btn3.Width - 10, btn1.Height + btn2.Height + 30);
			btn3.Text = "グラフ表示";
			btn3.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			btn3.Click += Button3_Click;
			this.Controls.Add(btn3);

			//終了ボタン
			extbtn = new Button();
			extbtn.Location = new Point(ClientSize.Width - extbtn.Width - 10, ClientSize.Height - extbtn.Height - 20);
			extbtn.Text = "終了";
			extbtn.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
			extbtn.Click += Button4_Click;
			this.Controls.Add(extbtn);

			//ピクチャーボックスコントロール
			picBox = new PictureBox();	//枠の中のPictureオブジェクトの作成
			picBox.BorderStyle = BorderStyle.Fixed3D;
			picBox.Location = new Point(10, 10);				//枠内の位置
			picBox.Width = ClientSize.Width - btn1.Width - 30;
			picBox.Height = ClientSize.Height - 20;
			picBox.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right);
			//描画先とするEaselオブジェクトを作成する（同時にGraphics、Pen、Brushオブジェクトも作成する）
			easel = new Easel(picBox.Width, picBox.Height);
			picBox.Image = easel.Canvas;					//easelのCanvasを貼る
			this.Controls.Add(picBox);						//picBoxをコントロールに追加する
		}

		//メインフォームのサイズ変更がプロパティに反映された場合
		private void MyForm_SizeChanged(object sender, EventArgs e)
		{
			easel.Resize(picBox.ClientSize.Width, picBox.ClientSize.Height);
			picBox.Image = easel.Canvas;
		}

		//終了処理
		protected override void OnFormClosing(FormClosingEventArgs e)
		{
			base.OnFormClosing(e);
			DialogResult dr = MessageBox.Show("終了しますか？", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
			if(dr == DialogResult.No)
			{
				e.Cancel = true;
			}
			else
				//PictureBoxのマネージド リソースとアンマネージド リソースを解放
				picBox.Dispose();
		}

		private void Button1_Click(object sender, EventArgs e)
		{
			//ボタン1
			DateDialog dd = new DateDialog("生年月日を入力してください。");
			//テスト
			if(dd.ShowDialog() == DialogResult.OK)
			{
				MessageBox.Show(dd.date.ToString(), "選択された生年月日", MessageBoxButtons.OK, MessageBoxIcon.Information);
				DOB = dd.date;
			}
			dd.Dispose();
		}

		private void Button2_Click(object sender, EventArgs e)
		{
			//ボタン2 - Phase 1
			DateDialog dd = new DateDialog("バイオリズムを見る年月日を指定してください。");
			//テスト
			if(dd.ShowDialog() == DialogResult.OK)
			{
				MessageBox.Show(dd.date.ToString(), "指定された年月日", MessageBoxButtons.OK, MessageBoxIcon.Information);
				DOT = dd.date;
			}
			dd.Dispose();
		}

		private void Button3_Click(object sender, EventArgs e)
		{
			//ボタン3
			//初期設定
			TimeSpan ts = DOT - DOB;		//誕生から指定日までの経過日数
			Color oldcol = easel.gFtColor;	//現在の前景色を退避
			easel.Clear();					//画面消去
			//経過日数計算のテスト
			//MessageBox.Show("経過日数は" + ts.Days.ToString() + "日です。", "経過日数", MessageBoxButtons.OK, MessageBoxIcon.Information);
			//int days = NumOfDays(DOB.Year, DOB.Month, DOB.Day, DOT.Year, DOT.Month, DOT.Day);
			//MessageBox.Show("経過日数は" + days.ToString() + "日です。", "VB6プログラムによる経過日数", MessageBoxButtons.OK, MessageBoxIcon.Information);
			//days = NODays(DOB.Year, DOB.Month, DOB.Day, DOT.Year, DOT.Month, DOT.Day);
			//if(days > 0)
			//	MessageBox.Show("経過日数は" + days.ToString() + "日です。", "愚直な日数計算による経過日数", MessageBoxButtons.OK, MessageBoxIcon.Information);
			//身体、感情、知性の色定義を表示する
			easel.gFtColor = Color.Red;
			easel.PrintText("身体周期(Physical Cycle):", 10, 10, new StringFormat(StringFormatFlags.NoWrap));
			easel.gFtColor = Color.Green;
			easel.PrintText("感情周期(Emotional Cycle):", 10, 30, new StringFormat(StringFormatFlags.NoWrap));
			easel.gFtColor = Color.Blue;
			easel.PrintText("知性周期(Intellectual Cycle):", 10, 50, new StringFormat(StringFormatFlags.NoWrap));
			//表示領域の画面設定
			//黒枠と基準線
			easel.gFtColor = Color.Black;
			easel.Box(10, 80, picBox.Width - 10, picBox.Height - 10);
			int VCenter = (picBox.Height - 70) / 2 + 70;	//バイオリズムグラフ描画領域の水平中央線
			easel.Line(10, VCenter, picBox.Width - 10, VCenter);
			//日を表す縦線
			easel.gFtColor = Color.Silver;	//Color.Silver or Color.DarkGray
			for(int i = 1; i < 15; i++)
				easel.Line(10 + (int)(i * (double)(picBox.Width - 20) / 30.0), 80, 10 + (int)(i * (double)(picBox.Width - 20) / 30.0), picBox.Height - 10);
			for(int i = 16; i < 30; i++)
				easel.Line(10 + (int)(i * (double)(picBox.Width - 20) / 30.0), 80, 10 + (int)(i * (double)(picBox.Width - 20) / 30.0), picBox.Height - 10);
			//指定日のみ青紫で表示
			easel.gFtColor = Color.BlueViolet;
			easel.Line(picBox.Width / 2, 80, picBox.Width / 2, picBox.Height - 10);
			easel.PrintText(DOT.ToString("yyyy年MM月dd日"), picBox.Width / 2 - 40, 64, new StringFormat(StringFormatFlags.NoWrap));
			//正弦波グラフを横（10～picBox.Width - 10）、縦（80～picBox.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 ～ picBox.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)(picBox.Width - 20) / 30.0);	//x座標
				//y座標は↑が0で↓が数が多くなるので、Math.Sinを負数にすることで反転している。
				int Py = (int)(-Math.Sin(Math.PI * 2 * t / 23.0) * (double)(picBox.Height - 100) / 2) + picBox.Height / 2 + 40;
				int Ey = (int)(-Math.Sin(Math.PI * 2 * t / 28.0) * (double)(picBox.Height - 100) / 2) + picBox.Height / 2 + 40;
				int Iy = (int)(-Math.Sin(Math.PI * 2 * t / 33.0) * (double)(picBox.Height - 100) / 2) + picBox.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)	//最初の始点は描画しない
				{
					easel.gFtColor = Color.Red;
					easel.Line(oldx, oldPy, x, Py);
					easel.gFtColor = Color.Green;
					easel.Line(oldx, oldEy, x, Ey);
					easel.gFtColor = Color.Blue;
					easel.Line(oldx, oldIy, x, Iy);
				}
				oldx = x;
				oldPy = Py;
				oldEy = Ey;
				oldIy = Iy;
			}
			//身体、感情、知性の評価を表示する
			easel.gFtColor = Color.Red;
			easel.PrintText(Pstr, 160, 10, new StringFormat(StringFormatFlags.NoWrap));
			easel.gFtColor = Color.Green;
			easel.PrintText(Estr, 160, 30, new StringFormat(StringFormatFlags.NoWrap));
			easel.gFtColor = Color.Blue;
			easel.PrintText(Istr, 160, 50, new StringFormat(StringFormatFlags.NoWrap));
			//バイオリズムグラフを表示する
			picBox.Image = easel.Canvas;
			easel.gFtColor = oldcol;	//前景色を元に戻す
		}

		private void Button4_Click(object sender, EventArgs e)
		{
			//終了処理
			Close();
		}

		//////////////////////////////////
		//VB6のプログラムにあった計算方法
		//////////////////////////////////
		public int NumOfDays(int bYear, int bMonth, int bDay, int tYear, int tMonth, int tDay)
		{
			if(bMonth == 1 || bMonth == 2)
			{
				bMonth = bMonth + 12;
				bYear = bYear - 1;
			}
			if(tMonth == 1 || tMonth == 2)
			{
				tMonth = tMonth + 12;
				tYear = tYear - 1;
			}
			int days = ((int)(365.25 * (double)tYear) + (int)(tYear / 400) - (int)(tYear / 100) + (int)(30.59 * (double)(tMonth - 2)) + tDay)
						 - ((int)(365.25 * (double)bYear) + (int)(bYear / 400) - (int)(bYear / 100) + (int)(30.59 * (double)(bMonth - 2)) + bDay);
			return days;
		}

		/////////////////
		//愚直な日数計算
		/////////////////
		public int NODays(int bYear, int bMonth, int bDay, int tYear, int tMonth, int tDay)
		{
			//エラーチェック
			if((bYear > tYear) || (bYear == tYear && bMonth > tMonth) || (bYear == tYear && bMonth == tMonth && bDay >= tDay))
			{
				MessageBox.Show("指定日付が不正（生年月日以前）です", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return 0;
			}
			//12か月の日数を定数とする
			int[] month = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
			//経過日数の初期化
			int days = 0;
			//生年月日初月の日数
			days = month[bMonth - 1] - bDay;
			//翌月から年末までの日数
			for(int i = bMonth; i < 12; i++)
				days += month[i];
			//初年のうるう年修正
			if(bMonth < 3 && CheckLeap(bYear))
				days++;
			//翌年から指定年前年までの日数
			for(int i = bYear + 1; i < tYear; i++)
			{
				days += 365;
				if(CheckLeap(i))
					days++;
			}
			//指定年の日数
			for(int i = 0; i < tMonth - 1; i++)
				days += month[i];
			//初年のうるう年修正
			if(CheckLeap(tYear) && (tMonth > 2))	//3月以降の場合
				days++;
			//指定月の日数（2月29日の指定の場合はtDayでうるう年調整される）
			days += tDay;
			return days;
		}

		///////////////////
		//うるう年判別関数
		///////////////////
		bool CheckLeap(int y) {
			//(1) 西暦年号が4で割り切れ、且つ (2) 「西暦年号が100で割り切れて且つ400で割り切れな」くない
			//return ((y % 4 == 0) && !(!(y % 100 == 0)) && (y % 400 != 0)) ? true : false;
			//(1) 西暦年号が4で割り切れ、且つ (2) 西暦年号が100で割り切れてない、または400で割り切れる
			return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
		}
	}

	/////////////////////
	//日付入力ダイアログ
	/////////////////////
	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 = 0; i < 100; i++)	//100年分リスト
			{
				yyList.Items.Add((DateTime.Today.Year - i).ToString("D4"));
			}
			this.Controls.Add(yyList);
			yyList.SelectedIndex = 0;		//現在年のインデックス
			//コンボボックス（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);
			mmList.SelectedIndex = DateTime.Today.Month - 1;	//現在月のインデックス
			//コンボボックス（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);
			ddList.SelectedIndex = DateTime.Today.Day - 1;		//現在日のインデックス
			//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();
		}
	}
}
