/** nengowin.cpp
 * 和暦・西暦年号変換
 *
 * @copyright	(c)studio pahoo
 * @author		パパぱふぅ
 * @動作環境	MinGW C++ + Boost C++ Libraries
 * @参考URL		https://www.pahoo.org/e-soul/webtech/cpp01-19-01.shtm
 */
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <time.h>
#include <sstream>
#include <string>
#include <vector>
#include <locale>
#include <regex>
#include <cmath>
#include <winsock2.h>
#include <windows.h>
#include <shlobj.h>
#include <commctrl.h>
#include <richedit.h>
#include <boost/format.hpp>
#include <boost/program_options.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include "resource.h"
#include "pahooNormalizeText.hpp"

using namespace std;
using namespace boost;
using namespace boost::property_tree;
using namespace boost::program_options;

// 定数など ==================================================================
#define MAKER		"pahoo.org"				// 作成者
#define APPNAME		"nengowin"				// アプリケーション名
#define APPNAMEJP	"和暦・西暦年号変換"	// アプリケーション名（日本語）
#define APPVERSION	"1.2.10"					// バージョン
#define APPYEAR		"2020-26"				// 作成年
#define REFERENCE	"https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-19-01.shtm"	// 参考サイト

// ヘルプ・ファイル
#define HELPFILE	".\\etc\\help.chm"

// デフォルト保存ファイル名
#define SAVEFILE	"nengowin.txt"

// 西暦⇔元号変換ファイル名
#define TABLEERA_FNAME	"etc/era.txt"

// オプション保存ファイル名：変更不可
#define OPTIONFILE	"option.txt"

// 現在のインターフェイス
HINSTANCE hInst;

// アプリケーション・ウィンドウ
HWND hParent;

// 変換プション
string Option;

// 変換オプションの初期値
#define NOPTION_INIT	"C1hH"

// アプリケーション・ウィンドウ位置
unsigned hParent_X, hParent_Y;

// エラー・メッセージ格納用【変更不可】
string ErrorMessage;

// pahooNormalizeTextオブジェクト
pahooNormalizeText* pNT;

// char*バッファサイズ
#define SIZE_BUFF	512

// 変換元テキスト（初期値）
#define DEF_SOUR	"慶応四年一月三日、鳥羽・伏見の戦いが起きる。10月23日、明治天皇が即位。改元は1868年1月25日に遡って適用された。明治2年5月17日、五稜郭が開放され、戊辰戦争は終わる。"

// バージョン情報
string Version = (boost::format(R"(
%1%  バージョン %2%

本アプリケーションはMIT Licenseです．
商用を含む無償利用が可能です．自由に改造できます．
再配布の際は，下記の著作権表記，およびURLと本使用条件を必ず明記してください．

　Copyright by (c)studio pahoo, %3%
　https://www.pahoo.org/

なお，本アプリケーションの利用または改造することによって生じた得失ついては一切関知いたしません．また，二次利用先の組織・企業・団体の目的・内容・活動については一切関知いたしません．

本アプリケーションは，下記のライブラリ，フレームワーク，APIを利用しています．
　MinGW（g++）／Boost C++ライブラリ
)")
% APPNAMEJP
% APPVERSION
% APPYEAR).str();

// CUIヘルプ情報
string Help = (boost::format(R"(
%1%  バージョン %2%

nengo.exe -オプション
        -s, --sour   入力テキストファイル
        -c, --clip   クリップボードから入力
        -d, --dest   出力テキストファイル
        -p, --paste  クリップボードへ出力
        -o, --option 変換条件（複数並べて指定可能）：GUI版と同じ作用
                A　西暦に統一
                W　和暦に統一
                C　西暦(和暦)
                1　奈良時代以降
                2　新暦以降
                0　変換しない
                h　数字変換(西暦)：半角数字
                z　　　同上　　　：全角数字
                k　　　同上　　　：漢数字(単純)
                l　　　同上　　　：漢数字
                H　数字変換(和暦)：半角数字
                Z　　　同上　　　：全角数字
                K　　　同上　　　：漢数字(単純)
                L　　　同上　　　：漢数字
                ※条件はPCに保存され，次回起動時には省略可能です．
        -v, --version バージョン情報
        -h, --help    オンラインヘルプ
)")
% APPNAMEJP
% APPVERSION).str();

