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

#include "ActionInvoker.hpp"
#include "ProcessActionInvoker.hpp"
#include "UDPSendActionInvoker.hpp"
#include "BeepActionInvoker.hpp"

#include <vector>

#include <assert.h>

#include "Utility.hpp"


#include <strsafe.h>
#pragma comment(lib, "strsafe.lib")




ActionInvoker::ActionInvoker(const CSettingInfo& v_settingInfo) throw()
	: settingInfo_(v_settingInfo)
	, pLogger_(NULL)
{
}

ActionInvoker::~ActionInvoker() throw()
{
}


void ActionInvoker::setLogger(Logger* pLogger) throw()
{
	pLogger_ = pLogger;
}

Logger* ActionInvoker::getLogger() const throw()
{
	return pLogger_;
}

bool ActionInvoker::doAction(const tstring& v_absolutePath, const CFileInfo& fileInfo) throw()
{
	//required:
	assert( !v_absolutePath.empty() && "v_absolutePathに空は指定できません。");
	
	//do:

	// 実行パラメータの作成
	tstring dirname = GetDirName(v_absolutePath);
	tstring basename = GetBaseName(v_absolutePath);
	tstring namebody = GetNameBody(v_absolutePath);

	tstring watchDir = EnsureLast(settingInfo_.getWatchDir(), _T("\\"));
	tstring relativePath = GetRelativePath(watchDir, v_absolutePath);
	tstring relativeDir = GetRelativePath(watchDir, dirname);

	tstring shortPath = GetShortPathName(v_absolutePath);
	tstring shortDir = GetShortPathName(dirname);

	SYSTEMTIME systime = {0};
	GetLocalTime(&systime);

	TCHAR szDate[9] = {0};
	TCHAR szTime[7] = {0};
	TCHAR szCount[10] = {0};
	TCHAR szRunCount[10] = {0};
	
	HRESULT hr;

	hr = StringCbPrintf(
		szDate,
		sizeof(szDate),
		_T("%04d%02d%02d"),
		systime.wYear,
		systime.wMonth,
		systime.wDay
		);
	hr = StringCbPrintf(
		szTime,
		sizeof(szTime),
		_T("%02d%02d%02d"),
		systime.wHour,
		systime.wMinute,
		systime.wSecond
		);
	hr = StringCbPrintf(
		szCount,
		sizeof(szCount),
		_T("%d"),
		internalGetRunCount()
		);
	hr = StringCbPrintf(
		szRunCount,
		sizeof(szRunCount),
		_T("%d"),
		fileInfo.getRunCount()
		);

	tstring attrDirectory =
		((fileInfo.getAttribute() & FILE_ATTRIBUTE_DIRECTORY) != 0) ? _T("1") : _T("0"); // ファイルしか通知しないので常に0になる。
	tstring attrArchive =
		((fileInfo.getAttribute() & FILE_ATTRIBUTE_ARCHIVE) != 0) ? _T("1") : _T("0");
	tstring attrSystem =
		((fileInfo.getAttribute() & FILE_ATTRIBUTE_SYSTEM) != 0) ? _T("1") : _T("0");
	tstring attrHidden =
		((fileInfo.getAttribute() & FILE_ATTRIBUTE_HIDDEN) != 0) ? _T("1") : _T("0");
	tstring attrReadonly =
		((fileInfo.getAttribute() & FILE_ATTRIBUTE_READONLY) != 0) ? _T("1") : _T("0");


	// 実行パラメータのセット(置換変数)
	PropertyStringExpander expander;

	app.setAppConfVariablesTo(expander, true);

	struct ParamsPair
	{
		tstring name;
		tstring value;
	};

	ParamsPair paramsPairs[] = {
		{_T("PATH"),     v_absolutePath},
		{_T("FULLPATH"), v_absolutePath},
		{_T("DIR"),      dirname},
		{_T("DIRNAME"),  dirname},
		{_T("NAME"),     basename},
		{_T("BASENAME"), basename},
		{_T("NAMEBODY"), namebody},
		{_T("DATE"),     szDate},
		{_T("TIME"),     szTime},
		{_T("SPATH"),    shortPath},
		{_T("SDIR"),     shortDir},
		{_T("COUNT"),    szCount},
		{_T("RUNCOUNT"), szRunCount},
		{_T("WATCHDIR"), watchDir},
		{_T("RELATIVEPATH"), relativePath},
		{_T("RELATIVEDIR"),  relativeDir},
		{_T("ATTRIBUTE_DIRECTORY"), attrDirectory},
		{_T("ATTRIBUTE_ARCHIVE"),   attrArchive},
		{_T("ATTRIBUTE_SYSTEM"),    attrSystem},
		{_T("ATTRIBUTE_HIDDEN"),    attrHidden},
		{_T("ATTRIBUTE_READONLY"),  attrReadonly},
		{_T(""),         _T("")},
	};

	LPCTSTR sec = _T("EXPAND_VAR_NAMES");
	for (int num = 0;; num++) {
		ParamsPair& paramPair = paramsPairs[num];
		const tstring& name = paramPair.name;
		const tstring& value = paramPair.value;
		if (name.empty()) {
			break;
		}
		tstring varName = app.GetString(sec, name.c_str(), name.c_str());
		if ( !varName.empty()) {
			expander.addProperty(varName, value);
		}
	}

	// 実行パラメータの展開
	tstring verb;
	tstring appName;
	tstring param;
	tstring appCurrentDir;

	adjustParameters(expander, v_absolutePath, fileInfo, verb, appName, param, appCurrentDir);

	// 実行部
	return internalDoAction(v_absolutePath, fileInfo, verb, appName, param, appCurrentDir);
}

