#include "stdafx.h"
#include "RNSF7.h"
#include "regopendlg.h"
#include "commfunc.h"
#include "MainListview.h"

using namespace std;
using namespace boost;

static const char *regOpenKeyDlg_pszRegKey;

void regOpenKeyDlg(HWND hWnd, const char *pszRegKey)
{
	regOpenKeyDlg_pszRegKey = pszRegKey;
	DialogBox(hInst, (LPCTSTR)IDD_REGOPENKEY, hWnd, (DLGPROC)regOpenKeyDlgProc);
}

const wchar_t *hivekeyconv[] = {
	L"HKCR\\\\", L"HKEY_CLASSES_ROOT\\\\",
	L"HKCU\\\\", L"HKEY_CURRENT_USER\\\\",
	L"HKLM\\\\", L"HKEY_LOCAL_MACHINE\\\\",
	L"HKCC\\\\", L"HKEY_CURRENT_CONFIG\\\\",
	L"HKDD\\\\", L"HKEY_DYN_DATA\\\\",
	L"HKPD\\\\", L"HKEY_PERFORMANCE_DATA\\\\",
	L"HKPT\\\\", L"HKEY_PERFORMANCE_TEXT\\\\",
	L"HKPN\\\\", L"HKEY_PERFORMANCE_NLSTEXT\\\\",
	0, 0};

const wchar_t *z2h[] = {
	L"Ａ", L"A",
	L"Ｂ", L"B",
	L"Ｃ", L"C",
	L"Ｄ", L"D",
	L"Ｅ", L"E",
	L"Ｆ", L"F",
	L"Ｇ", L"G",
	L"Ｈ", L"H",
	L"Ｉ", L"I",
	L"Ｊ", L"J",
	L"Ｋ", L"K",
	L"Ｌ", L"L",
	L"Ｍ", L"M",
	L"Ｎ", L"N",
	L"Ｏ", L"O",
	L"Ｐ", L"P",
	L"Ｑ", L"Q",
	L"Ｒ", L"R",
	L"Ｓ", L"S",
	L"Ｔ", L"T",
	L"Ｕ", L"U",
	L"Ｖ", L"V",
	L"Ｗ", L"W",
	L"Ｘ", L"X",
	L"Ｙ", L"Y",
	L"Ｚ", L"Z",
	L"ａ", L"a",
	L"ｂ", L"b",
	L"ｃ", L"c",
	L"ｄ", L"d",
	L"ｅ", L"e",
	L"ｆ", L"f",
	L"ｇ", L"g",
	L"ｈ", L"h",
	L"ｉ", L"i",
	L"ｊ", L"j",
	L"ｋ", L"k",
	L"ｌ", L"l",
	L"ｍ", L"m",
	L"ｎ", L"n",
	L"ｏ", L"o",
	L"ｐ", L"p",
	L"ｑ", L"q",
	L"ｒ", L"r",
	L"ｓ", L"s",
	L"ｔ", L"t",
	L"ｕ", L"u",
	L"ｖ", L"v",
	L"ｗ", L"w",
	L"ｘ", L"x",
	L"ｙ", L"y",
	L"ｚ", L"z",
	L"０", L"0",
	L"１", L"1",
	L"２", L"2",
	L"３", L"3",
	L"４", L"4",
	L"５", L"5",
	L"６", L"6",
	L"７", L"7",
	L"８", L"8",
	L"９", L"9",
	L"！", L"!",
	L"”", L"\"",
	L"＃", L"#",
	L"＄", L"$",
	L"％", L"%",
	L"＆", L"&",
	L"’", L"'",
	L"（", L"(",
	L"）", L")",
	L"＝", L"=",
	L"ー", L"-",
	L"〜", L"~",
	L"＾", L"^",
	L"｜", L"|",
	L"￥", L"\\",
	L"＠", L"@",
	L"［", L"[",
	L"｛", L"{",
	L"；", L";",
	L"＋", L"+",
	L"：", L":",
	L"＊", L"*",
	L"］", L"]",
	L"｝", L"}",
	L"，", L",",
	L"＜", L"<",
	L"．", L".",
	L"＞", L">",
	L"／", L"/",
	L"？", L"?",
	L"＿", L"_",
	L"　", L" ",
	L"△", L" ",
	0, 0};