// 変換オプション読込／保存 =================================================
/**
 * AppDataのパスを取得
 * @param	char* appname アプリケーション名
 * @return	string パス
 */
string getMyPath(const char* appname) {
	static TCHAR myPath[MAX_PATH] = "";

	if (strlen(myPath) == 0) {
		if (SHGetSpecialFolderPath(NULL, myPath, CSIDL_APPDATA, 0)) {
			TCHAR *ptmp = _tcsrchr(myPath, _T('\\'));
			if (ptmp != NULL) {
				ptmp = _tcsinc(ptmp);
				*ptmp = _T('\0');
			}
			strcat(myPath, _T("Roaming"));
			CreateDirectory((LPCTSTR)myPath, NULL);
			strcat(myPath, _T("\\pahoo.org"));
			CreateDirectory((LPCTSTR)myPath, NULL);
			strcat(myPath, _T("\\"));
			strcat(myPath, _T(appname));
			CreateDirectory((LPCTSTR)myPath, NULL);
			strcat(myPath, _T("\\"));
		} else {
		}
	}
	return (string)myPath;
}

/**
 * パラメータの初期化
 * @param	なし
 * @return	なし
 */
void initParameter(void) {
	Option = NOPTION_INIT;
	hParent_X = 0;
	hParent_Y = 0;
}

/**
 * パラメータの読み込み
 * @param	なし
 * @return	なし
 */
void loadParameter(void) {
	ptree pt;

	// 初期値設定
	initParameter();

	// XMLファイル読み込み
	try {
		xml_parser::read_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt);

		// XML解釈
		try {
			// 形式チェック
			if (boost::optional<string>str = pt.get_optional<string>("parameter")) {
			} else {
				return;
			}
			// パラメータ読み込み
			for (auto it : pt.get_child("parameter")) {
				string type= it.second.get_optional<string>("<xmlattr>.type").value();
				if (type == "option") {
					Option = (string)it.second.data();
				} else if (type == "wx") {
					hParent_X = (unsigned)stoi(it.second.data());
				} else if (type == "wy") {
					hParent_Y = (unsigned)stoi(it.second.data());
				}
			}
		// 解釈失敗したら初期値設定
		} catch (xml_parser_error& e) {
			initParameter();
			return;
		}
	// 読み込み失敗したら初期値設定
	} catch (xml_parser_error& e) {
		initParameter();
		return;
	}

	// アプリケーション・ウィンドウの位置（デスクトップ範囲外なら原点移動）
	HWND hDesktop = GetDesktopWindow();
	WINDOWINFO windowInfo;
	windowInfo.cbSize = sizeof(WINDOWINFO);
	GetWindowInfo(hDesktop, &windowInfo);
	if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
		hParent_X = 0;
	}
	if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
		hParent_Y = 0;
	}
}

/**
 * パラメータの保存
 * @param	なし
 * @return	なし
 */
void saveParameter(void) {
#ifndef CMDAPP
	// アプリケーション・ウィンドウの位置取得
	WINDOWINFO windowInfo;
	windowInfo.cbSize = sizeof(WINDOWINFO);
	GetWindowInfo(hParent, &windowInfo);
	hParent_X = (unsigned)windowInfo.rcWindow.left;
	hParent_Y = (unsigned)windowInfo.rcWindow.top;
	if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
		hParent_X = 0;
	}
	if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
		hParent_Y = 0;
	}
#endif

	// XMLファイルへ書き込む
	ptree pt;
	ptree& child1 = pt.add("parameter.param", Option);
	child1.add("<xmlattr>.type", "option");
	ptree& child2 = pt.add("parameter.param", hParent_X);
	child2.add("<xmlattr>.type", "wx");
	ptree& child3 = pt.add("parameter.param", hParent_Y);
	child3.add("<xmlattr>.type", "wy");

	const int indent = 4;
	write_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt, std::locale(),
		xml_writer_make_settings<std::string>(' ', indent));
}

