/* folder_dialog.cpp
   Copyright (C) 2005-2006 Free Software Foundation, Inc.

This file is part of Mysaifu JVM

Mysaifu JVM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

Mysaifu JVM is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
*/

// Original source from GSFinder+
// http://page.freett.com/todamitsu/GSFinderE.htm

#include "StdAfx.h"
#include "resource.h"
#include "folder_dialog.h"

/**
 * ダイアログボックスのコールバック
 */
LRESULT CALLBACK FolderDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);


static void on_expanding(NMTREEVIEW* lpnmtv);
static void on_expanded(NMTREEVIEW* lpnmtv);
static void get_selected_path(HWND hwndTV, HTREEITEM hItem, LPTSTR pszKey);
static int enum_children(HWND hwndTV, HTREEITEM hParent, LPTSTR pszPath);
static int has_children(LPTSTR pszPath, LPTSTR pszKey);
static _TCHAR* get_device_name();
static void make_path(_TCHAR* buff, _TCHAR* dir, _TCHAR* file);
static void set_current_folder(HWND hwndTV, LPCTSTR lpszPath);

/**
 * フォルダダイアログを表示する
 */
LPITEMIDLIST WINAPI
MySHBrowseForFolder(LPMYBROWSEINFO lpbi) {
	return (LPITEMIDLIST)
			DialogBoxParam(GetModuleHandle(NULL),
				   MAKEINTRESOURCE(IDD_FOLDER),
				   lpbi->hwndOwner,
				   (DLGPROC) FolderDialogProc,
				   (LPARAM) lpbi);
}


LRESULT CALLBACK FolderDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	static LPMYBROWSEINFO lpbi;
	switch (message) {
	case WM_INITDIALOG:
		{
			lpbi = (LPMYBROWSEINFO) lParam;
			HWND hTreeView = GetDlgItem(hDlg, IDC_TREE_FOLDER);

			SHINITDLGINFO shidi;
			shidi.dwMask = SHIDIM_FLAGS;
			shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
			shidi.hDlg = hDlg;
			SHInitDialog(&shidi);

			SendMessage(hTreeView, CCM_SETVERSION, COMCTL32_VERSION, 0);

			//サイズ調整 (TreeView)
			RECT rcdlg, rctree;
			GetWindowRect(hDlg, &rcdlg);
			GetWindowRect(hTreeView, &rctree);
			int height = rcdlg.bottom - rctree.top - rctree.left;
			int width = (rcdlg.right - rcdlg.left) - (rctree.left * 2);
			ScreenToClient(hDlg, (LPPOINT) &rctree);
			SetWindowPos(hTreeView,
						 NULL,
						 rctree.left,
						 rctree.top,
						 width,
						 height,
						 SWP_NOMOVE | SWP_NOZORDER);

			//イメージリスト作成
			SHFILEINFO shfi = {0};
			HIMAGELIST hImageList = (HIMAGELIST) SHGetFileInfo(_T("\\"),
															   0,
															   &shfi,
															   sizeof(shfi),
															   SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
			TreeView_SetImageList(hTreeView, hImageList, TVSIL_NORMAL);

			//フォルダ読み込み
			HCURSOR hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

			// ルートフォルダを挿入する
			TVINSERTSTRUCT tvis;
			tvis.hParent = NULL;
			tvis.hInsertAfter	= TVI_SORT;
			tvis.item.mask		= TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
			tvis.item.pszText	= get_device_name();
			tvis.item.iImage	= shfi.iIcon;
			tvis.item.iSelectedImage = shfi.iIcon;
			tvis.item.cChildren	= 1;

			// ルートフォルダを展開する
			HTREEITEM hRoot = TreeView_InsertItem(hTreeView, &tvis);
			TreeView_Expand(hTreeView, hRoot, TVE_EXPAND);

			SetCursor(hCursor);

			// タイトルを設定する
			if (lpbi->lpszTitle) {
				SetWindowText(GetDlgItem(hDlg, IDC_TITLE), lpbi->lpszTitle);
			}
			
			// カレントフォルダを設定する
			if (lpbi->pidlRoot) {
				_TCHAR path[MAX_PATH];
				SHGetPathFromIDList(lpbi->pidlRoot, path);
				set_current_folder(hTreeView, path);
			}

			return TRUE;
		}
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDOK:
			{
				HWND hTreeView = GetDlgItem(hDlg, IDC_TREE_FOLDER);
				HTREEITEM hTreeItem = TreeView_GetSelection(hTreeView);

				// 項目の名前を取得する
				if (lpbi->pszDisplayName) {
					TV_ITEM tvi = {0};
					tvi.mask       = TVIF_TEXT;
					tvi.hItem      = hTreeItem;
					tvi.pszText    = lpbi->pszDisplayName;
					tvi.cchTextMax = MAX_PATH;
					TreeView_GetItem(hTreeView, &tvi);
				}

				// フルパスを取得する
				_TCHAR path[MAX_PATH];
				get_selected_path(hTreeView, hTreeItem, path);

				// ITEMIDLISTに変換する
				IShellFolder* isf;
				ULONG chEaten;
				ITEMIDLIST* idlist = NULL;
				if (SHGetDesktopFolder(&isf) == NOERROR) {
					isf->ParseDisplayName(NULL, NULL, path, &chEaten, &idlist, NULL);
					isf->Release();
				}

				// フォルダのアイコンインデックスを取得する
				SHFILEINFO shfi = {0};
				SHGetFileInfo(path,
						      0,
							  &shfi,
							  sizeof(shfi),
							  SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
				lpbi->iImage = shfi.iIcon;

				EndDialog(hDlg, (int) idlist);
			}
			return TRUE;

		case IDCANCEL:
			EndDialog(hDlg, NULL);
			return TRUE;
		}
		break;

	case WM_NOTIFY:
		{
			NMHDR* lpnmh = (NMHDR*)lParam;
			if (lpnmh->idFrom == IDC_TREE_FOLDER) {
				switch (lpnmh->code) {
				case TVN_ITEMEXPANDING:
					{
						HCURSOR hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
						on_expanding((NMTREEVIEW*) lpnmh);
						SetCursor(hCursor);
					}
					return TRUE;

				case TVN_ITEMEXPANDED:
					on_expanded((NMTREEVIEW*) lpnmh);
					break;
				}
			}
		}
		break;
		
	}
	return FALSE;
}

