//------------------------------------------------------------------------------
//	その他のメソッド
//
//		作成者：三上直樹，2023/03/29 作成，(C)三上直樹 2023
//------------------------------------------------------------------------------

using System;
using System.Windows.Forms;
using System.Numerics;

using Mikami.WindowsApplication;
using Mikami.Remez;

using static System.Math;
using static System.Numerics.Complex;

namespace FIR_Design_Remez
{
	public partial class Form1 : Form
	{
		// 設計パラメータの入力
		private bool SpecInput()
		{
			int minOrder = 1;

			// フィルタの次数の入力
			if (!textBox1.ToInt(out nOrder_,
								v => (v >= minOrder) && (v <= MAX_ORDER_),
								"フィルタの次数を " + minOrder.ToString() + " 以上，"
								+ MAX_ORDER_.ToString() + " 以下にしてください．"
								)) return false;

			// 標本化周波数の入力
			if (!textBox2.ToDouble(out fSkHz_, x => x > 0,
				"標本化周波数は正の値にしてください．")) return false;
			fSample_ = Abs(fSkHz_)*1.0E3;			// Hz 単位に変換		

			// TextBox 行列のパラメータの数値化
			string msgFr = "帯域端周波数を0以上，標本化周波数/2 以下にしてください．";
			bool FrCheck(double x) => (x >= 0.0) && (x <= fSkHz_ / 2);
			string msgZ1 = "0 以上, 1 以下の数にしてください";
			bool GnChek(double x) => (x >= 0) && (x <= 1);
			string msgZ2 = "0 より大きい数にしてください";
			bool WtChek(double x) => x > 0;
			for (int n=0; n<nBand_; n++)
			{
				if (!parameters_[0, n].ToDouble(out bands_[n*2], FrCheck, msgFr)) return false;
				if (!parameters_[1, n].ToDouble(out bands_[n*2+1], FrCheck, msgFr)) return false;
				if (!parameters_[2, n].ToDouble(out gain_[n], GnChek, msgZ1)) return false;
				if (!parameters_[3, n].ToDouble(out weight_[n], WtChek, msgZ2)) return false;
			}

			// 帯域端の周波数設定の検査
			for (int n=1; n<2*nBand_; n++)
				if (bands_[n-1] >= bands_[n])
				{
					MessageBox.Show("帯域端周波数の設定が間違っています．");
					if ((n % 2) == 0) parameters_[0, n/2].Select();
					else			  parameters_[1, n/2].Select();
					((TextBox)ActiveControl).SelectAll();
					return false;
				}

			// 通過域／阻止域型で一番高い帯域の利得が 0 ではない奇数次のフィルタの設計を禁止
			if ((type_ == Remez.FType.passStop) && (gain_[nBand_-1] != 0.0)
												&& ((nOrder_ % 2) != 0))
			{
				MessageBox.Show("次数は偶数にしてください．");
				textBox1.Select();
				textBox1.SelectAll();
				return false;
			}
			return true;
		}

		// TextBox 行列の見た目のコントロール
		private void TextBoxCtrl()
		{
			if (type_ == Remez.FType.passStop)	// 通過域／阻止域型の場合
			{
				CommonProc.Enable(label2, label5, label6,
								  parameters_[2, 0], parameters_[3, 0]);

				BandEnableOnOff((int)numericUpDown1.Value);
			}
			else								// 微分器，ヒルベルト変換器の場合
			{
				nBand_ = 1;
				for (int n=1; n<MAX_BANDS_; n++)
				{
					labelBand_[n].Enabled = false;
					for (int k=0; k<4; k++)	parameters_[k, n].Enabled = false;
				}
				
				parameters_[3, 0].Text = "1";
				CommonProc.Disable(label2, parameters_[3, 0], label6);

				if (type_ == Remez.FType.diff)
					CommonProc.Enable(parameters_[2, 0], label5);
				else
				{
					parameters_[2, 0].Text = "1";
					CommonProc.Disable(parameters_[2, 0], label5);
				}
			}
		}