/**
 * パラメータの削除
 * @param	なし
 * @return	なし
 */
void delParameter(void) {
	remove((getMyPath(APPNAME) + APPNAME + ".xml").c_str());

	// 初期値設定
	initParameter();
}

// クリップボード関連 ======================================================
/**
 * クリップボードに文字列をセット
 * @param	string str 文字列
 * @return	なし
 */
void setClipboardData(string str) {
	char buff[str.size() + 1];
	char_traits<char>::copy(buff, str.c_str(), str.size() + 1);

	OpenClipboard(NULL);
	EmptyClipboard();

	HGLOBAL hText = GlobalAlloc(GHND | GMEM_SHARE, str.size() + 1);
	PTSTR pText = (PTSTR) GlobalLock(hText);
	lstrcpy(pText, buff);
	GlobalUnlock(hText);
	SetClipboardData(CF_TEXT, hText);
	CloseClipboard();
}

/**
 * クリップボードから文字列を取得
 * @param	なし
 * @return	文字列／NULL＝取得失敗
 */
char* getClipboardData(void) {
	static char buff[SIZE_BUFF + 1];

	OpenClipboard(NULL);
	HGLOBAL hText = GetClipboardData(CF_TEXT);

	if (hText == NULL) {
		return NULL;
	} else {
		PTSTR pText = (PTSTR) GlobalLock(hText);
		lstrcpy(buff, pText);
		GlobalUnlock(hText);
	}
	CloseClipboard();

	return buff;
}

#ifndef CMDAPP
// ファイル読込処理 ========================================================
/**
 * テキストファイルを読み込む
 * @param	const char* fname  読み込むファイル名
 * @return	wstring 読み込んだテキスト
*/
wstring __loadTXT(const char* fname) {
	ifstream ifs(fname);
	if (! ifs) {
		ErrorMessage = (string)fname + " の読み込みに失敗しました";
	}
	string ss;
	wstring wstr = L"";
	while (getline(ifs, ss)) {
		wstr += _SW(ss) + L"\n";
	}

	return wstr;
}

/**
 * テキストファイルを読み込む（ダイアログ）
 * @param	なし
 * @return	wstring 読み込んだテキスト
*/
wstring loadTXT(void) {
	static char fname[MAX_PATH + 1];
	strcpy(fname, SAVEFILE);
	OPENFILENAME of;

	// OPENFILENAME構造体のサイズをセット
	memset(&of, 0, sizeof(OPENFILENAME));
	of.lStructSize = sizeof(OPENFILENAME);
	// ダイアログボックスを所有するウィンドウへのハンドル
	of.hwndOwner = hParent;
	of.lpstrFilter = TEXT("テキストファイル(*.txt)\0*.txt\0すべてのファイル(*.*)\0*.*\0\0");
	// ファイル名を格納したバッファのアドレス
	of.lpstrFile = (LPTSTR)fname; 
	// lpstrFileメンバで指定されるバッファのサイズ
	of.nMaxFile = MAX_PATH;
	of.Flags = OFN_OVERWRITEPROMPT;
	// デフォルトの拡張子を格納したバッファのアドレス
	of.lpstrDefExt = TEXT("txt");
	// コモンダイアログの表示
	GetOpenFileName(&of);
	// ファイル保存
	return __loadTXT(fname);
}

// ファイル保存処理 ========================================================
/**
 * 結果をTXTファイルに保存する
 * @param	const char* fname  保存ファイル名
 * @param	const wstring wstr 保存するテキスト
 * @return	なし
*/
void __saveTXT(const char* fname, const wstring wstr) {
	ofstream outputfile(fname);

	if (wstr != L"") {
		outputfile << _WS(wstr) << endl;
	}

	if (outputfile.bad()) {
		ErrorMessage = "テキストファイル書き込みエラー";
	}
	outputfile.close();
}

