//------------------------------------------------------------------------------
//	Form1 で使っているイベントハンドラおよび描画以外のメソッド
//
//		作成者：三上直樹，2023/06/23 作成，©三上直樹 2023
//------------------------------------------------------------------------------

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

using Mikami.WindowsApplication;

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

namespace IIR_Design
{
	public partial class Form1 : Form
	{
		// フィルタの形状により，入力可／不可な箇所を有効にする
		private void EnableCtrlShape()
		{
			switch (formType_.Checked)
			{
				case "バ":
					CommonProc.Disable(label4, label5, label6, label7, textBox3, textBox4);
					break;
				case "チ":
					CommonProc.Enable(label4, label5, textBox3);
					CommonProc.Disable(label6, label7, textBox4);
					break;
				case "逆":
					CommonProc.Enable(label6, label7, textBox4);
					CommonProc.Disable(label4, label5, textBox3);
					break;
				case "連":
					CommonProc.Enable(label4, label5, label6, label7, textBox3, textBox4);
					break;
			}
		}

		// フィルタの通過／阻止帯域により，入力可／不可な箇所を有効にする
		private void EnableCtrlBand()
		{
			if (passBand_.IsChecked("LPF", "HPF"))
				CommonProc.Disable(label10, label11, textBox6);
			else
				CommonProc.Enable(label10, label11, textBox6);
		}


		// 設計パラメータの入力
		private bool ParmInput(out int order, out double eps, out double scale, out double delta)
		{
			scale = eps = delta = 0;

			int min_order = 1;
			int max_order = (passBand_.IsChecked("LPF", "HPF")) ? MAX_ORDER_ : MAX_ORDER_/2;

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

			// 設計されるディジタルフィルタの伝達関数の次数設定
			if (passBand_.IsChecked("LPF", "HPF")) zOrd_ = order;
			else								   zOrd_ = order*2;

			if (!textBox2.ToDouble(out fSkHz_)) return false;
			fSample_ = Abs(fSkHz_)*1.0E3;

			if (!textBox5.ToDouble(out fcLow_,
								   v => (v > 0.0) && (v < fSkHz_/2),
								   "遮断周波数1を標本化周波数の 1/2 未満の正の値に設定してください．"
								   )) return false;	// 遮断周波数

			if (passBand_.IsChecked("BPF", "BRF"))	// BPF, BRF の場合
			{
				if (!textBox6.ToDouble(out fcHigh_,
									   v => (v > 0.0) && (v < fSkHz_/2) && (v > fcLow_),
									   "遮断周波数２を標本化周波数の 1/2 未満の正の値で，\r\n"+
									   "遮断周波数１より大きな値に設定してください．"
									   )) return false;
			}

			fcLow_ = fcLow_*1000.0; 			// kHz 単位に換算
			fcHigh_ = fcHigh_*1000.0; 			// kHz 単位に換算
			if (passBand_.IsChecked("LPF", "HPF")) fcHigh_ = 0;

			// 通過域のリップル
			if (formType_.IsChecked("チ", "連"))
			{
				if (!textBox3.ToDouble(out dbRrpl_, v => v > 0.0,
					"通過域のリップルを 0 より大きな値に設定してください．")) return false;

				scale = (order & 1) == 0 ? Pow(10.0, dbRrpl_/20.0) : 1.0;
				if (formType_.IsChecked("チ"))
					delta = Pow(10.0, dbRrpl_/20.0);			// Chebyshev
				else
					eps = Sqrt(Pow(10.0, dbRrpl_/10.0) - 1.0);	// Elliptic
			}
			else
			{
				// 阻止域のリップル
				if (formType_.IsChecked("逆"))
				{
					if (!dBdownInput()) return false;
					delta = Pow(10.0, dbD_/20.0);
				}
				scale = 1.0;
			}

			return true;
		}

		// 阻止域の減衰量の入力
		private bool dBdownInput()
		{
			return textBox4.ToDouble(out dbD_, v => v > 0.0,
									 "阻止域の減衰量は正の値を入力してください．");
		}

		// Elliptic フィルタの設計パラメータ入力その他
		private bool EllipInp(double order, double fa, double eps, double arg,
							  out double cgam, out double Kk, out double k, out double u,
							  out double k2, out double wc, out double cbp)
		{
			wc = u = Kk = k2 = k = cbp = 0;

			cgam = Cos((fa + fcLow_)*PI/fSample_)/Cos(arg);
			if (!dBdownInput()) return false;

			double m1 = eps/Sqrt(Pow(10.0, dbD_/10.0) - 1.0);
			m1 = m1*m1;
			k = Jacobi.Modulus(Jacobi.EllipC(m1)/(order*Jacobi.EllipC(1.0 - m1)));
			double wr = (passBand_.IsChecked("HPF", "BRF")) ? k : 1.0/k;

			double f3;
			if (passBand_.IsChecked("LPF", "HPF"))
				f3 = Atan(Tan(arg)*wr)*fSample_/PI;		// LPF, HPF の場合
			else
			{											// BPF, BRF の場合
				double wrtan = wr*Tan(arg);
				double wrtan2 = wrtan*wrtan;
				double tmp = (cgam + Sqrt(wrtan2*((1.0 - cgam*cgam) + wrtan2)))/(1.0 + wrtan2);
				f3 = (PI/2.0 - Asin(tmp))*fSample_/(2.0*PI);
			}

			double ang = f3*PI/fSample_;
			double cang = Cos(ang);
			double sang = Sin(ang);

			if (passBand_.IsChecked("LPF", "HPF"))
				wr = sang/(cang*Tan(arg));						// LPF, HPF の場合
			else												// BPF, BRF の場合
			{
				double q = cang*cang - sang*sang;
				sang = 2.0*cang*sang;
				cang = q;
				wr = (cgam - cang)/(sang*Tan(arg));
			}

			if (passBand_.IsChecked("HPF", "BRF")) wr = 1.0/wr;	// HPF, BRF の場合
			wr = Abs(wr);
			cbp = wr;
			LambdaPlane(order, eps, wr, out Kk, out u, out k2, out wc);

			return true;
		}