bool translateKey(HWND hDlg, const char *pszBefore)
{
	// 変換のために、まずはSJISをUnicodeへ変換。まず長さを得る
	int len_vwb = MultiByteToWideChar(GetACP(), MB_PRECOMPOSED, pszBefore, -1, NULL, 0);
	if (len_vwb == 0) {
		return false;
	}

	// メモリを確保し、Unicode変換
	vector<wchar_t> vw_str;		// 変換前(Unicode)

	++len_vwb;
	vw_str.resize(len_vwb);
	if ( 0 == MultiByteToWideChar(GetACP(), MB_PRECOMPOSED, pszBefore, -1, &vw_str[0], len_vwb) ) {
		return false;
	}

	// 変換前文字列を、widechar 文字列変数へ代入
	wstring w_str = &vw_str[0];

	// 標準的な正規表現サーチ・全体を１行とする・複数のマッチを探す
	regex_constants::match_flag_type flags = regex_constants::match_default |
											 regex_constants::match_single_line |
											 regex_constants::match_any;

	if (SendDlgItemMessage(hDlg, IDC_CHECK_ARROWEN, BM_GETCHECK, 0, 0) == BST_CHECKED) {
		// → を \ に変換
		wregex wre(L"→", wregex::normal);
		w_str = regex_replace(w_str, wre, L"\\", flags);
	}
	if (SendDlgItemMessage(hDlg, IDC_CHECK_ZEN2HAN, BM_GETCHECK, 0, 0) == BST_CHECKED) {
		// 全角文字（アルファベット・数字・記号）を半角文字に変換
		for(int i=0 ; z2h[i] ; i+=2) {
			wregex wre(z2h[i], wregex::normal);
			w_str = regex_replace(w_str, wre, z2h[i+1], flags);
		}
	}
	if (SendDlgItemMessage(hDlg, IDC_CHECK_SPENSP, BM_GETCHECK, 0, 0) == BST_CHECKED) {
		// △\△ を \ に変換
		wregex wre(L" \\\\ ", wregex::normal);
		w_str = regex_replace(w_str, wre, L"\\", flags);
	}
	if (SendDlgItemMessage(hDlg, IDC_CHECK_ENEN, BM_GETCHECK, 0, 0) == BST_CHECKED) {
		// \\ を \ に変換
		wregex wre(L"\\\\\\\\", wregex::normal);
		w_str = regex_replace(w_str, wre, L"\\", flags);
	}
	if (SendDlgItemMessage(hDlg, IDC_CHECK_SPENSP, BM_GETCHECK, 0, 0) == BST_CHECKED) {
		// △\△ を \ に変換(もう１回)
		wregex wre(L" \\\\ ", wregex::normal);
		w_str = regex_replace(w_str, wre, L"\\", flags);
	}
	if (SendDlgItemMessage(hDlg, IDC_CHECK_ENEN, BM_GETCHECK, 0, 0) == BST_CHECKED) {
		// \\ を \ に変換(もう１回)
		wregex wre(L"\\\\\\\\", wregex::normal);
		w_str = regex_replace(w_str, wre, L"\\", flags);
	}
	if (SendDlgItemMessage(hDlg, IDC_CHECK_HIVE, BM_GETCHECK, 0, 0) == BST_CHECKED) {
		// ハイブ略称を正式名称に変換
		for(int i=0 ; hivekeyconv[i] ; i+=2) {
			wregex wre(hivekeyconv[i], wregex::normal);
			w_str = regex_replace(w_str, wre, hivekeyconv[i+1], flags);
		}
	}
#if _MSC_VER < 1500
#ifndef _WIN64
	// Windows Meでは、「マイ コンピュータ\」を付加する
	if (winver < 500) {
		w_str.insert(0, L"マイ コンピュータ\\");
	}
#endif /* _WIN64 */
#endif // _MSC_VER < 1500
	int len_wca = WideCharToMultiByte(GetACP(), 0, w_str.c_str(), -1, NULL, 0, NULL, NULL);
	if ( len_wca == 0 ) {
		return false;
	}
	++len_wca;

	vector<char>    vc_str;		// 変換後(SJIS)

	vc_str.resize(len_wca);
	if ( 0 == WideCharToMultiByte(GetACP(), 0, w_str.c_str(), -1, &vc_str[0], len_wca, NULL, NULL) ) {
		return NULL;
	}

	SetDlgItemText(hDlg, IDC_EDIT_REGKEY_AFTER, &vc_str[0]);

	return true;
}

