// MultiRnd.cpp :
// 「機能項目のダイアログ状態を指定フォルダへ一括保存」「機能項目のダイアログ状態を指定フォルダから一括読込」

//	HKEY_CURRENT_USER\Software\InaSoftAns\Rnsf7\CurrentVersion
//	  RNDALL_FOLDER(REG_SZ) : RND格納フォルダ
//	  RNDALL_SEL(DWORD)     : 0ならALL, 1ならSELECT
//	  RNDALL_RND(MULTI_SZ)  : 対象RNDリスト(rndファイルのファイル名のリスト。\0区切り複数文字列である)


#include "stdafx.h"
#include "RNSF7.h"
#include "MultiRnd.h"
#include "opendlg.h"

static LPMLVI pg_pMlvi;
static int    pg_nCountMlvi;
static int    pg_mode;
static char   pg_out_mode;
static char   pg_out_folder[520];
static char  *pg_msz_rnds;

EXTERN HICON hCBLIconCheck;	// チェックアイコン（ダイアログアイコンに使うので）

// ツールチップ
const struct DEFTOOLTIP rnd_deftooltip[] = {
	{IDC_EDIT_FOLDER,        "rndファイル名の格納先のフォルダ名を指定します。エクスプローラ等からドラッグ＆ドロップできます。"},
	{IDC_BUTTON_BROWSE,      "フォルダを選択して、入力ボックスに反映します。"},
	{IDC_RADIO_ALL,          "実行可能なすべての機能項目を確実に対象とします。デフォルト非表示の標準スクリプト(Rnsf7.rsc等)も対象とします。"},
	{IDC_RADIO_SELECT,       "選択式に機能項目を選ぶ場合にこちらを選択します。"},
	{IDOK,                   "このダイアログを閉じ、上記の内容で実行開始します。"},
	{IDCANCEL,               "このダイアログを閉じ、実行中止します。"},
	{0,0}};

// ダイアログのアンカー等に関する情報
const DLGITEM rnd_dlgitems[] = {
	{IDC_EDIT_FOLDER,        ANCHOR_TOPLEFTRIGHT},
	{IDC_BUTTON_BROWSE,      ANCHOR_TOPRIGHT},
	{IDC_LIST_SELECT,        ANCHOR_CENTER},
	{IDOK,                   ANCHOR_BOTTOMRIGHT},
	{IDCANCEL,               ANCHOR_BOTTOMRIGHT},
	{0,0}
};

