/** earthquakewin.cpp
 * 最近の地震情報を表示
 *
 * @copyright	(c)studio pahoo
 * @author		パパぱふぅ
 * @開発環境	MinGW C++ + cURL + OpenSSL + Boost C++ Libraries
 *					+ WebView2Loader.dll + Leaflet + OSM (+ GoogleMap)
 * @参考URL		https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-12-01.shtm
*/
// 初期化処理 ======================================================
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <time.h>
#include <sstream>
#include <string>
#include <locale>
#include <regex>
#include <winsock2.h>
#include <windows.h>
#include <shlobj.h>
#include <commctrl.h>
#include <objbase.h>
#include <wrl.h>
#include <richedit.h>

#include <curl/curl.h>
#include <boost/format.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/algorithm/string.hpp>
#include "mystrings.h"
#include "apikey.h"
#include "WebView2.h"
#include "pahooWebView2.hpp"
#include "pahooCache.hpp"
#include "pahooGeocode.hpp"
#include "resource.h"

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

// 定数など ==================================================================
#define MAKER		"pahoo.org"				// 作成者
#define APPNAME		"earthquakewin"			// アプリケーション名
#define APPNAMEJP	"最近の地震情報"		// アプリケーション名（日本語）
#define APPVERSION	"4.2.4"					// バージョン
#define APPYEAR		"2020-26"				// 作成年
#define REFERENCE	"https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-12-01.shtm"	// 参考サイト

// ListViewItemの最大文字長【変更不可】
#define MAX_LISTVIEWITEM	259

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

// デフォルト保存ファイル名
#define SAVEFILE	"eq_%04d%02d%02d_%02d%02d.csv"

// キャッシュ・ディレクトリ
#define DIR_CACHE_FEED		"pcache1\\"
#define DIR_CACHE_FEED_L	"pcache2\\"
#define DIR_CACHE_DATA		"pcache3\\"

// キャッシュ保持時間（デフォルト；分）(0：キャッシュしない)
#define LIFE_CACHE_FEED		5		// 高頻度フィードに対して
#define LIFE_CACHE_FEED_L	120		// 長期フィードに対して
#define LIFE_CACHE_DATA		720		// 地震情報に対して

// 気象庁防災情報XML：高頻度フィード - 地震火山【変更不可】
#define FEED_EVOL	"https://www.data.jma.go.jp/developer/xml/feed/eqvol.xml"

// 気象庁防災情報XML：長期フィード - 地震火山【変更不可】
#define FEED_EVOL_L	"https://www.data.jma.go.jp/developer/xml/feed/eqvol_l.xml"

// マップID
#define MAP_ID			"map_id"
// 地図の大きさ
#define MAP_WIDTH		600		// 地図の幅（ピクセル）
#define MAP_HEIGHT		400		// 地図の高さ（ピクセル）
// 経度・緯度（初期値）
#define DEF_LONGITUDE	139.766667
double Longitude = DEF_LONGITUDE;
#define DEF_LATITUDE	35.681111
double Latitude  = DEF_LATITUDE;
// 地図拡大率（初期値）
#define DEF_ZOOM		6
int Zoom = DEF_ZOOM;
// 地図の種類（初期値）
#define DEF_MAPTYPE		"GSISTD"
string Maptype = DEF_MAPTYPE;
#define INFO_WIDTH	(int)(MAP_WIDTH * 0.75)	// 情報ウィンドウの最大幅
#define INFO_OFFSET_X	0				// 情報ウィンドウのオフセット位置（X)
#define INFO_OFFSET_Y	-10				// 情報ウィンドウのオフセット位置（Y)

// 地震情報の最大格納数
#define MAX_EARTHQUAKE	100

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

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

// WebView2ウィンドウ
HWND hWebView2;

// プログラム終了フラグ
bool flagFinishProgram = false;

// ICoreWebView2：pahooWebView2.cpp で定義する．
extern ICoreWebView2* webView2;

// テンポラリファイル名
char tmpFname[MAX_PATH + 1];
wchar_t wUri[MAX_PATH * 2 + 1];

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

// エラー・メッセージ格納用
string ErrorMessage;

// UserAgent
string UserAgent;

// pahooGeocodeオブジェクト
pahooGeocode* pGC;

// WebView2ハンドラの動作を指定する．【変更不可】
enum class eAction {
	GetLatLong,				// 緯度・経度取得してブラウザ表示
	GetZoomType,			// 拡大率・マップタイプ取得してブラウザ表示
	GetLatLongZoomType,		// 緯度・経度・拡大率・マップタイプ取得してブラウザ表示
	Finish					// プログラム終了
};
eAction selectAction = eAction::GetLatLongZoomType;

// 地震情報
wstring infoEarthquake;

// 地震情報を格納する構造体
struct _Earthquake {
	int id				= 0;			// マッピングID
	int year			= 0;			// 西暦年
	int month 			= 0;			// 月
	int day				= 0;			// 日
	int hour			= 0;			// 時
	int minuite			= 0;			// 分
	wstring location	= L"";			// 震源地
	double latitude		= 0.0;			// 緯度
	double longitude	= 0.0;			// 経度
	double depth		= 0.0;			// 深さ
	double magnitude	= -1.0;			// マグニチュード
	int maxintensity	= -1;			// 最大震度
} Earthquake[MAX_EARTHQUAKE];

// サブ・プログラム =====================================================
/**
 * パラメータの初期化
 * @param	なし
 * @return	なし
 */