// ツールチップ
const struct DEFTOOLTIP rod_deftooltip[] = {
	{IDC_COMBO_REGKEY_BEFORE,"開きたいレジストリ キーを指定します。Webサイトからキー名をコピー＆ペーストされることを想定しています。"},
	{IDC_CHECK_ZEN2HAN,      "上の入力欄に入力された全角文字(アルファベット・数字・記号)を、半角文字に変換します。"},
	{IDC_CHECK_SPENSP,       "\\ の前後が半角スペース1つで囲まれているような場合に、半角スペースを取り除きます。"},
	{IDC_CHECK_ENEN,         "C言語のプログラムのソースコード等で \\ が2つ重ねられているような場合に、\\ を1つにします。"},
	{IDC_CHECK_ARROWEN,      "レジストリ解説のWebページにあるような、キー名の区切りに '→' が用いられているような場合に、'\\' に変換します。"},
	{IDC_CHECK_HIVE,         "ハイブの略称を正式名称に変換します。(HKCU→HKEY_CURRENT_USER, HKLM→HKEY_LOCAL_MACHINE等)"},
	{IDOK,                   "変換後のレジストリキー名で表示されているレジストリキーを、レジストリ エディタで開きます。"},
	{IDCANCEL,               "何もせずにこのダイアログを閉じます。"},
	{IDHELP,				 "このダイアログに関するヘルプを表示します。"},
	{0,0}};

// ダイアログのアンカー等に関する情報
const DLGITEM dlgitems[] = {
	{IDC_COMBO_REGKEY_BEFORE,ANCHOR_TOPLEFTRIGHT},
	{IDC_EDIT_REGKEY_AFTER,  ANCHOR_BOTTOMLEFTRIGHT},
	{IDOK,                   ANCHOR_BOTTOMRIGHT},
	{IDCANCEL,               ANCHOR_BOTTOMRIGHT},
	{IDHELP,	             ANCHOR_BOTTOMRIGHT},
	{0,0}
};