// msz（\0区切り\0\0終端）の文字列からstr（\0終端）を検索する
bool search_msz(const char *msz, const char *str)
{
	if (msz == NULL || str == NULL) {
		return false;
	}

	for(int i=0 ; !(msz[i]=='\0' && msz[i+1]=='\0') ; ++i) {
		if (lstrcmpi(&msz[i], str) == 0) {
			// 一致
			return true;
		}
		else {
			// 次の文字列開始位置までスキップ
			while(msz[++i] != '\0');

			// スキップした先も\0なら終了
			if (msz[i+1] == '\0') {
				break;
			}
		}
	}

	return false;
}

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

	switch (message) {
		case WM_INITDIALOG:
			{
				// 初期化共通
				REG reg;

				if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_READONLY)) {
					if (reg.ReadInteger("RNDALL_SEL", 0) == 0) {
						SendDlgItemMessage(hDlg, IDC_RADIO_ALL, BM_SETCHECK, BST_CHECKED, 0);
					}
					else {
						SendDlgItemMessage(hDlg, IDC_RADIO_SELECT, BM_SETCHECK, BST_CHECKED, 0);
					}

					reg.ReadStringForEditbox("RNDALL_FOLDER", hDlg, IDC_EDIT_FOLDER, "");

					reg.CloseKey();
				}
				else {
					SendDlgItemMessage(hDlg, IDC_RADIO_ALL, BM_SETCHECK, BST_CHECKED, 0);
				}

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

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

				// コンボボックスのリストを準備
				cbq = new ComboboxQueue();
				cbq->Init(hDlg, IDC_EDIT_FOLDER, 16, HKEY_CURRENT_USER, RNSF7REGKEY, "multirnd_combo");

				// アイコン設定
				SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)(pg_mode == MR_SAVE ? hCBLIconUncheck : hCBLIconCheck));
			}
			if (pg_mode == MR_SAVE) {
				// 「機能項目のダイアログ状態を指定フォルダへ一括保存」初期化
				SetWindowText(hDlg, "ダイアログ状態の一括保存");
				SetDlgItemText(hDlg, IDC_STATIC_ALL, "・選択可能な機能項目（スクリプト）すべてが対象となります。\n"
					                                 "・ダイアログ状態ファイル(.rnd)の名前部分は、スクリプトの名前部分と同じとなります。");
				SetDlgItemText(hDlg, IDC_RADIO_SELECT, "保存対象の機能項目（スクリプト）を選択する(&S)");
				ShowWindow(GetDlgItem(hDlg, IDC_STATIC_SELECT), SW_HIDE);
				ShowWindow(GetDlgItem(hDlg, IDC_LIST_SELECT), SW_SHOW);

				if (SendDlgItemMessage(hDlg, IDC_RADIO_ALL, BM_GETCHECK, 0, 0) == BST_CHECKED) {
					EnableWindow(GetDlgItem(hDlg, IDC_LIST_SELECT), FALSE);
				}

				// ダイアログサイズを縦2倍に
				RECT rc;
				GetWindowRect(hDlg, &rc);
				HMONITOR hMon = MonitorFromWindow(hDlg, MONITOR_DEFAULTTOPRIMARY);
				MONITORINFO mi;
				mi.cbSize = sizeof(MONITORINFO);
				GetMonitorInfo(hMon, &mi);
				// ダイアログサイズを縦2倍にした分、Y座標はちょっと上に来る。
				SetWindowPos(hDlg, 0, rc.left, (rc.top-mi.rcMonitor.top)/2+mi.rcMonitor.top, rc.right - rc.left, (rc.bottom - rc.top)*2, SWP_NOZORDER);

				REG reg;
				char *msz_rnds = NULL;
				if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_READONLY)) {
					// レジストリからチェックを付けるべき項目群を読み出す
					DWORD dwSize = 0;
					msz_rnds = reg.new_ReadMultiStringDirect("RNDALL_RND", &dwSize);
					reg.CloseKey();
				}

				// リストビューの内容を準備
				HWND hListView = GetDlgItem(hDlg, IDC_LIST_SELECT);

				// チェック状態セット
				DWORD dwStyle = ListView_GetExtendedListViewStyle(hListView);
				dwStyle |= LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT;
				ListView_SetExtendedListViewStyle(hListView, dwStyle);

				// 小さいアイコンをリストビューのイメージに追加する
				HIMAGELIST	himlSmall;
				int i;

				himlSmall = ImageList_Create(16, 16, ILC_COLOR32 /*ILC_COLORDDB */ | ILC_MASK, pg_nCountMlvi+1, 0); // +1は非表示rscのため
				for(i=0 ; i<pg_nCountMlvi ; i++) {
					ImageList_AddIcon(himlSmall, pg_pMlvi[i].hSmallIcon ? pg_pMlvi[i].hSmallIcon :
												 pg_pMlvi[i].hicon      ? pg_pMlvi[i].hicon      : hDefaultRscSmallIcon);
				}
				ImageList_AddIcon(himlSmall, hDefaultRscSmallIcon);
				ListView_SetImageList(hListView, himlSmall, LVSIL_SMALL);

				LV_COLUMN	lvColumn;
				lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
				lvColumn.fmt = LVCFMT_LEFT; // 左寄せ
				lvColumn.pszText = (LPSTR)"ファイル名";
				lvColumn.cx = 120; // 仮の値
				ListView_InsertColumn(hListView, 0, &lvColumn);
				lvColumn.pszText = (LPSTR)"項目";
				lvColumn.cx = 150; // 仮の値
				ListView_InsertColumn(hListView, 1, &lvColumn);
				lvColumn.pszText = (LPSTR)"説明";
				lvColumn.cx = 405; // 仮の値
				ListView_InsertColumn(hListView, 2, &lvColumn);

				{ //if (ccpu32_GetComCtl32Ver() >= 471) { // comctl32.dllのバージョンが4.71以降なら、infotipを付けられるようにする
					ListView_SetExtendedListViewStyleEx(hListView, LVS_EX_INFOTIP /*| LVS_EX_BORDERSELECT*/, LVS_EX_INFOTIP /*| LVS_EX_BORDERSELECT*/);
				}

				// アイテムを初期化し、ListViewコントロールへアイテム数を供給する
				ListView_DeleteAllItems(hListView);
				ListView_SetItemCount(hListView, pg_nCountMlvi + invisible_item_num); // +invisible_item_num は標準で非表示のスクリプトのため

				LVITEM lvItem;

				for(i=0 ; i<pg_nCountMlvi+invisible_item_num ; ++i) {
					ZeroMemory(&lvItem, sizeof(lvItem));
					lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
					lvItem.iImage = (i<pg_nCountMlvi) ? i : pg_nCountMlvi; // pg_nCountMlviは非表示rscのために準備されたアイコン
					lvItem.iItem = i;
					lvItem.iSubItem = 0;
					lvItem.pszText = (LPSTR)((i<pg_nCountMlvi) ? pg_pMlvi[i].rsc_fname : invisible_item_rsc[i-pg_nCountMlvi]);
					lvItem.state = (i==0) ? LVIS_FOCUSED : 0;
					lvItem.stateMask = LVIS_FOCUSED;
					ListView_InsertItem(hListView, (LVITEM *)&lvItem);
					ListView_SetItemText(hListView, i, 1, (LPSTR)((i<pg_nCountMlvi) ? pg_pMlvi[i].item : invisible_item_name[i-pg_nCountMlvi]));
					ListView_SetItemText(hListView, i, 2, (LPSTR)((i<pg_nCountMlvi) ? pg_pMlvi[i].info : "いじくるつくーるに対する設定（メニューより呼び出されます）"));

					if (search_msz(msz_rnds, lvItem.pszText)) {
						// チェック
						ListView_SetCheckState(hListView, i, TRUE);
					}
				}

				ListView_SetItemState(hListView, 0, LVIS_FOCUSED, LVIS_FOCUSED);
				ListView_SetColumnWidth(hListView, 0, LVSCW_AUTOSIZE);
				ListView_SetColumnWidth(hListView, 1, LVSCW_AUTOSIZE);
				ListView_SetColumnWidth(hListView, 2, LVSCW_AUTOSIZE);

				// 複数文字列の解放
				safedelete(msz_rnds);
			}

			return 0;

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

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

		case WM_DESTROY:
			// コンボボックスの情報を破棄
			safedelete1(cbq);

			// リサイズのための情報を破棄
			safedelete1(sizedlg);

			return (INT_PTR)TRUE;

		case WM_DROPFILES: // 編集ボックスがファイルをドロップされた
			{
				char foldername[MAX_PATH*2] = "";

				if (0 < DragQueryFile((HDROP)wParam, 0, foldername, _countof(foldername))) {
					SetDlgItemText(hDlg, IDC_EDIT_FOLDER, foldername);
				}
				DragFinish((HDROP)wParam);
			}
			break;

		case WM_COMMAND:
			switch(LOWORD(wParam)) {
				case IDC_BUTTON_BROWSE:
					{ // フォルダ選択
						char chPutFolder[MAX_PATH*2] = "";

						if (GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT_FOLDER)) < _countof(chPutFolder)) {
							GetDlgItemText(hDlg, IDC_EDIT_FOLDER, chPutFolder, _countof(chPutFolder));
						}

						if ( browseFolder( hDlg, chPutFolder, "ダイアログ状態の一括保存／読込フォルダ選択" ) ) {
							SetDlgItemText(hDlg, IDC_EDIT_FOLDER, chPutFolder);
						}
					}
					return (INT_PTR)TRUE;

				case IDC_RADIO_ALL: // 全選択
				case IDC_RADIO_SELECT: // 指定選択
					if (pg_mode == MR_SAVE) {
						if (SendDlgItemMessage(hDlg, IDC_RADIO_ALL, BM_GETCHECK, 0, 0) == BST_CHECKED) {
							EnableWindow(GetDlgItem(hDlg, IDC_LIST_SELECT), FALSE);
						}
						else {
							EnableWindow(GetDlgItem(hDlg, IDC_LIST_SELECT), TRUE);
						}
						return (INT_PTR)TRUE;
					}
					break;


				case IDOK:
					{
						int len = GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT_FOLDER));

						if (len == 0) {
							MessageBox(hDlg, "フォルダ名を指定して下さい。", "ダイアログ状態の一括保存／読込", MB_OK | MB_ICONSTOP);
							return (INT_PTR)TRUE;
						}
						else if (len >= _countof(pg_out_folder)) {
							MessageBox(hDlg, "フォルダ名の指定が長すぎます。", "ダイアログ状態の一括保存／読込", MB_OK | MB_ICONSTOP);
							return (INT_PTR)TRUE;
						}
						else {
							// フォルダ名取得
							GetDlgItemText(hDlg, IDC_EDIT_FOLDER, pg_out_folder, _countof(pg_out_folder));
							cbq->Add(pg_out_folder);
						}

						// 全指定？選択指定？
						if (SendDlgItemMessage(hDlg, IDC_RADIO_ALL, BM_GETCHECK, 0, 0) == BST_CHECKED) {
							pg_out_mode = MR_ALL;
						}
						else {
							pg_out_mode = MR_SELECT;
						}

						if (pg_mode == MR_SAVE) {
							// 保存時のみ…機能項目を選択
							// リストビューの取得の準備
							HWND hListView = GetDlgItem(hDlg, IDC_LIST_SELECT);

							// まずはサイズを取得
							unsigned uSize = 1;
							int i, p = 0;

							for(i=0 ; i<pg_nCountMlvi+invisible_item_num ; ++i) {
								if (ListView_GetCheckState(hListView, i) == TRUE) {
									uSize += lstrlen((i<pg_nCountMlvi) ? pg_pMlvi[i].rsc_fname : invisible_item_rsc[i-pg_nCountMlvi]) + 1;
								}
							}

							if (uSize == 1) {
								// SELECTなのに無指定なのはおかしいのでエラー
								if (pg_out_mode == MR_SELECT) {
									MessageBox(hDlg, "保存対象の機能項目を選択して下さい。", "ダイアログ状態の一括保存／読込", MB_OK | MB_ICONSTOP);
									return (INT_PTR)TRUE;
								}
								// ALLで無指定は素通り。ただしレジストリ反映は行えるようにしておく
								pg_msz_rnds = NULL;
							}
							else {
								// メモリ確保＆文字列構築
								pg_msz_rnds = zalloc(uSize);
								if (pg_msz_rnds) {
									for(i=p=0 ; i<pg_nCountMlvi+invisible_item_num ; ++i) {
										if (ListView_GetCheckState(hListView, i) == TRUE) {
											lstrcpy(&pg_msz_rnds[p], (i<pg_nCountMlvi) ? pg_pMlvi[i].rsc_fname : invisible_item_rsc[i-pg_nCountMlvi]);
											p += lstrlen(&pg_msz_rnds[p]) + 1;
										}
									}
									++p;
								}
								else {
									MessageBox(hDlg, "処理を続行するためのメモリが足りません。", "ダイアログ状態の一括保存／読込", MB_OK | MB_ICONSTOP);
									return (INT_PTR)TRUE;
								}
							}

							REG reg2;
							if (reg2.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_CREATE)) {
								if (pg_msz_rnds) reg2.WriteMultiStringDirect("RNDALL_RND", pg_msz_rnds, p);
								else             reg2.DeleteValue("RNDALL_RND");
								reg2.CloseKey();
							}

							if (pg_out_mode == MR_ALL) {
								// ALLでは使わないので、ここで解放してしまう
								safefree(pg_msz_rnds);
							}
						}

						REG reg;
						if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_CREATE)) {
							reg.WriteString("RNDALL_FOLDER", pg_out_folder, TREG_SZ);
							reg.WriteInteger("RNDALL_SEL", pg_out_mode);
							reg.CloseKey();
						}
					}
					// このまま下へ
				case IDCANCEL:
					EndDialog(hDlg, LOWORD(wParam));
					return (INT_PTR)TRUE;
			}

			break;

	}

	return 0;
}

int	multirnd(HWND hWnd, const LPMLVI pMlvi, int nCountMlvi, int mode, char *out_mode, char *out_folder, char **msz_rnds)
{
	pg_pMlvi = pMlvi;
	pg_nCountMlvi = nCountMlvi;
	pg_mode = mode;

	if (IDOK == DialogBox(hInst, (LPCTSTR)IDD_DIALOG_RND, hWnd, (DLGPROC)multirndDlgProc)) {
		if (out_mode)   *out_mode = pg_out_mode;
		if (out_folder) lstrcpy(out_folder, pg_out_folder);
		if (pg_mode == MR_SAVE) {
			if (msz_rnds)   *msz_rnds = pg_msz_rnds;
			else            safefree(pg_msz_rnds);
		}

		return 1;
	}
	else {
		return 0;
	}
}