/**
 * 結果をTXTファイルに保存する（ダイアログ）
 * @param	const wstring wstr 保存するテキスト
 * @return	なし
*/
void saveTXT(const wstring wstr) {
	static char fname[MAX_PATH + 1];
	strcpy(fname, SAVEFILE);
	OPENFILENAME of;

	// OPENFILENAME構造体のサイズをセット
	memset(&of, 0, sizeof(OPENFILENAME));
	of.lStructSize = sizeof(OPENFILENAME);
	// ダイアログボックスを所有するウィンドウへのハンドル
	of.hwndOwner = hParent;
	of.lpstrFilter = TEXT("テキストファイル(*.txt)\0*.txt\0すべてのファイル(*.*)\0*.*\0\0");
	// ファイル名を格納したバッファのアドレス
	of.lpstrFile = (LPTSTR)fname; 
	// lpstrFileメンバで指定されるバッファのサイズ
	of.nMaxFile = MAX_PATH;
	of.Flags = OFN_OVERWRITEPROMPT;
	// デフォルトの拡張子を格納したバッファのアドレス
	of.lpstrDefExt = TEXT("txt");
	// コモンダイアログの表示
	GetSaveFileName(&of);
	// ファイル保存
	__saveTXT(fname, wstr);
}

// GUI処理 =================================================================
/**
 * テキストボックスに文字列を代入
 * @param  HWND		hDlg  ダイアログID
 * @param  unsigned	id    テキストボックスID
 * @param  string	s     設定値
 * @return なし
 */
void setStrEditBox(HWND hDlg, unsigned id, string s) {
	char buff[s.length() + 1];
	char_traits<char>::copy(buff, s.c_str(), s.size() + 1);
	SetDlgItemText(hDlg, id, buff);
}

/**
 * テキストボックスから文字列を取得
 * @param  HWND		hDlg  ダイアログID
 * @param  unsigned	id    テキストボックスID
 * @return string 取得文字列
 */
string getStrEditBox(HWND hDlg, unsigned id) {
	char buff[SIZE_BUFF * 10 + 1];
	GetDlgItemText(hDlg, id, buff, SIZE_BUFF * 10);
	return (string) buff;
}

/**
 * ダイアログを親ウィンドウの中央に配置
 * @param	HWND hDlg		自分のハンドラ
 * @global	HWND hParent	親ウィンドウのハンドラ
 * @return	なし
 */
void CenterWindow(HWND hDlg) {
	int x, y;
	RECT rc;

	GetWindowRect(hParent, &rc);
	x = rc.left + (rc.right - rc.left) / 2;
	y = rc.top + (rc.bottom - rc.top) / 2;

	SetWindowPos(hDlg, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}

// 変換オプション取得・設定 ==================================================
/**
 * 変換オプションを設定する
 * @param	HWND hDlg	ウィンドウ・ハンドラ
 * @param	string opt	変換オプション
 * @return	なし
 */
void setOption(HWND hDlg, string opt) {
	for (int i = 0; i < (int)opt.length(); i++) {
		char* ch = (char*)opt.substr(i, 1).c_str();
		switch (*ch) {
		// 変換方式
		case OPTION_MODE_AD:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_AD),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ERA),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ADERA),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_MODE_ERA:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_AD),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ERA),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ADERA),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_MODE_ADERA:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_AD),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ERA),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ADERA),
							BM_SETCHECK, BST_CHECKED,   0);
			break;
		// 変換範囲
		case OPTION_SCOPE_1:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_1),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_2),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_0),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_SCOPE_2:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_1),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_2),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_0),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_SCOPE_0:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_1),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_2),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_0),
							BM_SETCHECK, BST_CHECKED,   0);
			break;
		// 数字変換（西暦）
		case OPTION_AD_HAN:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_AD_ZEN:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_AD_KAN1:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_AD_KAN2:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2),
							BM_SETCHECK, BST_CHECKED,   0);
			break;
		// 数字変換（和暦）
		case OPTION_ERA_HAN:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_ERA_ZEN:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_ERA_KAN1:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1),
							BM_SETCHECK, BST_CHECKED,   0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2),
							BM_SETCHECK, BST_UNCHECKED, 0);
			break;
		case OPTION_ERA_KAN2:
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1),
							BM_SETCHECK, BST_UNCHECKED, 0);
			SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2),
							BM_SETCHECK, BST_CHECKED,   0);
			break;
		default:
			break;
		}
	}
}

