// ---------------------------------------------------------------------------
//	M88 - PC-8801 emulator
//	CRT フィルタのパラメータ設定ダイアログ (モードレス)
//	Copyright (C) 2026
//	2-clause BSD license
// ---------------------------------------------------------------------------

#include "headers.h"
#include "resource.h"
#include "wincrtcfg.h"
#include "messages.h"

using namespace PC8801;

// トラックバーの位置は「パラメータ値 × 1000」。
// Config は「× 10000」で持つので config = pos * 10 で換算する
static const int RANGE_SCANLINE   = 1000;   // 0.000 〜 1.000
static const int RANGE_VIGNETTE   = 1000;   // 0.000 〜 1.000
static const int RANGE_ABERRATION = 8000;   // 0.000 〜 8.000
static const int RANGE_CURVATURE  =  300;   // 0.000 〜 0.300

// ---------------------------------------------------------------------------
//	構築/消滅
//
WinCRTConfig::WinCRTConfig()
{
	hwnd = 0;
	hwndparent = 0;
	hinst = 0;
	config = 0;
}

WinCRTConfig::~WinCRTConfig()
{
}

// ---------------------------------------------------------------------------
//	スライダ位置(×1000) をラベルへ変換
//
static void SetValueText(HWND hdlg, int id, int pos)
{
	char buf[32];
	wsprintf(buf, "%d.%03d", pos / 1000, pos % 1000);
	SetDlgItemText(hdlg, id, buf);
}

// ---------------------------------------------------------------------------
//	ダイアログ表示
//
void WinCRTConfig::Show(HINSTANCE hinstance, HWND parent, Config* conf)
{
	if (hwnd)
	{
		SetFocus(hwnd);
		return;
	}
	hinst = hinstance;
	hwndparent = parent;
	config = conf;
	hwnd = CreateDialogParam(hinst, MAKEINTRESOURCE(IDD_CRTFILTER),
							 parent, DLGPROC((void*)DlgProcGate), (LPARAM)this);
	ShowWindow(hwnd, SW_SHOW);
}

void WinCRTConfig::Close()
{
	if (hwnd)
	{
		DestroyWindow(hwnd);
		hwnd = 0;
	}
}

bool WinCRTConfig::ProcMsg(MSG& msg)
{
	if (hwnd && IsDialogMessage(hwnd, &msg))
		return true;
	return false;
}

// ---------------------------------------------------------------------------
//	親から config が書き換わったことを知らせる (メニューでの ON/OFF 等)
//
void WinCRTConfig::UpdateControls()
{
	if (hwnd)
		UpdateSliders(hwnd);
}

// ---------------------------------------------------------------------------
//	初期化 (トラックバーの範囲・コンボの選択肢を設定してから表示を合わせる)
//
void WinCRTConfig::InitDialog(HWND hdlg)
{
	SendDlgItemMessage(hdlg, IDC_CRT_SCANLINE, TBM_SETRANGE, TRUE, MAKELONG(0, RANGE_SCANLINE));
	SendDlgItemMessage(hdlg, IDC_CRT_SCANLINE, TBM_SETPAGESIZE, 0, RANGE_SCANLINE / 20);
	SendDlgItemMessage(hdlg, IDC_CRT_VIGNETTE, TBM_SETRANGE, TRUE, MAKELONG(0, RANGE_VIGNETTE));
	SendDlgItemMessage(hdlg, IDC_CRT_VIGNETTE, TBM_SETPAGESIZE, 0, RANGE_VIGNETTE / 20);
	SendDlgItemMessage(hdlg, IDC_CRT_ABERRATION, TBM_SETRANGE, TRUE, MAKELONG(0, RANGE_ABERRATION));
	SendDlgItemMessage(hdlg, IDC_CRT_ABERRATION, TBM_SETPAGESIZE, 0, RANGE_ABERRATION / 20);
	SendDlgItemMessage(hdlg, IDC_CRT_CURVATURE, TBM_SETRANGE, TRUE, MAKELONG(0, RANGE_CURVATURE));
	SendDlgItemMessage(hdlg, IDC_CRT_CURVATURE, TBM_SETPAGESIZE, 0, RANGE_CURVATURE / 20);

	SendDlgItemMessage(hdlg, IDC_CRT_NATIVEHEIGHT, CB_ADDSTRING, 0, (LPARAM) "200");
	SendDlgItemMessage(hdlg, IDC_CRT_NATIVEHEIGHT, CB_ADDSTRING, 0, (LPARAM) "400");
	SendDlgItemMessage(hdlg, IDC_CRT_NATIVEHEIGHT, CB_ADDSTRING, 0, (LPARAM) "800");

	SendDlgItemMessage(hdlg, IDC_CRT_PRESET_TARGET, CB_ADDSTRING, 0, (LPARAM) "ユーザ1");
	SendDlgItemMessage(hdlg, IDC_CRT_PRESET_TARGET, CB_ADDSTRING, 0, (LPARAM) "ユーザ2");
	SendDlgItemMessage(hdlg, IDC_CRT_PRESET_TARGET, CB_ADDSTRING, 0, (LPARAM) "ユーザ3");
	SendDlgItemMessage(hdlg, IDC_CRT_PRESET_TARGET, CB_SETCURSEL, 0, 0);

	UpdateSliders(hdlg);
}