INT_PTR CALLBACK regOpenKeyDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static SIZEDLG *sizedlg;
	static ComboboxQueue *cbq;
	static HICON hDlgIcon;

	switch (message) {
		case WM_INITDIALOG:
			{
				// アイコンの設定
				if (0 != ExtractIconEx("REGEDIT.EXE", 0, NULL, &hDlgIcon, 1)) {
					SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hDlgIcon);
				}

				// ツールチップの設定
				CreateToolTip(hInst, hDlg, rod_deftooltip);

				// ダイアログのコントロールのアンカーを登録
				sizedlg = new SIZEDLG(hDlg, dlgitems);
				sizedlg->SetMinMaxInfo(sizedlg->GetDefWidth(), sizedlg->GetDefHeight(), -1, sizedlg->GetDefHeight());

				// レジストリから設定を読み出す
				REG reg;
				if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_READONLY)) {
					// 全角→半角変換
					if (reg.ReadInteger("RegOpenKeyDlg_Zen2Han", 1) != 0) {
						SendDlgItemMessage(hDlg, IDC_CHECK_ZEN2HAN, BM_SETCHECK, BST_CHECKED, NULL);
					}
					// △\△ を \ に変換
					if (reg.ReadInteger("RegOpenKeyDlg_SpEnSp", 0) != 0) {
						SendDlgItemMessage(hDlg, IDC_CHECK_SPENSP, BM_SETCHECK, BST_CHECKED, NULL);
					}
					// \\ を \ に変換
					if (reg.ReadInteger("RegOpenKeyDlg_EnEn", 0) != 0) {
						SendDlgItemMessage(hDlg, IDC_CHECK_ENEN, BM_SETCHECK, BST_CHECKED, NULL);
					}
					// → を \ に変換
					if (reg.ReadInteger("RegOpenKeyDlg_ArrowEn", 0) != 0) {
						SendDlgItemMessage(hDlg, IDC_CHECK_ARROWEN, BM_SETCHECK, BST_CHECKED, NULL);
					}
					// Hive略称を正式名称に変換
					if (reg.ReadInteger("RegOpenKeyDlg_Hive", 0) != 0) {
						SendDlgItemMessage(hDlg, IDC_CHECK_HIVE, BM_SETCHECK, BST_CHECKED, NULL);
					}
					reg.CloseKey();
				}

				char *pstr = NULL;
				if (regOpenKeyDlg_pszRegKey && regOpenKeyDlg_pszRegKey[0]) {
					// 引数指定されたキーをデフォルト値として採用
					pstr = new_strcpy(regOpenKeyDlg_pszRegKey);
				}
				else {
					// レジストリからデフォルト（LastKey）値の取得
					if (reg.OpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit", TREG_OPEN_READONLY)) {
						pstr = reg.new_ReadString("LastKey");
						reg.CloseKey();
					}
				}

				if (pstr && pstr[0]) {
					char *phead = strstr(pstr, "HKEY_");
					if (phead) { // 「マイコンピュータ」で始まっていたら、それを取り除く
						SetDlgItemText(hDlg, IDC_COMBO_REGKEY_BEFORE, phead);
					}
					else {
						SetDlgItemText(hDlg, IDC_COMBO_REGKEY_BEFORE, pstr);
					}

					// 変換試行
					if (false == translateKey(hDlg, phead ? phead : pstr)) {
						MessageBox(hDlg, "変換中にエラーが発生しました。", "キーを指定してレジストリ エディタを開く", MB_OK | MB_ICONSTOP);
					}
				}
				safedelete(pstr);

				// フォーカスは入力ボックスへ
				SetFocus(GetDlgItem(hDlg, IDC_COMBO_REGKEY_BEFORE));

				// コンボボックスのリストを準備
				cbq = new ComboboxQueue();
				cbq->Init(hDlg, IDC_COMBO_REGKEY_BEFORE, 16, HKEY_CURRENT_USER, RNSF7REGKEY, "regopendlg_combo");
			}
			return (INT_PTR)FALSE;
				
		case WM_HELP: // ヘルプの呼び出し
			callhelp2(hDlg, "HLP000020.html");
			return (INT_PTR)TRUE;

		case WM_GETMINMAXINFO:
			// ダイアログの最小サイズを決める
			sizedlg->GetMinMaxInfo(lParam);
			return (INT_PTR)TRUE;

		case WM_SIZE:
			// リサイズ
			sizedlg->Resize(1);
			return (INT_PTR)TRUE;

		case WM_DESTROY:
			// コンボボックスの情報を破棄
			delete cbq;
			cbq = NULL;

			// リサイズのための情報を破棄
			delete sizedlg;
			sizedlg = NULL;

			// ダイアログアイコンの破棄
			if (hDlgIcon != NULL) {
				SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_SMALL, 0);
				DestroyIcon(hDlgIcon);
				hDlgIcon = NULL;
			}
			return (INT_PTR)TRUE;

		case WM_COMMAND:
			switch(LOWORD(wParam)) {
				case IDC_COMBO_REGKEY_BEFORE:
					if (HIWORD(wParam) == CBN_EDITCHANGE) {
						// 編集ボックスが編集された
					}
					else if (HIWORD(wParam) == CBN_SELCHANGE) {
						// コンボボックスでリストから選ばれた場合
						HWND hCombo = (HWND)lParam;

						// 変換前レジストリキーを取得(SJIS)。まずは長さを計算。
						int iCur = (int)SendMessage(hCombo, CB_GETCURSEL, 0, 0);
						int len_vcb = (int)SendMessage(hCombo, CB_GETLBTEXTLEN, (WPARAM)iCur, 0);
						if (len_vcb == 0) {
							// 文字列が入力されていない場合
							SetDlgItemText(hDlg, IDC_EDIT_REGKEY_AFTER, "");
							return (INT_PTR)TRUE;
						}

						// メモリを確保し、変換前レジストリキーを取得(SJIS)
						++len_vcb;
						char *pbuf = (char *)calloc(len_vcb, sizeof(char));
						if (pbuf == NULL) {
							MessageBox(hDlg, "キーを扱うためのメモリが足りません。", "キーを指定してレジストリ エディタを開く", MB_OK | MB_ICONSTOP);
							return (INT_PTR)TRUE;
						}

						SendMessage(hCombo, CB_GETLBTEXT, (WPARAM)iCur, (LPARAM)pbuf);
						if (false == translateKey(hDlg, pbuf)) {
							MessageBox(hDlg, "変換中にエラーが発生しました。", "キーを指定してレジストリ エディタを開く", MB_OK | MB_ICONSTOP);
						}
						safefree(pbuf);

						return (INT_PTR)TRUE;
					}
					else {
						return (INT_PTR)TRUE; // 編集ではなかった
					}
				case IDC_CHECK_ZEN2HAN: // 全角→半角変換
				case IDC_CHECK_SPENSP:  // △\△ を \ に変換
				case IDC_CHECK_ENEN:    // \\ を \ に変換
				case IDC_CHECK_ARROWEN: // → を \ に変換
				case IDC_CHECK_HIVE:    // Hive略称を正式名称に変換
					// 変換試行
					{
						// 変換前レジストリキーを取得(SJIS)。まずは長さを計算。
						int len_vcb = GetWindowTextLength(GetDlgItem(hDlg, IDC_COMBO_REGKEY_BEFORE));
						if (len_vcb == 0) {
							// 文字列が入力されていない場合
							SetDlgItemText(hDlg, IDC_EDIT_REGKEY_AFTER, "");
							return (INT_PTR)TRUE;
						}

						// メモリを確保し、変換前レジストリキーを取得(SJIS)
						++len_vcb;
						char *pbuf = (char *)calloc(len_vcb, sizeof(char));
						if (pbuf == NULL) {
							MessageBox(hDlg, "キーを扱うためのメモリが足りません。", "キーを指定してレジストリ エディタを開く", MB_OK | MB_ICONSTOP);
							return (INT_PTR)TRUE;
						}

						GetDlgItemText(hDlg, IDC_COMBO_REGKEY_BEFORE, pbuf, len_vcb);
						if (false == translateKey(hDlg, pbuf)) {
							MessageBox(hDlg, "変換中にエラーが発生しました。", "キーを指定してレジストリ エディタを開く", MB_OK | MB_ICONSTOP);
						}
						safefree(pbuf);
					}
					return (INT_PTR)TRUE;

				case IDHELP:
					callhelp2(hDlg, "HLP000020.html");
					return TRUE;

				case IDOK: // レジストリエディタを開く
					{
						// コンボボックスの内容を記録
						int len = GetWindowTextLength(GetDlgItem(hDlg, IDC_COMBO_REGKEY_BEFORE));
						if (len > 0) {
							char *pbuf = (char *)calloc(len+1, sizeof(char));
							if (pbuf == NULL) {
								MessageBox(hDlg, "キーを扱うためのメモリが足りません。", "キーを指定してレジストリ エディタを開く", MB_OK | MB_ICONSTOP);
								return (INT_PTR)TRUE;
							}
							GetDlgItemText(hDlg, IDC_COMBO_REGKEY_BEFORE, pbuf, len+1);
							cbq->Add(pbuf);
							free(pbuf);
						}
						
						// 対象レジストリ（変換後）の取得
						REG reg;
						if (reg.OpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit", TREG_OPEN_CREATE)) {
							BOOL bRes = reg.WriteStringFromEditbox("LastKey", hDlg, IDC_EDIT_REGKEY_AFTER, TREG_NORMAL_SZ);
							reg.CloseKey();

							if (bRes == FALSE) {
								MessageBox(hDlg, "キーを扱うためのメモリが足りません。", "キーを指定してレジストリ エディタを開く", MB_OK | MB_ICONSTOP);
								return (INT_PTR)TRUE;
							}
						}
						else {
							dispErrorMessage(hDlg, MB_OK | MB_ICONSTOP, "キーを指定してレジストリ エディタを開く",
								             "レジストリキー'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit'を書き込みアクセスで開こうとしてエラーになりました。キーを指定するためのレジストリキーを開けないため、処理を中止します。\nAPIが報告した原因：", getLastErrorString(tRegGetLastError()), NULL, NULL, NULL, NULL);
							return (INT_PTR)TRUE;
						}
					}
					if ((HINSTANCE)32 >= ShellExecute(hDlg, "open", "regedit.exe", "-m", NULL, SW_SHOWNORMAL)) {
						MessageBox(hDlg, "「レジストリ エディタ」の起動に失敗しました。", "キーを指定してレジストリ エディタを開く", MB_OK | MB_ICONSTOP);
					}
					// ↓そのまま下へ
				case IDCANCEL:	// このダイアログを閉じる
					{
						REG reg;
						if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_CREATE)) {
							// レジストリへ設定を保存する
							reg.WriteInteger("RegOpenKeyDlg_Zen2Han", (int)SendDlgItemMessage(hDlg, IDC_CHECK_ZEN2HAN, BM_GETCHECK, 0, NULL)); // 全角→半角変換
							reg.WriteInteger("RegOpenKeyDlg_SpEnSp",  (int)SendDlgItemMessage(hDlg, IDC_CHECK_SPENSP,  BM_GETCHECK, 0, NULL)); // △\△ を \ に変換
							reg.WriteInteger("RegOpenKeyDlg_EnEn",    (int)SendDlgItemMessage(hDlg, IDC_CHECK_ENEN,    BM_GETCHECK, 0, NULL)); // \\ を \ に変換
							reg.WriteInteger("RegOpenKeyDlg_ArrowEn", (int)SendDlgItemMessage(hDlg, IDC_CHECK_ARROWEN, BM_GETCHECK, 0, NULL)); // → を \ に変換
							reg.WriteInteger("RegOpenKeyDlg_Hive",    (int)SendDlgItemMessage(hDlg, IDC_CHECK_HIVE,    BM_GETCHECK, 0, NULL)); // Hive略称を正式名称に変換
							reg.CloseKey();
						}
						else {
							dispErrorMessage(hDlg, MB_OK | MB_ICONSTOP, "レジストリへの書き込み中エラー", "レジストリキー'HKEY_CURRENT_USER\\", RNSF7REGKEY,
											 "'を書き込みアクセスで開こうとしてエラーになりました。\nAPIが報告した原因：", getLastErrorString(tRegGetLastError()), NULL, NULL);
						}

						EndDialog(hDlg, LOWORD(wParam));
					}
					return (INT_PTR)TRUE;
			}
			break;
	}
	return (INT_PTR)FALSE;
}