/**
 * 変換オプションを取得する
 * @param	HWND hDlg	ウィンドウ・ハンドラ
 * @return	string 変換オプション
 */
string getOption(HWND hDlg) {
	string opt = "";
	// 変換方式
	if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_AD), BM_GETCHECK, 0, 0)) {
		opt += OPTION_MODE_AD;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ERA), BM_GETCHECK, 0, 0)) {
		opt += OPTION_MODE_ERA;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ADERA), BM_GETCHECK, 0, 0)) {
		opt += OPTION_MODE_ADERA;
	}
	// 変換範囲
	if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_1), BM_GETCHECK, 0, 0)) {
		opt += OPTION_SCOPE_1;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_2), BM_GETCHECK, 0, 0)) {
		opt += OPTION_SCOPE_2;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_0), BM_GETCHECK, 0, 0)) {
		opt += OPTION_SCOPE_0;
	}
	// 数字変換（西暦）
	if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN), BM_GETCHECK, 0, 0)) {
		opt += OPTION_AD_HAN;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN), BM_GETCHECK, 0, 0)) {
		opt += OPTION_AD_ZEN;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1), BM_GETCHECK, 0, 0)) {
		opt += OPTION_AD_KAN1;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2), BM_GETCHECK, 0, 0)) {
		opt += OPTION_AD_KAN2;
	}
	// 数字変換（和暦）
	if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN), BM_GETCHECK, 0, 0)) {
		opt += OPTION_ERA_HAN;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN), BM_GETCHECK, 0, 0)) {
		opt += OPTION_ERA_ZEN;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1), BM_GETCHECK, 0, 0)) {
		opt += OPTION_ERA_KAN1;
	} else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2), BM_GETCHECK, 0, 0)) {
		opt += OPTION_ERA_KAN2;
	}

	return opt;
}

// バージョン表示ダイアログ =================================================
/**
 * イベントハンドラ：バージョン表示ダイアログ
 * @param	HWND hDlg			ウィンドウ・ハンドラ
 * @paramm	UINT uMsg			メッセージ識別子
 * @param	WPARAM wParam		メッセージの最初のパラメータ
 * @paramL	PARAM lParam		メッセージの2番目のパラメータ
 * @return	INT_PTR CALLBACK	TRUE：メッセージ処理完了／FALSE：未完了
 */
INT_PTR CALLBACK processHelp(HWND hDlg, UINT uMsg,
					WPARAM wParam, LPARAM lParam) {
	switch (uMsg) {
	// ダイアログ初期化
	case WM_INITDIALOG:
		CenterWindow(hDlg);
		setStrEditBox(hDlg, IDC_TEXT_HELP, Version);
		break;

		// ボタン押下
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		// 実行
		case IDC_BUTTON_OK:
			EndDialog(hDlg, 0);
			break;
		default:
			return 1;
		}
		break;
		// プログラム終了
	case WM_CLOSE:
		EndDialog(hDlg, 0);
		break;
	}
	return 0;
}

/**
 * ヘルプ・ダイアログを作成
 * @param  HWND hDlg		親ウィンドウ・ハンドラ
 * @param  DLGPROC handler	イベント・ハンドラ
 * @return なし
 */
void createHelp(HWND hDlg, DLGPROC handler) {
	DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG_VER), hDlg, (DLGPROC) handler);
}

// メインウィンドウ ==========================================================
/**
 * イベントハンドラ：メインウィンドウ
 * @param	HWND hDlg			親ウィンドウ・ハンドラ
 * @paramm	UINT uMsg			メッセージ識別子
 * @param	WPARAM wParam		メッセージの最初のパラメータ
 * @paramL	PARAM lParam		メッセージの2番目のパラメータ
 * @return	INT_PTR CALLBACK	TRUE：メッセージ処理完了／FALSE：未完了
 */