		// 設計の結果を RitchTextBox へ出力する
		private void OutputToRitchTextBox()
		{
			richTextBox1.Clear();

			ParmWriteNum("// 標本化周波数 (kHz)     ", fSkHz_);
			ParmWriteNum("// 次数                   ", nOrder_);

			richTextBox1.AppendText("//                       ");
			for (int n=0; n<nBand_; n++)
				richTextBox1.AppendText("      " + labelBand_[n].Text);
			richTextBox1.AppendText("\r\n");
			
			ParmWrite("// 下側帯域端周波数 (kHz) ", 0);
			ParmWrite("// 上側帯域端周波数 (kHz) ", 1);
			ParmWrite("// 利得                   ", 2);
			ParmWrite("// 重み                   ", 3);
			ParmWriteNum("// 偏差                   ", deviat_);
			double[] devDb = new double[nBand_];
			for (int n=0; n<nBand_; n++)
				devDb[n] = 20.0*Log10(gain_[n] + deviat_[n]);
			ParmWriteNum("// 偏差 [dB]              ", devDb);

			if (radioButton6.Checked) WriteInt((int)(numericUpDown2.Value));
			else					  WriteFloatDouble((int)(numericUpDown2.Value));
		}

		// float, double の場合の出力
		private void WriteFloatDouble(int num)
		{
			string s1 = radioButton4.Checked ? " 0.000000E+00f" :
											   " 0.000000000000E+00";
			string fmt = s1 + ";-" + s1.Remove(0, 1);	// 負の数への対応

			string sOut = "        ";
			int count = 0;
			for (int n=0; n<=nOrder_; n++)
			{
				sOut = sOut + " " + hk_[n].ToString(fmt) + ",";
				count++;
				if (count >= num)
				{
					if (n == (hk_.Length - 1))
						sOut = sOut.Remove(sOut.Length - 1);
					richTextBox1.AppendText(sOut + "\r\n");
					count = 0;
					sOut = "        ";
				}
			}
			if (sOut != "        ")
				richTextBox1.AppendText(sOut.Remove(sOut.Length - 1) + "\r\n");
		}

		// int の場合の出力
		private void WriteInt(int num)
		{
			string sOut = "        ";
			int count = 0;
			for (int n=0; n<=nOrder_; n++)
			{
				sOut += $" {(int)Round(32768*hk_[n]), 6:D},";
				count++;
				if (count >= num)
				{
					if (n == (hk_.Length - 1))
						sOut = sOut.Remove(sOut.Length - 1);
					richTextBox1.AppendText(sOut + "\r\n");
					count = 0;
					sOut = "        ";
				}
			}
			if (sOut != "        ")
				richTextBox1.AppendText(sOut.Remove(sOut.Length - 1) + "\r\n");
		}

		// TextBox 行列のパラメータ値をリッチテキストボックスへ出力する
		private void ParmWrite(string s, int itemIndex)
		{
			for (int n=0; n<nBand_; n++)
				s += TextToNumText(parameters_[itemIndex, n].Text);
			richTextBox1.AppendText(s + "\r\n");
			string TextToNumText(string str) => $"{Double.Parse(str), 12:F6}";
		}

		// doube[] のパラメータ値をリッチテキストボックスへ出力する
		private void ParmWriteNum(string s, double[] x)
		{
			for (int n=0; n<nBand_; n++) s += $"{x[n], 12:F6}";
			richTextBox1.AppendText(s + "\r\n");
		}

		// doube のパラメータ値をリッチテキストボックスへ出力する
		private void ParmWriteNum(string s, double x) =>
			richTextBox1.AppendText(s + $"{x, 12:F6}" + "\r\n");

		// int のパラメータ値をリッチテキストボックスへ出力する
		private void ParmWriteNum(string s, int x) =>
			richTextBox1.AppendText(s + $"{x, 12:D}" + "\r\n");

		// 振幅特性の計算
		private void CalcAmp(ref float[] logA_k)
		{
			double dw = PI/F_DIV_;
			float Amin_dB = (float)(20.0*Log10(A_MIN_));
			for (int n=0; n<logA_k.Length; n++)
			{
				double x = HwA(n*dw);
				logA_k[n] = (x < A_MIN_) ? Amin_dB : (float)(20.0*Log10(x));
			}
		}

		// 正規化角周波数を与えてそのときの振幅特性の値を得る
		private double HwA(double w)
		{
			Complex jw = -ImaginaryOne*w;
			Complex hz = Zero;
			for (int k=0; k<=nOrder_; k++) hz += hk_[k]*Exp(jw*k);
			return Abs(hz);
		}
	}
}
