#include "stdafx.h"
#include "RNSF7.h"
#include "kwd_search.h"
#include "kwd_treectrl.h"
#include "commfunc.h"
#include "OpenSetting.h"
#include "MainListview.h"

#define TIMERID		3155
#define TIMERWAIT	700

// ツリービューに項目を追加(nLevelは1か2のみ指定可能らしい)
// (まず最初に hwndTV = NULL にして呼び出すこと)
HTREEITEM AddItemToTree(HWND hwndTV, LPSTR lpszItem, int nLevel, int id)
{
	TVITEM tvi;
	TVINSERTSTRUCT tvins;
	static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
	static HTREEITEM hPrevRootItem = NULL;
	static HTREEITEM hPrevLev2Item = NULL;

	if (hwndTV == NULL) { // tvins.hInsertAfterに入れるためのデータを初期化
		hPrevRootItem = hPrevLev2Item = NULL;
        return hPrev = (HTREEITEM)TVI_FIRST;
	}

	// TVINSERTSTRUCT構造体中の、使用されるメンバを決めておく
	tvi.mask = TVIF_TEXT | TVIF_PARAM;

	// テキストを設定
	tvi.pszText = lpszItem;
	tvi.cchTextMax = lstrlen(lpszItem);

	// 任意のデータを設定
	tvi.lParam = (LPARAM)id;

	tvins.item = tvi;
	tvins.hInsertAfter = hPrev;

	// 親フォルダとなるデータを設定（ただし、レベル２までしか対応していないようだ）
	if ( nLevel == 1 )
		tvins.hParent = TVI_ROOT;
	else if ( nLevel == 2 )
		tvins.hParent = hPrevRootItem;
	else
		tvins.hParent = hPrevLev2Item;

	// アイテムを追加(SendMessage)
	hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)&tvins);

	// 次回追加時のためにハンドルを保存
	if ( nLevel == 1 )
		hPrevRootItem = hPrev;
	else if ( nLevel == 2 )
		hPrevLev2Item = hPrev;

	return hPrev;
}

typedef struct {
	HTREEITEM	hParentItem;
	string      group;
	HTREEITEM	hLastItem;
} TVGROUPS;

vector<TVGROUPS> tvg;

// 既存で登録が行われていた…そのハンドルを返す
HTREEITEM AddRootItemToTree(HWND hwndTV, LPTSTR pszRootName)
{
	TVINSERTSTRUCT tvins;

	// ツリービューのグループに関する情報郡をクリア
	tvg.clear();
	tvg.reserve(RSCSTD);

	// ルートにアイテムを挿入するための情報を作成
	tvins.hInsertAfter = (HTREEITEM)TVI_FIRST;
	tvins.hParent = TVI_ROOT;
	tvins.item.mask = TVIF_TEXT | TVIF_PARAM;
	tvins.item.pszText = pszRootName;
	tvins.item.lParam = (LPARAM)NULL;

	return (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)&tvins);
}

// 既存で機能項目名の追加が行われていないかどうかを調べる
TVGROUPS *RegistOrSearchGroup(HWND hwndTV, HTREEITEM hRoot, const string &group)
{
	vector<TVGROUPS>::iterator iter;
	HTREEITEM hTreeItemAfter = (HTREEITEM)TVI_FIRST;

	for(iter=tvg.begin() ; iter!=tvg.end() ; ++iter) {
		if (iter->group.compare(group) == 0) {
			// 既存で登録が行われていた…そのハンドルを返す
			return &iter[0];
		}
		hTreeItemAfter = iter->hParentItem;
	}

	// 既存で登録が行われていなかった…新しく機能項目名を作成して返す
	TVINSERTSTRUCT tvins;
	TVGROUPS       tvg0;

	tvins.hInsertAfter = hTreeItemAfter;
	tvins.hParent = hRoot;
	tvins.item.mask = TVIF_TEXT | TVIF_PARAM;
	tvins.item.pszText = (LPTSTR)group.c_str();
	tvins.item.lParam = (LPARAM)NULL;

	tvg0.hParentItem  = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)&tvins);
	tvg0.group        = group;
	tvg0.hLastItem    = (HTREEITEM)TVI_FIRST;
	tvg.push_back(tvg0);

	return &(tvg.back());
}