static void get_selected_path(HWND hwndTV, HTREEITEM hItem, LPTSTR pszKey) {
	TCHAR szName[MAX_PATH];
    TV_ITEM tvi = {0};
   
	// 親を取得
    HTREEITEM hParent;
	hParent = TreeView_GetParent(hwndTV, hItem);
    if (hParent) { 
        // 項目の親の親・・・を取得する
        get_selected_path(hwndTV, hParent, pszKey);

        // 項目の名前を取得する
		tvi.mask       = TVIF_TEXT;
		tvi.hItem      = hItem;
		tvi.pszText    = szName;
		tvi.cchTextMax = MAX_PATH;
		TreeView_GetItem(hwndTV, &tvi);

		// 親のパスに今のフォルダ名を追加
		_TCHAR tmp[MAX_PATH];
		make_path(tmp, pszKey, szName);
		_tcscpy(pszKey, tmp);
	} else {
        // ルートパス
		_tcscpy(pszKey, _T("\\"));
    }
}

static void on_expanded(NMTREEVIEW* lpnmtv) {
	// 次にブランチを展開するときに改めて列挙するのでサブキーを削除する
	if (lpnmtv->action == TVE_COLLAPSE) {
		HTREEITEM hChild;
		HTREEITEM hNext;

		hChild = TreeView_GetChild (lpnmtv->hdr.hwndFrom, lpnmtv->itemNew.hItem);
		while (hChild) {
			hNext = TreeView_GetNextItem (lpnmtv->hdr.hwndFrom, hChild, TVGN_NEXT);
			TreeView_DeleteItem (lpnmtv->hdr.hwndFrom, hChild);
			hChild = hNext;
		}
	}
}

static void on_expanding(NMTREEVIEW* lpnmtv) {
	if (lpnmtv->action == TVE_EXPAND) {
		TCHAR szKey[MAX_PATH];
		//フォルダ名を取得
		get_selected_path(lpnmtv->hdr.hwndFrom, lpnmtv->itemNew.hItem, szKey);
		//子ツリーを列挙する
		enum_children(lpnmtv->hdr.hwndFrom, lpnmtv->itemNew.hItem, szKey);
	}
}

static BOOL has_children(LPTSTR pszPath, LPTSTR pszKey) {
	_TCHAR szName[MAX_PATH];
	HANDLE ff;
	WIN32_FIND_DATA wfd;
    BOOL result = FALSE;

	make_path(szName, pszPath, pszKey);
	if (_tcslen(szName) + 4 >= MAX_PATH) {
		// バッファサイズオーバー
		return FALSE;
	}
	_tcscat(szName, _T("\\*.*"));
	ff = FindFirstFile(szName, &wfd);
	if (ff != INVALID_HANDLE_VALUE) {
		do {
			if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				// サブディレクトリがある
				result = TRUE;
				break;
			}
		} while (FindNextFile (ff, &wfd) != 0);
		FindClose(ff);
    }
	return result;
}