// ---------------------------------------------------------------------------
//	config → コントロール
//
void WinCRTConfig::UpdateSliders(HWND hdlg)
{
	int scanline   = config->crtscanline   / 10;
	int vignette   = config->crtvignette   / 10;
	int aberration = config->crtaberration / 10;
	int curvature  = config->crtcurvature  / 10;

	SendDlgItemMessage(hdlg, IDC_CRT_SCANLINE, TBM_SETPOS, TRUE, scanline);
	SetValueText(hdlg, IDC_CRT_SCANLINE_TEXT, scanline);
	SendDlgItemMessage(hdlg, IDC_CRT_VIGNETTE, TBM_SETPOS, TRUE, vignette);
	SetValueText(hdlg, IDC_CRT_VIGNETTE_TEXT, vignette);
	SendDlgItemMessage(hdlg, IDC_CRT_ABERRATION, TBM_SETPOS, TRUE, aberration);
	SetValueText(hdlg, IDC_CRT_ABERRATION_TEXT, aberration);
	SendDlgItemMessage(hdlg, IDC_CRT_CURVATURE, TBM_SETPOS, TRUE, curvature);
	SetValueText(hdlg, IDC_CRT_CURVATURE_TEXT, curvature);

	CheckDlgButton(hdlg, IDC_CRT_ENABLE,     (config->flag2 & Config::usecrtfilter) ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hdlg, IDC_CRT_SMOOTHSTEP, config->crtsmoothstep ? BST_CHECKED : BST_UNCHECKED);

	int sel = 1;	// 400 が既定
	if (config->crtscanlinenativeheight == 200)
		sel = 0;
	else if (config->crtscanlinenativeheight == 800)
		sel = 2;
	SendDlgItemMessage(hdlg, IDC_CRT_NATIVEHEIGHT, CB_SETCURSEL, sel, 0);

	// CRT が無効のときはパラメータ類を触れなくする (仕様書 §5.4)
	BOOL en = (config->flag2 & Config::usecrtfilter) ? TRUE : FALSE;
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_SCANLINE), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_VIGNETTE), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_ABERRATION), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_CURVATURE), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_SMOOTHSTEP), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_NATIVEHEIGHT), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_PRESET_OFF), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_PRESET_WEAK), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_PRESET_MID), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_PRESET_STRONG), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_PRESET_RECOMMEND), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_PRESET_TARGET), en);
	EnableWindow(GetDlgItem(hdlg, IDC_CRT_PRESET_SAVE), en);
}

// ---------------------------------------------------------------------------
//	コントロール → config (スライダ操作時)
//
void WinCRTConfig::ReadSliders(HWND hdlg)
{
	int scanline   = (int) SendDlgItemMessage(hdlg, IDC_CRT_SCANLINE, TBM_GETPOS, 0, 0);
	int vignette   = (int) SendDlgItemMessage(hdlg, IDC_CRT_VIGNETTE, TBM_GETPOS, 0, 0);
	int aberration = (int) SendDlgItemMessage(hdlg, IDC_CRT_ABERRATION, TBM_GETPOS, 0, 0);
	int curvature  = (int) SendDlgItemMessage(hdlg, IDC_CRT_CURVATURE, TBM_GETPOS, 0, 0);

	config->crtscanline   = scanline   * 10;
	config->crtvignette   = vignette   * 10;
	config->crtaberration = aberration * 10;
	config->crtcurvature  = curvature  * 10;

	SetValueText(hdlg, IDC_CRT_SCANLINE_TEXT, scanline);
	SetValueText(hdlg, IDC_CRT_VIGNETTE_TEXT, vignette);
	SetValueText(hdlg, IDC_CRT_ABERRATION_TEXT, aberration);
	SetValueText(hdlg, IDC_CRT_CURVATURE_TEXT, curvature);
}

// ---------------------------------------------------------------------------
//	プリセットを適用 (smoothstep と 走査線の本数 には触らない)
//
void WinCRTConfig::SetPreset(int scanline, int vignette, int aberration, int curvature)
{
	config->crtscanline   = scanline;
	config->crtvignette   = vignette;
	config->crtaberration = aberration;
	config->crtcurvature  = curvature;
}