INT_PTR CALLBACK processMain(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	HICON hIcon;
	static wstring wsour, wdest;

	switch (uMsg) {
	// ダイアログ初期化
	case WM_INITDIALOG:
		hParent = hDlg;
		hIcon = (HICON) LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON,
				16, 16, 0);
		SendMessage(hParent, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);
		ErrorMessage = "";
		// パラメータ読み込み
		loadParameter();
		setStrEditBox(hDlg, IDC_EDIT_SOUR, DEF_SOUR);
		// アプリケーション・ウィンドウ移動
		SetWindowPos(hParent, NULL, hParent_X, hParent_Y, 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER));
		hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
		SendMessage(hParent, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
		setOption(hDlg, Option);
		// 西暦⇔元号変換表読み込み
		pNT->readTableEra(TABLEERA_FNAME);
		break;

	// ボタン押下
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		// 変換
		case IDC_BUTTON_EXEC:
		case IDM_EXEC:
			// 変換オプション取得
			Option = getOption(hDlg);
			// カーソルを砂時計に
			SetCursor(LoadCursor(NULL, IDC_WAIT));
			// 正規化実行
			wsour = _SW(getStrEditBox(hDlg, IDC_EDIT_SOUR));
			wdest = pNT->convNengo(wsour, Option.c_str());
			setStrEditBox(hDlg, IDC_EDIT_DEST, _WS(wdest));
			break;
		// 開く
		case IDM_OPEN:
			wsour = loadTXT();
			setStrEditBox(hDlg, IDC_EDIT_SOUR, _WS(wsour));
			break;
		// リセット
		case IDC_BUTTON_RESET:
		case IDM_RESET:
			setStrEditBox(hDlg, IDC_EDIT_SOUR, "");
			break;
		// 保存
		case IDC_BUTTON_SAVE:
		case IDM_SAVE:
			saveTXT(wdest);
			break;
		// 設定クリア＋アプリ終了
		case IDM_CLEAR_PARAMETER:
			delParameter();
			EndDialog(hParent, 0);
			return 0;
			break;
		// ヘルプ
		case IDM_HELP:
			ShellExecute(hParent, _T("open"), _T(HELPFILE), NULL, NULL, SW_RESTORE);
			break;
		// バージョン表示
		case IDM_VERSION:
			createHelp(hParent, processHelp);
			break;
		// 解説サイト
		case IDM_PAHOO:
			ShellExecute(NULL, _T("open"), _T(REFERENCE), NULL, NULL, SW_RESTORE);
			break;
		// コピー
		case IDM_COPY:
		case IDC_BUTTON_COPY:
			setClipboardData(getStrEditBox(hDlg, IDC_EDIT_DEST));
			break;
		// 切り取り
		case IDM_DELETE:
			setStrEditBox(hParent, IDC_EDIT_SOUR, "");
			setStrEditBox(hParent, IDC_EDIT_DEST, "");
			break;
		// プログラム終了
		case IDM_QUIT:
			// パラメータ保存
			saveParameter();
			EndDialog(hParent, 0);
			return 0;
		default:
			return 0;
		}
		break;

	// プログラム終了
	case WM_CLOSE:
		// パラメータ保存
		saveParameter();
		EndDialog(hParent, 0);
		return 0;
	}
	return 0;
}

// GUIメインプログラム ======================================================
/**
 * GUI用メインプログラム
 * @param	HINSTANCE hInstance			インスタンスハンドル
 * @paramm	HINSTANCE hPrevInstance		未使用(常にNULL)：Win16時代の名残
 * @param	LPSTR lpCmdLine				コマンドライン引数
 * @paramL	int nShowCmd				ウィンドウの表示方法
 * @return	int リターンコード
 */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
	LoadLibrary("RICHED20.DLL");
	hInst = hInstance;

	// pahooNormalizeTextオブジェクト
	pNT = new pahooNormalizeText();

	DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)processMain);

	// オブジェクト解放
	delete pNT;

	return 0;
}

// CUIメイン・プログラム ====================================================
#else
/**
 * CUI用メインプログラム
 * @param	int argc
 * @paramm	char* argv[]
 * @return	int リターンコード
 */