void initParameter(void) {
	Longitude = DEF_LONGITUDE;
	Latitude  = DEF_LATITUDE;
	Zoom      = DEF_ZOOM;
	Maptype   = DEF_MAPTYPE;
	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 (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 == "latitude") {
					Latitude = stod(it.second.data());
				} else if (type == "longitude") {
					Longitude = stod(it.second.data());
				} else if (type == "zoom") {
					Zoom = stoi(it.second.data());
				} else if (type == "maptype") {
					Maptype = (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

	char lng[SIZE_BUFF + 1], lat[SIZE_BUFF + 1];
	snprintf(lng, SIZE_BUFF, "%.5f", Longitude);
	snprintf(lat, SIZE_BUFF, "%.5f", Latitude);

	// XMLファイルへ書き込む
	ptree pt;
	ptree& child1 = pt.add("parameter.param", lat);
	child1.add("<xmlattr>.type", "latitude");
	ptree& child2 = pt.add("parameter.param", lng);
	child2.add("<xmlattr>.type", "longitude");
	ptree& child3 = pt.add("parameter.param", to_string(Zoom));
	child3.add("<xmlattr>.type", "zoom");
	ptree& child4 = pt.add("parameter.param", Maptype);
	child4.add("<xmlattr>.type", "maptype");
	ptree& child5 = pt.add("parameter.param", (string)to_string(hParent_X));
	child5.add("<xmlattr>.type", "wx");
	ptree& child6 = pt.add("parameter.param", (string)to_string(hParent_Y));
	child6.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	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) {
	string help;

	switch(uMsg){
		// ダイアログ初期化
		case WM_INITDIALOG:
			CenterWindow(hDlg);
			// メッセージ作成
			help = (boost::format(R"(
%1%  バージョン %2%

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

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

なお，本アプリケーションの利用または改造することによって生じた得失ついては一切関知いたしません．気象庁防災情報XMLの変更・停止によって正常に機能しなくなる場合があります．また，二次利用先の組織・企業・団体の目的・内容・活動については一切関知いたしません．

本アプリケーションは，下記のライブラリ，フレームワーク，インターネット上のリソースを利用しています．
　MinGW（g++）／Boost C++ライブラリ／libcurl／OpenSSL+／
　WebView2Loader.dll／　Leaflet／OpenStreetMap／Googleマップ／
　気象庁防災情報XML
)")
% APPNAME
% APPVERSION
% APPYEAR
).str();
			setStrEditBox(hDlg, IDC_TEXT_HELP, help);
			break;

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

// 情報取得（インターネット） ===============================================
/**
 * 気象庁防災情報XMLから最新の震源・震度に関する情報URLを取得
 * @param	string* urls URL格納配列
 * @return	bool TRUE：取得成功／FALSE：取得失敗
*/
bool jma_getLastEarthquakeURLs(string* urls) {
	// URLパターン
	regex re01("https?\\:\\/\\/www\\.data\\.jma\\.go\\.jp\\/developer\\/xml\\/data\\/([0-9\\_]+)VXSE53\\_[0-9]+\\.xml");
	smatch mt1;
	int cnt = 0;
	bool flag;
	static string contents = "";
	pahooCache* pCC;	// pahooCacheオブジェクト

	// 高頻度フィードの解析
	pCC = new pahooCache(LIFE_CACHE_FEED, getMyPath(APPNAME) + DIR_CACHE_FEED, UserAgent);
	if (pCC->load(FEED_EVOL, &contents) == FALSE) {
		ErrorMessage = _WS(pCC->getError());
		return FALSE;
	}
	// XML読み込み
	try {
		std::stringstream ss;
		ss << contents;
		ptree pt;
		xml_parser::read_xml(ss, pt);

		// XML解釈
		wstring co = L"";
		try {
			for (auto it : pt.get_child("feed")) {
				// URL
				if (optional<string>url = it.second.get_optional<string>("link.<xmlattr>.href")) {
					flag = TRUE;
					if (regex_search(url.value(), mt1, re01)) {
						// 重複URLでなければ登録
						flag = TRUE;
						for (int i = 0; i < cnt; i++) {
							if (urls[i] == mt1[0].str()) {
								flag = FALSE;
								break;
							}
						}
						if (flag) {
							urls[cnt] = mt1[0].str();
//							cout << urls[cnt] << endl;
							cnt++;
						}
						if (cnt >= MAX_EARTHQUAKE) {
							break;
						}
					}
				}
			}
		// XML解釈エラー
		} catch(ptree_bad_path& e) {
			ErrorMessage = "気象庁防災情報XMLにアクセスできません";
		return FALSE;
		}
	// 読み込みエラー
	} catch(xml_parser_error& e) {
		ErrorMessage = "気象庁防災情報XMLにアクセスできません";
		return FALSE;
	}
	contents.clear();
	delete pCC;

	// 長期フィードの解析
	pCC = new pahooCache(LIFE_CACHE_FEED, getMyPath(APPNAME) + DIR_CACHE_FEED_L, UserAgent);
	int httpStatus = 0;
	readWebContents(FEED_EVOL_L, UserAgent, &contents, &httpStatus);
	if (pCC->load(FEED_EVOL_L, &contents) == FALSE) {
		ErrorMessage = _WS(pCC->getError());
		return FALSE;
	}
	// XML読み込み
	try {
		std::stringstream ss;
		ss << contents;
		ptree pt;
		xml_parser::read_xml(ss, pt);

		// XML解釈
		wstring co = L"";
		try {
			for (auto it : pt.get_child("feed")) {
				// URL
				if (optional<string>url = it.second.get_optional<string>("link.<xmlattr>.href")) {
					flag = TRUE;
					if (regex_search(url.value(), mt1, re01)) {
						// 重複URLでなければ登録
						flag = TRUE;
						for (int i = 0; i < cnt; i++) {
							if (urls[i] == mt1[0].str()) {
								flag = FALSE;
								break;
							}
						}
						if (flag) {
							urls[cnt] = mt1[0].str();
//							cout << urls[cnt] << endl;
							cnt++;
						}
						if (cnt >= MAX_EARTHQUAKE) {
							break;
						}
					}
				}
			}
		// XML解釈エラー
		} catch(ptree_bad_path& e) {
			ErrorMessage = "気象庁防災情報XMLにアクセスできません";
		return FALSE;
		}
	// 読み込みエラー
	} catch(xml_parser_error& e) {
		ErrorMessage = "気象庁防災情報XMLにアクセスできません";
		return FALSE;
	}
	contents.clear();
	delete pCC;

	if (cnt == 0) {
		ErrorMessage = "最近の地震情報はありません";
		return FALSE;
	}

	// URLを日時の新しい順にソート
	sort(urls, urls + MAX_EARTHQUAKE, greater<>());

	return TRUE;
}

/**
 * 地震情報取得（気象庁防災情報XMLから）
 * @param	なし
 * @return	bool TRUE：取得成功／FALSE：失敗
*/
bool getEarthquake(void) {
	// 年月日時分
	regex re1("([0-9]+)\\-([0-9]+)\\-([0-9]+)T([0-9]+)\\:([0-9]+)");
	// 緯度・経度・深さ
	regex re2("([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\-\\+\\/])([0-9]*)");
	smatch mt1, mt2;
	double lng, lat;
	pahooCache* pCC;	// pahooCacheオブジェクト

	// 地震情報URLを取得
	static string urls[MAX_EARTHQUAKE];
	bool res = jma_getLastEarthquakeURLs(urls);
	if (res == FALSE) {
		return FALSE;
	}

	static string contents = "";
	pCC = new pahooCache(LIFE_CACHE_DATA, getMyPath(APPNAME) + DIR_CACHE_DATA, UserAgent);
	for (int i = 0; i < MAX_EARTHQUAKE; i++) {
		if (urls[i] == "") {
//			cout << "count = " << i << endl;
			break;
		}
		int httpStatus = 0;
		readWebContents(urls[i], UserAgent, &contents, &httpStatus);
		if (pCC->load(urls[i], &contents) == FALSE) {
			ErrorMessage = _WS(pCC->getError());
			return FALSE;
		}
		// XML読み込み
		try {
			std::stringstream ss;
			ss << contents;
			ptree pt;
			xml_parser::read_xml(ss, pt);

			// XML解釈
			// 地震発生日時の取得
			if (optional<string>str = pt.get_optional<string>("Report.Body.Earthquake.OriginTime")) {
//				cout << str.value() << " ";
				if (regex_search(str.value(), mt1, re1)) {
					Earthquake[i].year    = stoi(mt1[1].str());
					Earthquake[i].month   = stoi(mt1[2].str());
					Earthquake[i].day     = stoi(mt1[3].str());
					Earthquake[i].hour    = stoi(mt1[4].str());
					Earthquake[i].minuite = stoi(mt1[5].str());
				}
			}
			// 震源地の取得
			if (optional<string>str = pt.get_optional<string>("Report.Body.Earthquake.Hypocenter.Area.Name")) {
				Earthquake[i].location = _UW(str.value());
//				cout << _WS(Earthquake[i].location) << "  ";
			}
			if (optional<string>str = pt.get_optional<string>("Report.Body.Earthquake.Hypocenter.Area.jmx_eb:Coordinate")) {
				if (regex_search(str.value(), mt2, re2)) {
					pGC->tokyo_wgs84(stod(mt2[2].str()), stod(mt2[1].str()), &lng, &lat);
					Earthquake[i].latitude  = lat;
					Earthquake[i].longitude = lng;
					if (mt2[3].str() == "/") {
						Earthquake[i].depth = (-1.0);
					} else {
						Earthquake[i].depth = stod(mt2[4].str());
					}
//					cout << "lat=" << lat << " lng=" << lng << " ";
				}
			}
			// マグニチュードの取得
			if (optional<string>str = pt.get_optional<string>("Report.Body.Earthquake.jmx_eb:Magnitude")) {
				Earthquake[i].magnitude = stod(str.value());
//				cout << "M=" << Earthquake[i].magnitude << " ";
			}
			// 最大震度の取得
			if (optional<string>str = pt.get_optional<string>("Report.Body.Intensity.Observation.MaxInt")) {
				Earthquake[i].maxintensity = stoi(str.value());
//				cout << "max=" << Earthquake[i].maxintensity << endl;
			}
		// 読み込みエラー
		} catch(xml_parser_error& e) {
			ErrorMessage = "気象庁防災情報XMLにアクセスできません";
			return FALSE;
		}
		contents.clear();
	}
	delete pCC;

	return TRUE;
}

// 地図表示（ブラウザ） ======================================================
/**
 * 表示用文字列変換：緯度・経度
 * @param	char*  buff   出力先
 * @param	double latlng 緯度または経度
 * @param	char*  prefix 数字の前に付ける文字列（例：北緯）
 * @return	なし
*/
void printLatLng(char* buff, double latlng, char* prefix) {
	if (latlng <= 0.0) {
		snprintf(buff, SIZE_BUFF, "不明");
	} else {
		snprintf(buff, SIZE_BUFF, "%s%.1f", prefix, latlng);
	}
}

/**
 * 表示用文字列変換：震源の深さ
 * @param	char* buff  出力先
 * @param	double depth 震源の深さ（メートル）
 * @return	なし
*/
void printDepth(char* buff, double depth) {
	if (depth < 0.0) {
		snprintf(buff, SIZE_BUFF, "不明");
	} else if (depth == 0.0) {
		snprintf(buff, SIZE_BUFF, "ごく浅い");
	} else {
		snprintf(buff, SIZE_BUFF, "約%.0fkm", depth / 1000.0);
	}
}

/**
 * 表示用文字列変換：マグニチュード
 * @param	char* buff       出力先
 * @param	double magnitude マグニチュード
 * @param	char* prefix     数字の前に付ける文字列（例：マグニチュード）
 * @return	なし
*/
void printMagnitude(char* buff, double magnitude, char* prefix) {
	if (magnitude < 0.0) {
		snprintf(buff, SIZE_BUFF, "不明");
	} else {
		snprintf(buff, SIZE_BUFF, "%s%.1f", prefix, magnitude);
	}
}

/**
 * 表示用文字列変換：震度
 * @param	char* buff      出力先
 * @param	int   intensity 震度
 * @return	なし
*/
void printIntensity(char* buff, int intensity) {
	if (intensity < 0) {
		snprintf(buff, SIZE_BUFF, "不明");
	} else {
		snprintf(buff, SIZE_BUFF, "%d", intensity);
	}
}

/**
 * 地震情報テキストを取得
 * @param	int i   地震情報番号
 * @return	wstring 地震情報
*/
wstring makeInfo(int i) {
	char buff1[SIZE_BUFF + 1], buff2[SIZE_BUFF + 1], buff3[SIZE_BUFF + 1];
	char buff4[SIZE_BUFF + 1], buff5[SIZE_BUFF + 1];

	// 震源の位置
	if ((Earthquake[i].latitude <= 0.0) || (Earthquake[i].longitude <= 0.0)) {
		snprintf(buff2, SIZE_BUFF, "不明");
	} else {
		snprintf(buff2, SIZE_BUFF, "北緯 %.1f度，東経 %.1f度", Earthquake[i].latitude, Earthquake[i].longitude);
	}

	// 震源の深さ
	printDepth(buff3, Earthquake[i].depth);

	// マグニチュード
	printMagnitude(buff4, Earthquake[i].magnitude, (char*)"マグニチュード ");

	// 最大震度
	printIntensity(buff5, Earthquake[i].maxintensity);

	// 表示テキスト作成
	snprintf(buff1, SIZE_BUFF, "発生日時：%04d年%02d月%02d日 %02d時%02d分頃\n震源地：%s\n震源の位置：%s\n震源の深さ：%s\n地震の規模：%s\n最大震度：%s", Earthquake[i].year, Earthquake[i].month, Earthquake[i].day, Earthquake[i].hour, Earthquake[i].minuite, _WS(Earthquake[i].location).c_str(), buff2, buff3, buff4, buff5);

	return _SW(buff1);
}

/**
 * 地震情報をマッピング情報に変換
 * @param	なし
 * @return	int 変換したマッピング情報の件数
*/
int info2points(void) {
	int j = 0;
	int k = 0;
	for (int i = 0; i < MAX_EARTHQUAKE; i++) {
		if (Earthquake[i].year == 0) {
			break;
		}
		// 同じマッピング位置があるかどうか
		bool flag = FALSE;
		for (k = 0; k < j; k++) {
			if ((Earthquake[i].latitude == pGC->Ppoints[k].latitude) && (Earthquake[i].longitude == pGC->Ppoints[k].longitude)) {
				flag = TRUE;
				pGC->Ppoints[k].description += _SW("<br><hr>");
				break;
			}
		}
		// 新規のマッピング位置
		if (flag == FALSE) {
			k = j;
			pGC->Ppoints[k].latitude  = Earthquake[i].latitude;
			pGC->Ppoints[k].longitude = Earthquake[i].longitude;
			pGC->Ppoints[k].title = pGC->Ppoints[k].address = pGC->Ppoints[k].description = L"";
			pGC->Ppoints[k].icon = "";
			j++;
		}
		Earthquake[i].id = k + 1;

		pGC->Ppoints[k].description += wrepNL(makeInfo(i), _SW("<br>"));

		// 打ち切り条件
		if (j >= 26) {
			break;
		}
	}

	return j;
}

/**
 * 地図表示用HTML生成
 * @param	string 表示するインフォメーション
 * @return	string 生成したHTML文
 */
string makeMapHTML(wstring info) {
	int cnt = info2points();
	string script = pGC->makeMapLeaflet(MAP_ID, Earthquake[0].longitude, Earthquake[0].latitude, Zoom, Maptype, pGC->Ppoints, cnt, "", "", INFO_WIDTH, INFO_OFFSET_X, INFO_OFFSET_Y);

	string html = (boost::format(R"(<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="SJIS">
<title>%9%</title>
<meta name="author" content="studio pahoo" />
<meta name="copyright" content="studio pahoo" />
<meta name="ROBOTS" content="NOINDEX,NOFOLLOW" />
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
%8%
</head>
<body>
<div id="%1%" style="width:%2%px; height:%3%px;"></div>
<form>
<input id="latitude"  type="hidden" value="%4%" />
<input id="longitude" type="hidden" value="%5%" />
<input id="zoom"      type="hidden" value="%6%" />
<input id="maptype"   type="hidden" value="%7%" />
</form>
</body>
</html>
)")
% MAP_ID						// 地図ID
% MAP_WIDTH						// 地図の幅
% MAP_HEIGHT					// 地図の高さ
% Earthquake[0].latitude		// 緯度
% Earthquake[0].longitude		// 経度
% Zoom							// 地図拡大率
% Maptype						// 地図タイプ
% script						// 地図描画スクリプト
% APPNAMEJP						// アプリケーション名
).str();

	return html;
}

/**
 * エラーメッセージをHTML文にする
 * @param	なし
 * @return	string HTML文
*/
string makeErrorHTML(void) {
	return (boost::format(R"(
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="SJIS">
<title>%2%</title>
<meta name="author" content="studio pahoo" />
<meta name="copyright" content="studio pahoo" />
<meta name="ROBOTS" content="NOINDEX,NOFOLLOW" />
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
</head>
<body>
<p style="color:red; font-size:large;">%1%</p>
</body>
</html>
)")
% ErrorMessage
% APPNAMEJP
).str();
}

/**
 * ブラウザ・コントロールを表示
 * @param	wstring info 情報ウィンドウに表示するテキスト
 * @param	char* tmpname 読み込むHTMLファイル名
 * @return	なし
*/
void viewBrowser(wstring info, const char* tmpname) {
	string html;
	string fname;
	ofstream ofs;

	// 表示用HTMLファイル作成
	if ((ErrorMessage == "") && (! pGC->isError())) {
		html = makeMapHTML(info);
	} else {
		html = makeErrorHTML();
	}
	ofs.open(tmpname);
	ofs << html;
	ofs.close();

	fname = (string)tmpname;
	std::replace(fname.begin(), fname.end(), '\\', '/');

	if (isWeb2Ready()) {
		webView2->Reload();
	}
}

// 地震情報一覧作成（リストビュー） =========================================
// リストビューの列
enum {
	COL_ID,
	COL_DATETIME,
	COL_LOCATION,
	COL_LATITUDE,
	COL_LONGITUDE,
	COL_DEPTH,
	COL_MAGNITUDE,
	COL_MAXINTENSITY
};

/**
 * 地震情報一覧フレーム作成
 * @param	HWND hDlg  ウィンドウハンドル
 * @return	なし
*/
void makeListViewFrame(HWND hWnd) {
	LVCOLUMNA lvcol;
	lvcol.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_FMT;

	lvcol.cx = 25;
	lvcol.fmt = LVCFMT_CENTER;
	lvcol.pszText = (char *)"ID";
	SendMessage(hWnd, LVM_INSERTCOLUMNA, COL_ID, (WPARAM)&lvcol);
	lvcol.cx = 140;
	lvcol.fmt = LVCFMT_CENTER;
	lvcol.pszText = (char *)"発生日時";
	SendMessage(hWnd, LVM_INSERTCOLUMNA, COL_DATETIME, (WPARAM)&lvcol);
	lvcol.cx = 120;
	lvcol.fmt = LVCFMT_CENTER;
	lvcol.pszText = (char *)"場所";
	SendMessage(hWnd, LVM_INSERTCOLUMNA, COL_LOCATION, (WPARAM)&lvcol);
	lvcol.cx = 50;
	lvcol.fmt = LVCFMT_CENTER;
	lvcol.pszText = (char *)"北緯";
	SendMessage(hWnd, LVM_INSERTCOLUMNA, COL_LATITUDE, (WPARAM)&lvcol);
	lvcol.cx = 50;
	lvcol.fmt = LVCFMT_CENTER;
	lvcol.pszText = (char *)"東経";
	SendMessage(hWnd, LVM_INSERTCOLUMNA, COL_LONGITUDE, (WPARAM)&lvcol);
	lvcol.cx = 80;
	lvcol.fmt = LVCFMT_CENTER;
	lvcol.pszText = (char *)"深さ";
	SendMessage(hWnd, LVM_INSERTCOLUMNA, COL_DEPTH, (WPARAM)&lvcol);
	lvcol.cx = 50;
	lvcol.fmt = LVCFMT_CENTER;
	lvcol.pszText = (char *)"Ｍ";
	SendMessage(hWnd, LVM_INSERTCOLUMNA, COL_MAGNITUDE, (WPARAM)&lvcol);
	lvcol.cx = 70;
	lvcol.fmt = LVCFMT_CENTER;
	lvcol.pszText = (char *)"最大震度";
	SendMessage(hWnd, LVM_INSERTCOLUMNA, COL_MAXINTENSITY, (WPARAM)&lvcol);

	// 罫線表示
	DWORD dwStyle = ListView_GetExtendedListViewStyle(hWnd);
	dwStyle = dwStyle | LVS_EX_GRIDLINES | LVS_EX_ONECLICKACTIVATE | LVS_EX_FULLROWSELECT;;
	ListView_SetExtendedListViewStyle(hWnd, dwStyle);
}

/**
 * 地震情報一覧表示
 * @param	HWND hDlg  ウィンドウハンドル
 * @return	なし
*/
void makeListView(HWND hWnd) {
	LVITEMA item;
	char buff[SIZE_BUFF + 1];

	// 初期化
	for (int i = 0; i < __SIZE_PPOINTS; i++) {
		item.iItem = i;
		SendMessage(hWnd, LVM_DELETEITEM, 0, (WPARAM)&item);
	}

	// データ行
	for (int i = 0; i < MAX_EARTHQUAKE; i++) {
		if (Earthquake[i].year == 0) {
			break;
		}
		// ID
		item.mask = LVIF_TEXT;
		item.iItem = i;
		item.iSubItem = COL_ID;
		string s = {(char)(64 + Earthquake[i].id)};
		item.pszText = (LPSTR)(s.c_str());
		SendMessage(hWnd, LVM_INSERTITEMA, 0, (WPARAM)&item);
		// 日時
		snprintf(buff, SIZE_BUFF, "%02d月%02d日 %02d時%02d分頃",
			Earthquake[i].month, Earthquake[i].day,
			Earthquake[i].hour, Earthquake[i].minuite);
		item.iSubItem = COL_DATETIME;
		item.pszText  = (LPSTR)buff;
		SendMessage(hWnd, LVM_SETITEMA, 0,(WPARAM)&item);
		// 場所
		item.iSubItem = COL_LOCATION;
		item.pszText  = (LPSTR)_WS(Earthquake[i].location).c_str();
		SendMessage(hWnd, LVM_SETITEMA, 0,(WPARAM)&item);
		// 北緯
		printLatLng(buff, Earthquake[i].latitude, (char*)"");
		item.iSubItem = COL_LATITUDE;
		item.pszText  = (LPSTR)buff;
		SendMessage(hWnd, LVM_SETITEMA, 0,(WPARAM)&item);
		// 東経
		printLatLng(buff, Earthquake[i].longitude, (char*)"");
		item.iSubItem = COL_LONGITUDE;
		item.pszText  = (LPSTR)buff;
		SendMessage(hWnd, LVM_SETITEMA, 0,(WPARAM)&item);
		// 震源の深さ
		printDepth(buff, Earthquake[i].depth);
		item.iSubItem = COL_DEPTH;
		item.pszText  = (LPSTR)buff;
		SendMessage(hWnd, LVM_SETITEMA, 0,(WPARAM)&item);
		// マグニチュード
		printMagnitude(buff, Earthquake[i].magnitude, (char*)"");
		item.iSubItem = COL_MAGNITUDE;
		item.pszText  = (LPSTR)buff;
		SendMessage(hWnd, LVM_SETITEMA, 0,(WPARAM)&item);
		// 最大震度
		printIntensity(buff, Earthquake[i].maxintensity);
		item.iSubItem = COL_MAXINTENSITY;
		item.pszText  = (LPSTR)buff;
		SendMessage(hWnd, LVM_SETITEMA, 0,(WPARAM)&item);
	}
}

// 保存・コピー =============================================================
/**
 * 一覧情報をCSVファイルに保存する：保存処理
 * @param	const char* fname 保存ファイル名
 * @return	なし
 */
void __saveCSV(const char *fname) {
	char buff1[SIZE_BUFF + 1], buff2[SIZE_BUFF + 1], buff3[SIZE_BUFF + 1];
	char buff4[SIZE_BUFF + 1], buff5[SIZE_BUFF + 1], buff6[SIZE_BUFF + 1];
	string str;
	ofstream outputfile(fname);

	// ラベル
	outputfile << "\"" << "ID" << "\",";
	outputfile << "\"" << "日時" << "\",";
	outputfile << "\"" << "場所" << "\",";
	outputfile << "\"" << "緯度" << "\",";
	outputfile << "\"" << "経度" << "\",";
	outputfile << "\"" << "深さ" << "\",";
	outputfile << "\"" << "マグニチュード" << "\",";
	outputfile << "\"" << "最大震度" << "\"";
	outputfile << endl;

	// データ本体
	for (int i = 0; i < MAX_EARTHQUAKE; i++) {
		if (Earthquake[i].year != 0) {
			// 日時
			snprintf(buff1, SIZE_BUFF, "%04d-%02d-%02dT%02d:%02d:00+09:00",
				Earthquake[i].year, Earthquake[i].month, Earthquake[i].day,
				Earthquake[i].hour, Earthquake[i].minuite);
			// 緯度
			printLatLng(buff2, Earthquake[i].latitude, (char*)"");
			// 経度
			printLatLng(buff3, Earthquake[i].longitude, (char*)"");
			// 震源の深さ
			printDepth(buff4, Earthquake[i].depth);
			// マグニチュード
			printMagnitude(buff5, Earthquake[i].magnitude, (char*)"");
			// 最大震度
			printIntensity(buff6, Earthquake[i].maxintensity);

			outputfile << "\"" << (char)(64 + Earthquake[i].id) << "\",";
			outputfile << "\"" << buff1 << "\",";
			outputfile << "\"" << _WS(Earthquake[i].location) << "\",";
			outputfile << "\"" << buff2 << "\",";
			outputfile << "\"" << buff3 << "\",";
			outputfile << "\"" << buff4 << "\",";
			outputfile << "\"" << buff5 << "\",";
			outputfile << "\"" << buff6 << "\"";
			outputfile << endl;
		}
		if (outputfile.bad()) {
			ErrorMessage = "CSVファイル書き込みエラー";
			break;
		}
	}
	outputfile.close();
}

/**
 * 一覧情報をCSVファイルに保存する：ファイルダイアログ
 * @param	なし
 * @return	なし
 */
void saveCSV(void) {
	static char fname[MAX_PATH + 1];
	snprintf(fname, MAX_PATH, SAVEFILE, Earthquake[0].year, Earthquake[0].month, Earthquake[0].day, Earthquake[0].hour, Earthquake[0].minuite);
	OPENFILENAME of;

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

/**
 * クリップボードにコピーする文字列を生成
 * @param	なし
 * @return	string コピーする文字列
 */
string copyText(void) {
	char buff1[SIZE_BUFF + 1], buff2[SIZE_BUFF + 1], buff3[SIZE_BUFF + 1];
	char buff4[SIZE_BUFF + 1], buff5[SIZE_BUFF + 1], buff6[SIZE_BUFF + 1];
	string str;
	ostringstream oss;

	// ラベル
	oss << "ID" << "\t";
	oss << "日時" << "\t";
	oss << "場所" << "\t";
	oss << "緯度" << "\t";
	oss << "経度" << "\t";
	oss << "深さ" << "\t";
	oss << "マグニチュード" << "\t";
	oss << "最大震度" << endl;

	// データ本体
	for (int i = 0; i < MAX_EARTHQUAKE; i++) {
		if (Earthquake[i].year != 0) {
			// 日時
			snprintf(buff1, SIZE_BUFF, "%04d-%02d-%02dT%02d:%02d:00+09:00",
				Earthquake[i].year, Earthquake[i].month, Earthquake[i].day,
				Earthquake[i].hour, Earthquake[i].minuite);
			// 緯度
			printLatLng(buff2, Earthquake[i].latitude, (char*)"");
			// 経度
			printLatLng(buff3, Earthquake[i].longitude, (char*)"");
			// 震源の深さ
			printDepth(buff4, Earthquake[i].depth);
			// マグニチュード
			printMagnitude(buff5, Earthquake[i].magnitude, (char*)"");
			// 最大震度
			printIntensity(buff6, Earthquake[i].maxintensity);

			oss << (char)(64 + Earthquake[i].id) << "\t";
			oss << buff1 << "\t";
			oss << _WS(Earthquake[i].location) << "\t";
			oss << buff2 << "\t";
			oss << buff3 << "\t";
			oss << buff4 << "\t";
			oss << buff5 << "\t";
			oss << buff6 << endl;
		}
	}
	return oss.str();
}

// WebView2制御用関数群 ======================================================
/**
 * WebView2：ダイアログを初期化する
 * @param	HWND hDlg	親ウィンドウ・ハンドラ
 * @return	なし
*/
void initDialog(HWND hDlg) {
	HICON hIcon;
	hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
	SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
	ErrorMessage = "";

	// カーソルを砂時計に
	SetCursor(LoadCursor(NULL, IDC_WAIT));
	// オプション読み込み
	loadParameter();
	// アプリケーション・ウィンドウ移動
	SetWindowPos(hDlg, NULL, hParent_X, hParent_Y, 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER));

	// テンポラリ・ファイル名取得
	getTempFname(tmpFname);
	size_t wLen = 0;
	mbstowcs_s(&wLen, wUri, MAX_PATH * 2, tmpFname, MAX_PATH);
	// WebView2生成
	hWebView2 = createWebView2(hInst, hDlg, 10, 10, MAP_WIDTH + 20, MAP_HEIGHT + 20, wUri);

	// Leafletが利用可能かどうか調べる
	const string url = "https://unpkg.com/leaflet@latest/";
	string contents = "";
	int httpStatus = 0;
	bool res = readWebContents(url, UserAgent, &contents, &httpStatus);
	if (res == FALSE) {
		ErrorMessage = "地図描画ライブラリ（Leaflet）が利用できません";
	} else if ((httpStatus < 200) || (httpStatus > 299)) {
		ErrorMessage = "地図描画ライブラリ（Leaflet）が利用できません";
	}

	// メッセージ・ループ
	MSG msg;
	static bool flagWebView2 = false;
	while (GetMessage(&msg, nullptr, 0, 0)) {
		if (msg.message == WM_QUIT) {
			// オプション保存
			// saveOption();
			DestroyWindow(hDlg);
		}
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		if (!flagWebView2 && isWeb2Ready()) {
			// カーソルを砂時計に
			SetCursor(LoadCursor(NULL, IDC_WAIT));
			// 地震情報取得
			getEarthquake();
			// ブラウザ・コントロールに表示
			viewBrowser(infoEarthquake, tmpFname);
			webView2->Reload();
			flagWebView2 = true;
			// 地震情報一覧フレーム作成
			makeListViewFrame(GetDlgItem(hDlg, IDC_LISTVIEW_EARTHQUAKE));
			// 地震情報一覧表示
			makeListView(GetDlgItem(hDlg, IDC_LISTVIEW_EARTHQUAKE));
		}
	}
}

/**
 * WebView2のパラメータを取り出す．
 * selectActionの値によって動作を変える。
 * @param	string result	JavaScript終了ハンドラから渡るデータ
 * @return	なし
*/
void execScriptCompleted(string result) {
	// パラメータを読み込む
	vector<string> tokens;
	split(tokens, result, is_any_of(",\""));
	int cnt = 0;
	for (string ss: tokens) {
		switch (cnt) {
			case 1:
				Longitude = stod(ss);
				break;
			case 2:
				Latitude = stod(ss);
				break;
			case 3:
				Zoom = stoi(ss);
				break;
			case 4:
				Maptype = ss;
				break;
		}
		cnt++;
	}

	switch (selectAction) {
		// プログラムを終了する．
		case eAction::Finish:
			// テンポラリ・ファイルを削除する．
			remove(tmpFname);
			// パラメータを保存する．
			saveParameter();
			// プログラムを終了する
			EndDialog(hParent, 0);
			DestroyWindow(hParent);
			break;
		// ブラウザ・コントロールに表示
		default:
			viewBrowser(infoEarthquake, tmpFname);
			break;
	}
//	cout << "Longitude: " << Longitude << endl;
//	cout << "Latitude: " << Latitude << endl;
//	cout << "Zoom: " << Zoom << endl;
//	cout << "Maptype: " << Maptype << endl;
}

/**
 * WebView2：JavaScript終了ハンドラ
 * @param	HWND hDlg			親ウィンドウ・ハンドラ
 * @paramm	UINT uMsg			メッセージ識別子
 * @param	WPARAM wParam		メッセージの最初のパラメータ
 * @paramL	PARAM lParam		メッセージの2番目のパラメータ
 * @return	INT_PTR CALLBACK	TRUE：メッセージ処理完了／FALSE：未完了
*/
class MyScriptCompletedHandler : public ICoreWebView2ExecuteScriptCompletedHandler {
public:
	HRESULT STDMETHODCALLTYPE Invoke(HRESULT error, PCWSTR result) override {
		if (SUCCEEDED(error)) {
			// PCWSTR → char* 変換
			int requiredSize = WideCharToMultiByte(CP_UTF8, 0, result, -1, nullptr, 0, nullptr, nullptr);
			char* buffer = new char[requiredSize];
			WideCharToMultiByte(CP_UTF8, 0, result, -1, buffer, requiredSize, nullptr, nullptr);
			std::string res(buffer);
			// JavaScript終了時に実行する関数
			execScriptCompleted(res);
		}
		return S_OK;
	}

	HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override {
		return E_NOINTERFACE;
	}

	ULONG STDMETHODCALLTYPE AddRef(void) override {
		return 1;
	}

	ULONG STDMETHODCALLTYPE Release(void) override {
		return 1;
	}
};

// WebView2のJavaScriptを実行してDOM要素を取得
MyScriptCompletedHandler* scriptCompletedHandler = new MyScriptCompletedHandler();

/**
 * WebView2のパラメータを保存する．
 * selectActionの値によって動作を変える。
 * @param	なし
 * @return	なし
*/
void execScriptAndAction(void) {
	// プログラム終了時に実行するJavaScript
	LPCWSTR js = L"document.getElementById('longitude').value + ',' + document.getElementById('latitude').value + ',' + document.getElementById('zoom').value + ',' + document.getElementById('maptype').value;";

	webView2->ExecuteScript(js, scriptCompletedHandler);
}

// メインウィンドウ ==========================================================
/**
 * イベントハンドラ：メインウィンドウ
 * @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) {
	pahooCache* pCC;	// pahooCacheオブジェクト

    switch(uMsg) {
	// ダイアログ初期化
	case WM_INITDIALOG:
		break;

	// ボタン押下
	case WM_COMMAND:
		 switch (LOWORD(wParam)) {
		// 実行
		case IDM_EXEC:
		case IDC_BUTTON_EXEC:
			ErrorMessage = "";
			// カーソルを砂時計に
			SetCursor(LoadCursor(NULL, IDC_WAIT));
			// 地震情報取得
			getEarthquake();
			// WebView2のパラメータを取得する．
			execScriptAndAction();
			// 地震情報一覧表示
			makeListView(GetDlgItem(hDlg, IDC_LISTVIEW_EARTHQUAKE));
            break;
		// 保存
		case IDC_BUTTON_SAVE:
		case IDM_SAVE:
			saveCSV();
			break;
		// GoogleAPIキー保存
		case IDM_GOOGLE_APIKEY:
			createSetAPIkey(hDlg, LABEL_GOOGLE_API, FNAME_GOOGLE_API, URL_GOOGLE_API, &pGC->GoogleAPIkey);
			pGC->readGoogleApiKey();
			// エラー・リセット
			ErrorMessage = "";
			pGC->resetError();
			// GoogleAPIキー未登録ならデフォルトのマップタイプに戻す．
			Maptype = pGC->isGoogleApiKey() ? "GMRD" : DEF_MAPTYPE;
			// ブラウザ・コントロールに表示
			viewBrowser(infoEarthquake, tmpFname);
			// 地震情報一覧表示
			makeListView(GetDlgItem(hDlg, IDC_LISTVIEW_EARTHQUAKE));
			break;
		// キャッシュ・クリア
		case IDM_CLEAR_CACHE:
			pCC = new pahooCache(LIFE_CACHE_FEED_L, getMyPath(APPNAME) + DIR_CACHE_FEED_L, UserAgent);
			pCC->delCache(".+", 0);
			delete pCC;
			pCC = new pahooCache(LIFE_CACHE_FEED, getMyPath(APPNAME) + DIR_CACHE_FEED, UserAgent);
			pCC->delCache(".+", 0);
			delete pCC;
			pCC = new pahooCache(LIFE_CACHE_DATA, getMyPath(APPNAME) + DIR_CACHE_DATA, UserAgent);
			pCC->delCache(".+", 0);
			delete pCC;
			break;
		// 設定クリア＋アプリ終了
		case IDM_CLEAR_PARAMETER:
			// 設定ファイルを削除する．
			delParameter();
			// テンポラリ・ファイルを削除する．
			remove(tmpFname);
			EndDialog(hParent, 0);
			DestroyWindow(hDlg);
			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(copyText());
			break;
		// プログラム終了
		case IDM_QUIT:
			selectAction = eAction::Finish;
			execScriptAndAction();
			break;
		default:
			return 1;
		}
		break;

	// プログラム終了
	case WM_CLOSE:
		selectAction = eAction::Finish;
		execScriptAndAction();
		break;
	}
	return 0;
}

// メイン・プログラム =======================================================
/**
 * Windowsメインプログラム
 * @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");

	// UserAgent生成
	static OSVERSIONINFOEX os;
	GetVersion2(&os);
	UserAgent = (string)"Mozilla/5.0 (" + APPNAME + "/"
		+ APPVERSION + "/" + MAKER
		+ ", Windows NT " + to_string(os.dwMajorVersion) + "."
		+ to_string(os.dwMinorVersion) + ")";

	// pahooGeocodeオブジェクト
	pGC = new pahooGeocode(APPNAME);

	// メイン・ダイアログ
	HWND hDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)processMain);
	hInst = hInstance;
	hParent = hDlg;
	// メイン・ダイアログを初期化する．
	initDialog(hDlg);

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

	return 0;
}

/*
** バージョンアップ履歴 =====================================================
 *
 * @version 4.2.4  2026/05/30 使用ライブラリ更新
 * @version 4.2.3  2026/02/21 使用ライブラリ更新
 * @version 4.2.2  2025/10/25 使用ライブラリ更新
 * @version 4.2.1  2025/07/17 使用ライブラリ更新
 * @version 4.2.0  2025/03/15 Leafletアクセス可否チェック追加,使用ライブラリ更新
 * @version 4.1.2  2024/11/23 使用ライブラリ更新
 * @version 4.1.1  2024/08/17 使用ライブラリ更新
 * @version 4.1.0  2024/05/03 enum eActionクラス導入，API入力処理を改良
 * @version 4.0.0  2024/04/27 Edgeブラウザ対応，64ビット対応
 * @version 3.4.5  2024/03/16 使用ライブラリ更新
 * @version 3.4.4  2023/11/11 使用ライブラリ更新
 * @version 3.4.3  2023/07/02 使用ライブラリ更新
 * @version 3.4.2  2023/03/18 使用ライブラリ更新
 * @version 3.4.1  2022/11/12 使用ライブラリ更新
 * @version 3.4.0  2022/09/03 各種パラメータの保存
 * @version 3.3.1  2022/09/03 最新版ライブラリに更新
 * @version 3.3    2022/03/19 気象庁防災情報XMLのhttps化に対応
 * @version 3.2    2021/05/24 不具合修正：深さ不明のパターン追加，他
 * @version 3.1    2021/05/01 キャッシュ・システム導入，等
 * @version 3.0    2021/03/30 最近の地震情報（複数）を一覧表示，等
 * @version 2.0    2021/03/07 気象庁防災情報XMLから情報取得に変更
 * @version 1.1    2021/01/02 pahooGeocodeクラス導入など大幅改訂
 * @version 1.0    2020/09/19 初版
*/