// ---------------------------------------------------------------------------
//	親へ反映を依頼する
//
//	WM_M88_APPLYCONFIG は memcmp で差分を見るため、同一の config
//	オブジェクトを直接書き換えている本ダイアログでは素通りしてしまう。
//	専用メッセージ WM_M88_APPLYCRTPARAM で直接 ApplyCRTParams() を呼ばせる
//
void WinCRTConfig::NotifyParent()
{
	if (hwndparent)
		PostMessage(hwndparent, WM_M88_APPLYCRTPARAM, 0, 0);
}

// ---------------------------------------------------------------------------
//	ダイアログ処理
//
INT_PTR WinCRTConfig::DlgProc(HWND hdlg, UINT msg, WPARAM wp, LPARAM lp)
{
	switch (msg)
	{
	case WM_INITDIALOG:
		InitDialog(hdlg);
		return true;

	case WM_HSCROLL:
		ReadSliders(hdlg);
		NotifyParent();
		return true;

	case WM_COMMAND:
		switch (LOWORD(wp))
		{
		case IDC_CRT_ENABLE:
			// 自分では切り替えない。親の既存処理(used3d の同期＋
			// バックエンド再生成)に任せ、その完了後に親側から
			// UpdateControls() が呼ばれて表示が揃う
			PostMessage(hwndparent, WM_COMMAND, IDM_CRT_ENABLE, 0);
			return true;

		case IDC_CRT_SMOOTHSTEP:
			config->crtsmoothstep = IsDlgButtonChecked(hdlg, IDC_CRT_SMOOTHSTEP) ? 1 : 0;
			NotifyParent();
			return true;

		case IDC_CRT_NATIVEHEIGHT:
			if (HIWORD(wp) == CBN_SELCHANGE)
			{
				static const int heights[] = { 200, 400, 800 };
				int sel = (int) SendDlgItemMessage(hdlg, IDC_CRT_NATIVEHEIGHT, CB_GETCURSEL, 0, 0);
				if (0 <= sel && sel < 3)
					config->crtscanlinenativeheight = heights[sel];
				NotifyParent();
			}
			return true;

		case IDC_CRT_PRESET_OFF:
			SetPreset(0, 0, 0, 0);
			UpdateSliders(hdlg);
			NotifyParent();
			return true;

		case IDC_CRT_PRESET_WEAK:
			SetPreset(config->crtpreset[0][0], config->crtpreset[0][1],
					  config->crtpreset[0][2], config->crtpreset[0][3]);
			UpdateSliders(hdlg);
			NotifyParent();
			return true;

		case IDC_CRT_PRESET_MID:
			SetPreset(config->crtpreset[1][0], config->crtpreset[1][1],
					  config->crtpreset[1][2], config->crtpreset[1][3]);
			UpdateSliders(hdlg);
			NotifyParent();
			return true;

		case IDC_CRT_PRESET_STRONG:
			SetPreset(config->crtpreset[2][0], config->crtpreset[2][1],
					  config->crtpreset[2][2], config->crtpreset[2][3]);
			UpdateSliders(hdlg);
			NotifyParent();
			return true;

		case IDC_CRT_PRESET_RECOMMEND:
			SetPreset(3600, 1000, 3500, 100);
			UpdateSliders(hdlg);
			NotifyParent();
			return true;

		case IDC_CRT_PRESET_SAVE:
			{
				int sel = (int) SendDlgItemMessage(hdlg, IDC_CRT_PRESET_TARGET,
												   CB_GETCURSEL, 0, 0);
				if (0 <= sel && sel < PC8801::Config::crtnumpresets)
				{
					// 現在のパラメータをそのプリセットへ上書きする。
					// M88.ini への書き出しは終了時の SaveConfig がまとめて行う
					config->crtpreset[sel][0] = config->crtscanline;
					config->crtpreset[sel][1] = config->crtvignette;
					config->crtpreset[sel][2] = config->crtaberration;
					config->crtpreset[sel][3] = config->crtcurvature;
				}
			}
			return true;

		case IDC_CRT_RELOAD:
			PostMessage(hwndparent, WM_COMMAND, IDM_CRT_RELOAD, 0);
			return true;

		case IDOK:
			DestroyWindow(hdlg);
			hwnd = 0;
			return true;
		}
		break;

	case WM_CLOSE:
		DestroyWindow(hdlg);
		hwnd = 0;
		return true;
	}
	return false;
}

INT_PTR CALLBACK WinCRTConfig::DlgProcGate
(HWND hwnd, UINT m, WPARAM w, LPARAM l)
{
	WinCRTConfig* self = 0;
	if (m == WM_INITDIALOG) {
		self = reinterpret_cast<WinCRTConfig*>(l);
		if (self) {
			::SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)self);
		}
	} else {
		self = (WinCRTConfig*)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
	}

	if (self) {
		return self->DlgProc(hwnd, m, w, l);
	} else {
		return FALSE;
	}
}