bool AddItem(HWND hwndTV, HTREEITEM hRoot, const ITEMGROUP *ig)
{
	if(ig == NULL) {
		MessageBox(NULL, "内部エラー：igがNULLです。", "AddItem", MB_OK | MB_ICONSTOP);
		return false;
	}

	// ig->pszGroupNameが初登録の機能項目の場合は、登録を行う
	TVGROUPS *tvg1 = RegistOrSearchGroup(hwndTV, hRoot, ig->group);
	TVINSERTSTRUCT tvins;

	// tvg1.hParentItemの下に、ig->pszTitle を登録する。
	tvins.hInsertAfter = tvg1->hLastItem;
	tvins.hParent = tvg1->hParentItem;
	tvins.item.mask = TVIF_TEXT | TVIF_PARAM;
	tvins.item.lParam = (LPARAM)ig;

	// タイトルに一工夫
	string title;
	if (!(ig->bOS & btOSMask)) {
		title.append("(使用不可)");
	}
	title.append(ig->item);
	tvins.item.pszText = (LPSTR)(title.c_str());

	tvg1->hLastItem = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)&tvins);

	return true;
}

void ExpandItems(HWND hwndTV)
{
	vector<TVGROUPS>::iterator iter;

	for(iter=tvg.begin() ; iter!=tvg.end() ; ++iter) {
		TreeView_Expand( hwndTV, iter->hParentItem, TVE_EXPAND );
	}
}

// ツールチップ
const struct DEFTOOLTIP r8deftooltip[] = {
	{IDC_CHECK_ALWAYS_FIND,"検索ボタンを押さなくても、最後の文字入力からしばらく経過したら検索を開始するようにします。"},
	{IDC_EDIT_SEARCH,      "検索したい文字列を指定します。"},
	{IDC_BUTTON_SEARCH,    "検索を開始し、下のツリーから必要な機能の絞り込みを行います。"},
	{IDC_CHECK_SHOW_ENABLE_ONLY, "この環境で使用可能と思われる機能のみを表示します。"},
	{IDC_BUTTON_STARTEDIT, "上で選択した編集項目の編集を開始します。"},
	{0,0}};

// ダイアログのアンカー等に関する情報
const DLGITEM dlgitems[] = {
	{IDC_CHECK_ALWAYS_FIND,     ANCHOR_TOPRIGHT},
	{IDC_EDIT_SEARCH,           ANCHOR_TOPLEFTRIGHT},
	{IDC_BUTTON_SEARCH,         ANCHOR_TOPRIGHT},
	{IDC_CHECK_SHOW_ENABLE_ONLY,ANCHOR_TOPLEFT},
	{IDC_TREE1,                 ANCHOR_CENTER},
	{IDC_STATIC_TITLE,          ANCHOR_BOTTOMLEFTRIGHT},
	{IDC_EDIT_MESSAGE,          ANCHOR_BOTTOMLEFTRIGHT},
	{IDC_BUTTON_STARTEDIT,      ANCHOR_BOTTOMRIGHT},
	{0,0}
};