int main(int argc, char* argv[]) {
	// パラメータ読み込み
	loadParameter();
	string sour = "";

	// pahooNormalizeTextオブジェクト
	pNT = new pahooNormalizeText();
	// 西暦⇔元号変換表読み込み
	pNT->readTableEra(TABLEERA_FNAME);

	// コマンドライン・オプションの定義
	options_description options("コマンドライン・オプション");
	options.add_options()
		("option,o", value<std::string>()->default_value(Option), "変換オプション")
		("sour,s", value<std::string>(), "入力テキスト")
		("clip,c", "クリップボードから入力")
		("dest,d", value<std::string>(), "出力テキスト")
		("paste,p", "クリップボードへ出力")
		("help,h", "ヘルプ")
		("version,v", "バージョン情報")
		;
	// コマンドライン・オプションの取得
	variables_map vm;
	try {
		store(parse_command_line(argc, argv, options), vm);
	} catch(const boost::program_options::error_with_option_name& e) {
		ErrorMessage =  e.what();
		cerr << ErrorMessage << endl;
		return 1;
	}
	notify(vm);

	// 変換オプション
	auto opt = vm["option"].as<string>();

	wstring wdest = L"";
	// ヘルプ情報
	if (vm.count("help")) {
		wdest = _SW(Help);
	// バージョン情報
	} else if (vm.count("version")) {
		wdest = _SW(Version);

	// 変換実行
	} else {
		// 入力ファイル
		wstring wsour = _SW(DEF_SOUR);
		if (vm.count("sour")) {
			auto infile = vm["sour"].as<string>();
			ifstream ifs(infile.c_str());
			if (ifs.fail()) {
				ErrorMessage = infile + " が見つかりません";
				cerr << ErrorMessage << endl;
				return 1;
			}
			string sour = "";
			string ss;
			while (getline(ifs, ss)) {
				sour += ss + "\r";
			}
			wsour = _SW(sour);
		// クリップボードから
		} else if (vm.count("clip")) {
			char *ss = getClipboardData();
			sour = ss;
			wsour = _SW(sour);
		// 標準入力から
		} else {
			string sour;
			cin >> sour;
			if (sour.length() > 0) {
				wsour = _SW(sour);
			}
		}
		wdest = pNT->convNengo(wsour, opt.c_str());
	}

	// 出力ファイル
	if (vm.count("dest")) {
		// 改行コード置換
		wregex re(_SW("\r"));
		wdest = regex_replace(wdest, re, _SW("\n"));
		auto outfile = vm["dest"].as<string>();
		ofstream ofs(outfile.c_str());
		ofs << _WS(wdest);
		if (ofs.bad()) {
			ErrorMessage = outfile + " への書き込みに失敗しました";
			cerr << ErrorMessage << endl;
			return 1;
		}
		ofs.close();
	// クリップボードへ
	} else if (vm.count("paste")) {
		// 改行コード置換
		wregex re(_SW("\r"));
		wdest = regex_replace(wdest, re, _SW("\n"));
		setClipboardData(_WS(wdest));
	// 標準出力へ
	} else {
		// 改行コード置換
		wregex re(_SW("\r"));
		wdest = regex_replace(wdest, re, _SW("\n"));
		cout << _WS(wdest);
	}

	// オブジェクト解放
	delete pNT;

	// パラメータ保存
	Option = opt;
	saveParameter();

	return 0;
}

#endif

/*
 ** バージョンアップ履歴 =====================================================
 *
 * @version 1.2.10 2026/03/15 使用ライブラリ更新, optional名前衝突回避
 * @version 1.2.9  2025/11/23 使用ライブラリ更新
 * @version 1.2.8  2025/08/02 使用ライブラリ更新
 * @version 1.2.7  2025/03/29 使用ライブラリ更新
 * @version 1.2.5  2024/08/31 使用ライブラリ更新
 * @version 1.2.4  2024/04/20 使用ライブラリ更新
 * @version 1.2.3  2023/12/17 使用ライブラリ更新
 * @version 1.2.2  2023/07/23 使用ライブラリ更新
 * @version 1.2.1  2023/04/08 使用ライブラリ更新
 * @version 1.2.0  2022/10/15 ウィンドウ位置を保存，設定クリア機能を追加
 * @version 1.1.0  2022/10/12 CUI版に --clip, --paste オプション追加
 * @version 1.0    2020/11/14 初版
*/