/*!
 * プロセス実行パラメータの調整。
 * アクションに応じて特殊変数・環境変数の展開し、非印字文字列を補正するなどを行う。
 * \param expander 置換変数の展開
 * \param v_absolutePath 絶対パス
 * \param fileInfo ファイル情報
 * \param verb アクションタイプを保存する
 * \param appName アプリケーションを保存する
 * \param param パラメータを保存する。
 * \param appCurrentDir カレントディレクトリを保存する。
 */
void ActionInvoker::adjustParameters(
	PropertyStringExpander expander,
	const tstring &, //v_absoluePath
	const CFileInfo &, //fileInfo
	tstring& verb,
	tstring& appName, 
	tstring& param,
	tstring& appCurrentDir) throw()
{
	verb = EliminateUnprintableChars(settingInfo_.getAction());

	appName = EliminateUnprintableChars(expander.expandString(settingInfo_.getAppName()));
	param = EliminateUnprintableChars(expander.expandString(settingInfo_.getParam()));
	appCurrentDir = EliminateUnprintableChars(expander.expandString(settingInfo_.getAppCurrentDir()));
}


//////////

ActionInvokerFactory::ActionInvokerFactory()
{
}

ActionInvokerFactory::~ActionInvokerFactory() throw()
{
}

ActionInvokerPtr ActionInvokerFactory::create(const CSettingInfo& v_settingInfo) const
{
	tstring verb = v_settingInfo.getAction();

	// オプション部を取り除く
	size_t pos = verb.find(_T(";"));
	if (pos != verb.npos) {
		verb = verb.substr(0, pos);
	}

	// VERBからアクションを判定する。
	if (verb == _T("*PROCESS") || verb == _T("*RUNAS")) {
		return ActionInvokerPtr(new CreateProcessAsLogonActionInvoker(v_settingInfo));
	}
	else if (verb == _T("*IPMSG")) {
		return ActionInvokerPtr(new UDPSendActionInvoker(v_settingInfo));
	}
	else if (verb == _T("*SOUND")) {
		return ActionInvokerPtr(new BeepActionInvoker(v_settingInfo));
	}

	// ShellExecute (デフォルト)
	return ActionInvokerPtr(new ShellExecActionInvoker(v_settingInfo));
}