///////////////////////////////////////////////////////////////
// EnumChildren - キーのサブキーを列挙する
static int enum_children(HWND hwndTV, HTREEITEM hParent, LPTSTR pszPath) {
    BOOL show_button;
	HANDLE ff;
	WIN32_FIND_DATA wfd;
	TCHAR szName[MAX_PATH];
	TVINSERTSTRUCT tvis;
	DWORD dwCnt = 0;

	make_path(szName, pszPath, _T("*.*"));

	// "My Documents"のフォルダ名を得ておく
	_TCHAR my_documents_path[MAX_PATH];
	SHGetDocumentsFolder(_T("\\"), my_documents_path);

	ff = FindFirstFile (szName, &wfd);
	if (ff != INVALID_HANDLE_VALUE) {
		do {
			if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				// 下位のディレクトリがあるか検査
				show_button = has_children(pszPath, wfd.cFileName);

				// ツリー ビュー コントロールにキーを挿入する
				tvis.hParent      = hParent;
				tvis.hInsertAfter = TVI_SORT;
				tvis.item.mask    = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
				tvis.item.pszText = wfd.cFileName;
				tvis.item.cChildren = show_button;

				// フォルダの種別ごとに表示するアイコンを変更する
				_TCHAR fullpath[MAX_PATH];
				make_path(fullpath, pszPath, wfd.cFileName);
				SHFILEINFO shfi = {0};
				SHGetFileInfo(fullpath,
							  0,
							  &shfi,
							  sizeof(shfi),
							  SHGFI_SMALLICON | SHGFI_SYSICONINDEX);
				tvis.item.iImage = shfi.iIcon;
				tvis.item.iSelectedImage = shfi.iIcon;

				TreeView_InsertItem(hwndTV, &tvis);

				dwCnt++;
			}
		} while (FindNextFile (ff, &wfd) != 0);
		FindClose(ff);
	}

    // サブキーがない場合は展開ボタンを削除する
    TVITEM tvi;
	tvi.hItem = hParent;
	tvi.mask  = TVIF_CHILDREN;
    if (dwCnt == 0)
        tvi.cChildren = 0;
    else
        tvi.cChildren = 1;
	TreeView_SetItem (hwndTV, &tvi);

	// ソート
	TreeView_SortChildren(hwndTV, hParent, 0);

	return dwCnt;
}

/**
 * デバイス名を取得する
 */
static _TCHAR* get_device_name() {
	HKEY hKey;
	static _TCHAR device_name[256];
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Ident"), 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
		DWORD dwType, dwSize;
		dwSize = sizeof(device_name);
		RegQueryValueEx(hKey,
						_T("Name"),
						0,
						&dwType,
						(LPBYTE) device_name,
						&dwSize);
		RegCloseKey(hKey);
	}
	return device_name;
}

static void make_path(_TCHAR* buff, _TCHAR* dir, _TCHAR* file) {
	_TCHAR* format;
	if (dir[_tcslen(dir) - 1] == _T('\\')) {
		format = _T("%s%s");
	} else {
		format = _T("%s\\%s");
	}
	_sntprintf(buff,
			   MAX_PATH - 1,
			   format,
			   dir,
			   file);
}

static void set_current_folder(HWND hwndTV, LPCTSTR lpszPath) {
	LPCTSTR lpszStart = _tcschr(lpszPath, _T('\\'));
	HTREEITEM hSelectItem = TreeView_GetRoot(hwndTV);

	HTREEITEM hParent = hSelectItem;
	TreeView_Expand(hwndTV, hParent, TVE_EXPAND);

	while (lpszStart) {
		TCHAR szName[MAX_PATH] = _T("");
		LPCTSTR lpszEnd = _tcschr(lpszStart + 1, _T('\\'));
		if (lpszEnd) {
			int len = lpszEnd - (lpszStart + 1);
			_tcsncpy(szName, ++lpszStart, len);
			szName[len] = 0;
		}
		else
			_tcscpy(szName, ++lpszStart);

		HTREEITEM hChild = TreeView_GetChild(hwndTV, hParent);
		while (hChild) {
			TCHAR szChildName[MAX_PATH] = _T("");
			TVITEM tvi;
			tvi.mask = TVIF_TEXT | TVIF_HANDLE;
			tvi.pszText = szChildName;
			tvi.cchTextMax = MAX_PATH;
			tvi.hItem = hChild;
			TreeView_GetItem(hwndTV, &tvi);
			if (_tcscmp(szName, szChildName) == 0) {
				hSelectItem = hParent = hChild;
				TreeView_Expand(hwndTV, hParent, TVE_EXPAND);
				break;
			}

			hChild = TreeView_GetNextItem(hwndTV, hChild, TVGN_NEXT);
		}
		if (!hChild) break;

		lpszStart = _tcschr(lpszStart, _T('\\'));
	}

	TreeView_SelectItem(hwndTV, hSelectItem);
}