// キーワード検索ダイアログのメッセージハンドラ
INT_PTR CALLBACK MainKwdSearchDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static SIZEDLG *sizedlg;
	static ComboboxQueue *cbq;

	switch (message) {
		case WM_INITDIALOG:
			{
				// アイコンの設定
				HICON hIcon = (HICON)LoadImage(hInst, (LPCTSTR)IDI_FOLDER_BROWSE, IMAGE_ICON, 16, 16, LR_SHARED);
				SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);

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

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

				// レジストリから設定を読み出す
				REG reg;
				if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_READONLY)) {
					// 使用可能なもののみ表示する
					if (reg.ReadInteger("SearchDlg_EnableOnly", 1) != 0) {
						SendDlgItemMessage(hDlg, IDC_CHECK_SHOW_ENABLE_ONLY, BM_SETCHECK, BST_CHECKED, NULL);
					}
					// 検索ボタンを押さなくても検索を開始する
					if (reg.ReadInteger("SearchDlg_AlwaysFind", 0) != 0) {
						SendDlgItemMessage(hDlg, IDC_CHECK_ALWAYS_FIND, BM_SETCHECK, BST_CHECKED, NULL);
						// 検索ボタンは非活性化
						EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_SEARCH), FALSE);
					}

					if ( rnsf7RegRead("NoSaveSettings", 0) == 0 ) {
						// ダイアログの位置とサイズ
						WINDOWPLACEMENT wp;
						ZeroMemory(&wp, sizeof(wp));
						wp.length = sizeof(WINDOWPLACEMENT);
						if (1 == reg.ReadInteger("SearchDlg_Max")) {
							wp.showCmd = SW_SHOWMAXIMIZED;
						}
						else {
							wp.showCmd = SW_SHOWNORMAL;
						}

						wp.rcNormalPosition.left   = reg.ReadInteger("SearchDlg_wLeft");
						wp.rcNormalPosition.top    = reg.ReadInteger("SearchDlg_wTop");
						wp.rcNormalPosition.right  = reg.ReadInteger("SearchDlg_wWidth") + wp.rcNormalPosition.left;
						wp.rcNormalPosition.bottom = reg.ReadInteger("SearchDlg_wHeight") + wp.rcNormalPosition.top;

						// ウィンドウの左上座標が画面外なら、ウィンドウ位置をデフォルトの位置にする
						POINT pt = {wp.rcNormalPosition.left, wp.rcNormalPosition.top};
						if (NULL != MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
							SetWindowPlacement(hDlg, &wp);
						}
					}
					reg.CloseKey();
				}

				// 検索用情報の作成
				if (readySearch(hDlg) == false) {
					EndDialog(hDlg, 0);
					return TRUE;
				}

				// ツリービューの最初の準備
				startSearch(hDlg);

				// フォーカスは検索ボックスへ
				SetFocus(GetDlgItem(hDlg, IDC_EDIT_SEARCH));

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

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

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

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

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

			// サーチ用の情報を破棄
			// freeSearchData();  // 破棄はせず、とっておこう。
			return TRUE;

		case WM_TIMER:
			if (wParam == (WPARAM)TIMERID) {
				startSearch(hDlg);
				KillTimer(hDlg, TIMERID);
			}
			return (INT_PTR)TRUE;

		case WM_NOTIFY:
			// ツリービューからの通知メッセージ受け取り
			if (wParam == (WPARAM)IDC_TREE1) {
				LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam;
				LPNMHDR      lpnmh = (LPNMHDR)lParam; 

				if (pnmtv->hdr.code == TVN_SELCHANGED) { // ツリービューで選択の変更が発生
					ITEMGROUP *pig = (ITEMGROUP *)pnmtv->itemNew.lParam;
					BOOL bStartEditEnable = FALSE;

					if (pig) {
						SetDlgItemText(hDlg, IDC_EDIT_MESSAGE, pig->keyword_show.c_str());
						if (pig->bOS & btOSMask) {
							// 編集開始ボタンを有効にする
							bStartEditEnable = TRUE;
						}
					}
					else {
						if (num_of_items == 0) {
							SetDlgItemText(hDlg, IDC_EDIT_MESSAGE, "入力された検索文字列では抽出できません。検索文字列を再入力してください。");
						}
						else {
							char s[256];
							wsprintf(s, "%d個の編集項目が抽出されています。\r\n編集したい設定項目を、上のツリービューから選択し、「選択した項目を編集する」ボタンを押してください。", num_of_items);
							SetDlgItemText(hDlg, IDC_EDIT_MESSAGE, s);
						}
					}
					EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_STARTEDIT), bStartEditEnable);
				}
				else if (lpnmh->code == NM_DBLCLK) { // ダブルクリック
					// 編集ボタン押下に転送
					SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_BUTTON_STARTEDIT, 0), 0);
				}
			}
			return (INT_PTR)TRUE;

		case WM_COMMAND:
			switch(LOWORD(wParam)) {
				case IDC_CHECK_ALWAYS_FIND: // 検索ボタンを押さなくても検索
					if (SendDlgItemMessage(hDlg, IDC_CHECK_ALWAYS_FIND, BM_GETCHECK, 0, 0) == BST_CHECKED) {
						// 検索ボタンを無効化する
						EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_SEARCH), FALSE);
						// 検索開始タイマを設定する。
						SetTimer(hDlg, TIMERID, TIMERWAIT, NULL);
					}
					else {
						// 検索ボタンを有効に戻す
						EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_SEARCH), TRUE);
					}
					return (INT_PTR)TRUE;

				case IDC_EDIT_SEARCH: // 検索：編集ボックスが編集された
					if (HIWORD(wParam) == CBN_EDITCHANGE || HIWORD(wParam) == CBN_SELCHANGE) {
						if (SendDlgItemMessage(hDlg, IDC_CHECK_ALWAYS_FIND, BM_GETCHECK, 0, 0) == BST_CHECKED) {
							// 検索ボタンを押さなくても検索する設定になっている場合、
							// 検索開始タイマを設定する。
							SetTimer(hDlg, TIMERID, TIMERWAIT, NULL);
						}
					}
					return (INT_PTR)TRUE;

				case IDC_CHECK_SHOW_ENABLE_ONLY: // 利用可能なものしか表示しない
					startSearch(hDlg);
					return (INT_PTR)TRUE;

				case IDC_BUTTON_SEARCH: // 検索ボタン押下
					if (startSearch(hDlg)) {		// 検索開始
						// 入力文字列をコンボボックスに登録
						int lenSearchText = GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT_SEARCH));
						if (lenSearchText > 0) {
							++lenSearchText;
							LPTSTR pbuf = (LPTSTR)calloc(lenSearchText, sizeof(TCHAR));
							if (pbuf) {
								GetDlgItemText(hDlg, IDC_EDIT_SEARCH, pbuf, lenSearchText);
								cbq->Add(pbuf);
								free(pbuf);
							}
						}
					}
					SetFocus(GetDlgItem(hDlg, IDC_TREE1));	// ツリービューにフォーカスを移動
					return (INT_PTR)TRUE;

				case IDCANCEL:	// このダイアログを閉じる
					{
						KillTimer(hDlg, TIMERID);

						REG reg;
						if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_CREATE)) {
							// レジストリへ設定を保存する
							reg.WriteInteger("SearchDlg_EnableOnly", (int)SendDlgItemMessage(hDlg, IDC_CHECK_SHOW_ENABLE_ONLY, BM_GETCHECK, 0, NULL)); // 使用可能なもののみ表示する
							reg.WriteInteger("SearchDlg_AlwaysFind", (int)SendDlgItemMessage(hDlg, IDC_CHECK_ALWAYS_FIND, BM_GETCHECK, 0, NULL)); // 検索ボタンを押さなくても検索を開始する

							// ダイアログの位置とサイズ
							WINDOWPLACEMENT wp;
							wp.length = sizeof(WINDOWPLACEMENT);
							if ( GetWindowPlacement(hDlg, &wp) ) {
								reg.WriteInteger("SearchDlg_Max",    wp.showCmd == SW_SHOWMAXIMIZED);
								reg.WriteInteger("SearchDlg_wLeft",  wp.rcNormalPosition.left);
								reg.WriteInteger("SearchDlg_wTop",   wp.rcNormalPosition.top);
								reg.WriteInteger("SearchDlg_wWidth", wp.rcNormalPosition.right - wp.rcNormalPosition.left);
								reg.WriteInteger("SearchDlg_wHeight",wp.rcNormalPosition.bottom - wp.rcNormalPosition.top);
							}
							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;

				case IDC_BUTTON_STARTEDIT: // 編集を開始する
					{
						// ツリービューのカーソル位置を得て、その位置の情報を読み取る
						HWND hTreeView = GetDlgItem(hDlg, IDC_TREE1);
						HTREEITEM hTreeCursor = TreeView_GetSelection(hTreeView);

						if (hTreeCursor != NULL) { // 選択がなかったら終了
							TVITEM tvi;

							tvi.mask = TVIF_PARAM;
							tvi.hItem = hTreeCursor;
							TreeView_GetItem(hTreeView, &tvi);

							if (tvi.lParam != 0) { // アイテムでなかったら終了
								ITEMGROUP *ig = (ITEMGROUP *)tvi.lParam;
								if (ig->bOS & btOSMask) {
									openrsc(hDlg, ig->rsc.c_str(), ig->id.c_str());
								}

								// 入力文字列をコンボボックスに登録
								int lenSearchText = GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT_SEARCH));
								if (lenSearchText > 0) {
									++lenSearchText;
									LPTSTR pbuf = (LPTSTR)calloc(lenSearchText, sizeof(TCHAR));
									if (pbuf) {
										GetDlgItemText(hDlg, IDC_EDIT_SEARCH, pbuf, lenSearchText);
										cbq->Add(pbuf);
										free(pbuf);
									}
								}
							}
						}
					}
					return (INT_PTR)TRUE;
			}
			break;
	}
	return (INT_PTR)FALSE;
}

void kwdDialog(HWND hWnd)
{
	DialogBox(hInst, (LPCTSTR)IDD_SEARCHDLG, hWnd, (DLGPROC)MainKwdSearchDialog);
}
