#include "stdafx.h"
#include "FWatchApp.hpp"
#include "resource.h"

#include "FWatchSettingDlg.hpp"
#include "SettingDlg.hpp"
#include "Utility.hpp"

#include <assert.h>

#include <commctrl.h>

#include "WaitCursor.hpp"



FWatchSettingDlg::FWatchSettingDlg()
	: hDlg_(NULL)
	, hList_(NULL)
	, uTimer_(0)
{
}

FWatchSettingDlg::~FWatchSettingDlg()
{
}

void FWatchSettingDlg::show(HWND hWnd)
{
	if (::DialogBoxParam(
			app.getInstanceHandle(),
			(LPCTSTR) IDD_SETDLG,
			hWnd,
			(DLGPROC) DlgProc,
			(LPARAM) this
			) == IDOK ) {
		app.getCL().SaveInfData(app.getInfPath());
	}
}

void FWatchSettingDlg::onInitDialog()
{
	hList_ = ::GetDlgItem(hDlg_, IDC_LIST);
	assert(hList_ != NULL && "リストビューのウィンドウハンドルがNULLです。");

	const CWatchThreadGroup& cl = app.getCL();

	// スタイルの変更
	// (チェックボックス付きオーナーデータ)
	ListView_SetExtendedListViewStyle(hList_, LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
	ListView_SetCallbackMask(hList_, LVIS_STATEIMAGEMASK);

	// カラムヘッダの登録
	LVCOLUMN data;
	data.mask = LVCF_TEXT | LVCF_WIDTH;
	data.cx = 100;
	const int colmx = GetColumnCount();
	for (int col=0; col < colmx; col++) {
		const tstring columnTitle = GetColTitle(col);
		data.pszText = (LPTSTR) columnTitle.c_str();
		ListView_InsertColumn(hList_, col, &data);
	}

	// リスト項目の個数
	const int count = cl.size();
	ListView_SetItemCountEx(hList_, count, 0);

	// ステータスとリスト項目の定期リフレッシュ
	uTimer_ = ::SetTimer(hDlg_, 1, 5000, NULL);
	displayStatus();
}

void FWatchSettingDlg::onClick(LPNMITEMACTIVATE pActive)
{
	int row = pActive->iItem;
	int col = pActive->iSubItem;
	int px = pActive->ptAction.x;

	CWatchThreadGroup& cl = app.getCL();

	if (col != 0 || px > 16 || row < 0 || row >= (int) cl.size()) {
		// 範囲外
		return;
	}

	bool running = cl.isRunning(row);
	if (running) {
		cl.stop(row);
	}
	else {
		cl.start(row);
	}
	displayStatus();
}

void FWatchSettingDlg::onGetDispInfo(LV_DISPINFO *pDispInfo)
{
	int idx = pDispInfo->item.iItem;
	int col = pDispInfo->item.iSubItem;

	if (pDispInfo->item.mask & LVIF_TEXT) {
		columnTextTemp_ = GetColText(idx, col);
		pDispInfo->item.pszText = const_cast<LPTSTR>(columnTextTemp_.c_str());
	}

	if (pDispInfo->item.mask & LVIF_STATE) {
		// スレッドの実行状態をチェックボックスに表示
		const CWatchThreadGroup& cl = app.getCL();
		if ( !cl.isRunning(idx)) {
			pDispInfo->item.state = INDEXTOSTATEIMAGEMASK(1); // Unchecked
		}
		else {
			pDispInfo->item.state = INDEXTOSTATEIMAGEMASK(2); // Checked
		}
	}
}

void FWatchSettingDlg::onTimer(UINT uTimer)
{
	if (uTimer == uTimer_) {
		displayStatus();
	}
}

void FWatchSettingDlg::displayStatus() const
{
	HWND hStatus = ::GetDlgItem(hDlg_, IDC_THREADGROUP_STATUS);
	assert(hStatus != NULL && "ステータス表示ラベルのウィンドウハンドルがNULLです。");

	const CWatchThreadGroup& cl = app.getCL();

	tstring status = app.LoadString(cl.isDryrRun() ? IDS_TASKMES_MODEDRYRUN : IDS_TASKMES_MODESTANDALONE);
	if (cl.isModified()) {
		status += app.LoadString(IDS_TASKMES_SETTINGDIRTY);
	}
	::SetWindowText(hStatus, status.c_str());

	// リストビューを再描画する。
	int count = cl.size();
	if (count > 0) {
		ListView_RedrawItems(hList_, 0, cl.size());
	}

	// 変更がなければOKボタンは非活性にする
	EnableWindow(GetDlgItem(hDlg_, IDOK), cl.isModified() ? TRUE : FALSE);
}

void FWatchSettingDlg::onEdit(bool bEditMode)
{
	assert(hList_ && "初期化されていません。");

	// リストコントロールの選択行を取得する。
	int sel = -1;
	for (int i = 0; i < ListView_GetItemCount(hList_); i++) {
		if (ListView_GetItemState(hList_, i, LVIS_SELECTED)) {
			LVITEM item;
			item.mask = LVIF_PARAM;
			item.iItem = i;
			if (ListView_GetItem(hList_, &item)) {
				sel = i;
				break;
			}
		}
	}

	if (bEditMode && sel < 0) {
		// 編集モード時に選択がなければ何もしない。
		return;
	}

	// 追加モード時に既存項目を選択している場合
	// コピーするか、空から作成するか選択する。
	if ( !bEditMode && sel >= 0) {
		if (app.showMessageBox(hDlg_, MB_YESNO | MB_ICONQUESTION, NULL, IDS_SETDLG_COPYSELECTION) == IDNO) {
			sel = -1;
		}
	}

	CWatchThreadGroup& cl = app.getCL();

	CSettingDlg sdlg(sel >= 0 ? cl.getInfo(sel) : CSettingInfo());

	if (sdlg.show(hDlg_)) {
		CWaitCursor waitCursor;
		const CSettingInfo info = sdlg.createSettingInfo();
		
		CWatchThread *pWatchThread = NULL;
		if (bEditMode) {
			pWatchThread = cl.replace(sel, info);
		}
		else {
			pWatchThread = cl.append(info);
			ListView_SetItemCountEx(hList_, cl.size(), 0);
		}

		if (pWatchThread && !pWatchThread->getSettingInfo().isWatchDisabled()) {
			pWatchThread->Start();
		}

		displayStatus();
	}
}

void FWatchSettingDlg::onDelete()
{
	CWaitCursor waitCursor;

	int idx = 0;
	while (idx < ListView_GetItemCount(hList_)) {
		if (ListView_GetItemState(hList_, idx, LVIS_SELECTED)) {
			app.getCL().erase(idx);
			ListView_DeleteItem(hList_, idx);
			idx = 0;
			continue;
		}
		idx++;
	}

	displayStatus();
}

BOOL FWatchSettingDlg::onCommand(UINT btn)
{
	BOOL result = TRUE;
	switch (btn)
	{
	case IDOK:
		::EndDialog(hDlg_, IDOK);
		break;

	case IDCANCEL:
		::EndDialog(hDlg_, IDCANCEL);
		break;

	case IDC_ADD:
	case IDC_EDIT:
		onEdit(btn == IDC_EDIT);
		break;

	case IDC_DEL:
		onDelete();
		break;

	default:
		result = FALSE;
		break;
	}

	displayStatus();
	return FALSE;
}

BOOL FWatchSettingDlg::onNotify(WPARAM wParam, LPARAM lParam)
{
	const UINT uID = (UINT) wParam;
	const NMHDR* pNMHeader = (NMHDR*) lParam;
	
	if (uID == IDC_LIST) {
		switch (pNMHeader->code) {
		case NM_DBLCLK:
			onEdit(true);
			break;
		
		case LVN_GETDISPINFO:
			onGetDispInfo((LV_DISPINFO *) lParam);
			break;

		case NM_CLICK:
			onClick((LPNMITEMACTIVATE) lParam);
			break;
		}
		return TRUE;
	}

	return FALSE;
}

tstring FWatchSettingDlg::GetColTitle(int col) const
{
	//required:
	assert(col >= 0 && col < GetColumnCount() && "カラムの範囲外です。");
	
	//do:
	return app.LoadString(IDS_SETTINGIDX0 + col);
}

int FWatchSettingDlg::GetColumnCount() const
{
	return 12;
}

tstring FWatchSettingDlg::GetColText(unsigned int idx, unsigned int col) const
{
	const CWatchThreadGroup& cl = app.getCL();
	//required:
	assert(idx >= 0 && idx < cl.size() && "アイテムの範囲外です。");

	//do:
	const CSettingInfo& settingInfo = cl.getInfo(idx);
	switch (col)
	{
	case 0:
		return settingInfo.getWatchDir();
	case 1:
		return settingInfo.getWatchFile();
	case 2:
		return settingInfo.getAction();
	case 3:
		return settingInfo.getAppName();
	case 4:
		return settingInfo.getParam();
	case 5:
		return IntToString(settingInfo.getMaxProcess());
	case 6:
		return settingInfo.getShowWindow().getDescription();
	case 7:
		return IntToString(settingInfo.getWaitWrite());
	case 8:
		return IntToString(settingInfo.getWatchDelay());
	case 9:
		return IntToString(settingInfo.getDeletePending());
	case 10:
		return settingInfo.getPersist();
	case 11:
		return settingInfo.getLog();
	default:
		assert(false && "範囲外のカラム指定です。");
		return _T("*ERR*");
	}
}

void FWatchSettingDlg::onHelp(LPHELPINFO pHelpInfo)
{
	if (pHelpInfo->iContextType != HELPINFO_WINDOW) {
		return;
	}
	app.showContextHelp(
		hDlg_,
		pHelpInfo->dwContextId,
		pHelpInfo->MousePos
		);
}

LRESULT CALLBACK FWatchSettingDlg::DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	FWatchSettingDlg* me = (FWatchSettingDlg*) ::GetWindowLongPtr(hDlg, DWLP_USER);
	assert((me == NULL || me->hDlg_ == hDlg) && "参照が不正です。");

	if (message == WM_INITDIALOG) {
		me = (FWatchSettingDlg*) lParam;
		assert(me != NULL && "インスタンスへのポインタが設定されていません。");

		::SetWindowLongPtr(hDlg, DWLP_USER, (LONG) me);
		me->hDlg_ = hDlg;
		me->onInitDialog();
	}
	else if (me != NULL) {
		switch (message) {
		case WM_COMMAND:
			return me->onCommand(LOWORD(wParam));
		
		case WM_NOTIFY:
			return me->onNotify(wParam, lParam);

		case WM_TIMER:
			me->onTimer((UINT) wParam);
			return TRUE;

		case WM_HELP:
			me->onHelp((LPHELPINFO) lParam);
		}
	}

	return FALSE;
}