		// 伝達関数の係数などの出力
		private void XferFuncOut(RichTextBox rtb)
		{
			rtb.Clear();
			string fmt = Format(groupBox3);

			switch (passBand_.Checked)
			{
				case "LPF": rtb.AppendText("// 低域通過フィルタ\r\n"); break;
				case "HPF": rtb.AppendText("// 高域通過フィルタ\r\n"); break;
				case "BPF": rtb.AppendText("// 帯域通過フィルタ\r\n"); break;
				case "BRF": rtb.AppendText("// 帯域阻止フィルタ\r\n"); break;
			}

			rtb.AppendText("// " + (new RadioGroup(groupBox1)).Checked + "特性\r\n");
			rtb.AppendText($"// 次数　　　　： {zOrd_} 次\r\n");
			rtb.AppendText($"// 標本化周波数：{fSample_/1.0E3, 8:F4} kHz\r\n");

			if (!textBox6.Enabled)
				rtb.AppendText($"// 遮断周波数  ：{fcLow_/1.0E3, 8:F4} kHz\r\n");
			else
			{
				rtb.AppendText($"// 遮断周波数１：{fcLow_/1.0E3, 8:F4} kHz\r\n");
				rtb.AppendText($"// 遮断周波数２：{fcHigh_/1.0E3, 8:F4} kHz\r\n");
			}
			if (formType_.IsChecked("チ", "連"))
				rtb.AppendText($"// 通過域のリップル：{dbRrpl_, 5:F2} dB\r\n");

			if (formType_.IsChecked("逆", "連"))
				rtb.AppendText($"// 阻止域の減衰量　：{dbD_, 5:F2} dB\r\n");

			rtb.AppendText("\r\n");
			rtb.AppendText("直接形の係数\r\n");
			if (radioButton9.Checked) rtb.AppendText(" m    分母 (am)        分子 (bm)\r\n");
			else					  rtb.AppendText(" m       分母 (am)           分子 (bm)\r\n");

			// 直接形の係数の出力
			if (radioButton9.Checked)
				rtb.AppendText(" 0 " + "                 " + bk_[0].ToString(fmt) + "\r\n");
			else
				rtb.AppendText(" 0 " + "                    " + bk_[0].ToString(fmt) + "\r\n");
			for (int n=1; n<=zOrd_; n++)
				rtb.AppendText($"{n, 2:D} " + (-ak_[n]).ToString(fmt) + " "
											+ bk_[n].ToString(fmt) + "\r\n");

			// 縦続形の係数の出力
			CascadeOut(richTextBox1, "縦続形の係数", 0.0, cm_);
			rtb.AppendText("\r\n");
			rtb.AppendText("利得定数 = " + gain_.ToString(fmt) + "\r\n");
		}

		// 縦続形の係数の出力
		private void CascadeOut(RichTextBox rtb, String s, double scl, Coefs[] cm)
		{
			string fmt = Format(groupBox3);
			const string rn = "\r\n";

			rtb.AppendText(rn + s);
			if (scl != 0.0)
			{
				rtb.AppendText(rn);
				rtb.AppendText("s0  = " + scl.ToString(fmt) + rn);
			}
			for (int n=0; n<(zOrd_+1)/2; n++)
			{
				string nEq = (n+1).ToString() + " = ";
				rtb.AppendText(rn);
				rtb.AppendText("a1" + nEq + cm[n].a1.ToString(fmt) + rn);
				if (cm[n].a2 != 0.0)		// 複素極のステージのみ出力
					rtb.AppendText("a2" + nEq + cm[n].a2.ToString(fmt) + rn);

				rtb.AppendText("b0" + nEq + cm[n].b0.ToString(fmt) + rn);
				rtb.AppendText("b1" + nEq + cm[n].b1.ToString(fmt) + rn);
				if (cm[n].b2 != 0.0)		// 複素極のステージのみ出力
					rtb.AppendText("b2" + nEq + cm[n].b2.ToString(fmt) + rn);
			}
		}

		// RadioButton の float，double に応じた書式を設定
		private string Format(GroupBox obj)
		{
			bool floatChecked = obj.Controls.OfType<RadioButton>()
								   .First(x => x.Text == "float").Checked;
			string s1 = floatChecked ? " 0.00000000E+00f" : " 0.000000000000E+00";
			string s2 = s1 + ";-" + s1.Remove(0, 1);	// 負の数への対応
			return s2;
		}

		// 正規化角周波数に対する振幅特性の値
		private double AbsHw(double w, double gain)
		{
			Complex z1 = Exp(-w*ImaginaryOne);	// 1/z
			Complex hz = gain;

			foreach (var c in cm_)
				hz = hz*(1.0 + (c.b1 + c.b2*z1)*z1)/(1.0 - (c.a1 + c.a2*z1)*z1);

			return Abs(hz);
		}

		// 振幅特性の計算
		private void CalcAmp(ref float[] logA_k)
		{
			double dw = PI/F_DIV_;
			for (int n=0; n<=F_DIV_; n++)
			{
				double x = AbsHw(n*dw, gain_);
				x = (x < 1.0E-10) ? -100 : 20.0*Log10(x);
				logA_k[n] = (float)x;
			}
		}
	}
}
