/** pahooWeather.cpp
 * 気象情報に関わるクラス：C++ソース
 *
 * @copyright	(c)studio pahoo
 * @author		パパぱふぅ
 * @動作環境	MinGW C++ + cURL + OpenSSL + Boost C++ Libraries
 * @参考URL		https://www.pahoo.org/e-soul/webtech/cpp01-16-01.shtm
 */
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <time.h>
#include <sstream>
#include <string>
#include <iterator>
#include <list>
#include <vector>
#include <regex>
#include <locale>
#include <winsock2.h>
#include <windows.h>
#include <shlobj.h>
#include <commctrl.h>
#include <math.h>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/format.hpp>
#include "mystrings.h"
#include "pahooWeather.hpp"

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

#define UNNOWN_INT	-9999

/**
 * 文字列変数からXMLを読み込む．
 * @param	string* xml XMLを格納した変数
 * @return	bool TRUE：pahoo.org上でのファイルの方が新しい
 */
property_tree::ptree readXmlFromString(const string& xml) {
	stringstream ss(xml);
	property_tree::ptree pt;
	property_tree::read_xml(ss, pt);

	return pt;
}

// 予報地点情報格納用クラス ===================================================
#define FILE_VERSION "2.2"			// 予報値地点情報ファイルのバージョン
#define SIZE_SPOTS	500				// 格納上限

class _Spots {
public:
	string  code        = "";		// 電文コード
	string  page        = "";		// ページ番号
	string  regionCode  = "";		// 地方コード
	wstring regionName  = L"";		// 地方名
	string  prefCode    = "";		// 都道府県コード
	wstring prefName    = L"";		// 都道府県名
	string  areaCode    = "";		// 地域コード
	wstring areaName    = L"";		// 地域名
	string  stationCode = "";		// 予報地点コード
	wstring stationName = L"";		// 予報地点名
	wstring location    = L"";		// 所在地
	double  latitude    = 0.0;		// 緯度
	double  longitude   = 0.0;		// 経度
};
static unique_ptr<_Spots> Spots[SIZE_SPOTS] = {};

// 気象庁防災情報XML：長期フィード - 定時配信
const char* FEED_REGULAR_L = "https://www.data.jma.go.jp/developer/xml/feed/regular_l.xml";

/**
 * コンストラクタ
 * @param	pahooCache* pc pahooCacheオブジェクト
 */
pahooWeather::pahooWeather(pahooCache* pc) {
	pCC = pc;
	readJmaSpots();
}

// デストラクタ
pahooWeather::~pahooWeather() {
}

// 各種処理 ==================================================================
/**
 * エラー状況
 * @param	なし
 * @return	bool TRUE:異常／FALSE:正常
*/
bool pahooWeather::isError(void) {
	return this->errmsg.length() > 0;
}

/**
 * エラーメッセージを取得する．
 * @param	なし
 * @return	wstring 現在発生しているエラーメッセージ
*/
wstring pahooWeather::getError(void) {
	return this->errmsg;
}

/**
 * エラーメッセージをセットする．
 * @param	wstring 現在発生しているエラーメッセージ
 * @return	なし
*/
void pahooWeather::setError(wstring wstr) {
	this->errmsg = wstr;
}

/**
 * エラーをリセットする
 * @param	なし
 * @return	なし
*/
void pahooWeather::resetError(void) {
	this->errmsg = L"";
}

/**
 * 直近で使用したWebAPIのURL取得
 * @param	なし
 * @return	string URL
*/
string pahooWeather::getwebapi(void) {
	return this->webapi;
}

// 予報地点情報 =======================================================
/**
 * ローカルの予報地点情報ファイルとpahoo.org上のファイルを比べる．
 * @param	なし
 * @return	bool TRUE：pahoo.org上でのファイルの方が新しい
 */
bool pahooWeather::isExistsNewerFileSpots(void) {
	string xml;
	string ssPahoo, ssLocal;

	// XML読み込み：pahoo.org
	int httpStatus = 0;
	if (readWebContents(FILE_SPOTS_PAHOO, UserAgent, &xml, &httpStatus, nullptr) == FALSE) {
		return FALSE;
	} else if ((httpStatus < 200) || (httpStatus > 299)) {
		return FALSE;
	}
	try {
		ptree ptPahoo;
		ptPahoo = readXmlFromString(xml);
		// XML解釈
		if (optional<string>updatePahoo = ptPahoo.get_optional<string>("jmaweatherspots.update")) {
			ssPahoo = (string)updatePahoo.get();
//			cout << "pahoo.org = " << ssPahoo << endl;
		// XML解釈失敗→更新しない
		} else {
			return FALSE;
		}
	// 読み込みエラー→更新しない
	} catch(xml_parser_error& e) {
		return FALSE;
	}

	// XML読み込み：ローカル
	string path = getModulePath();
	if (path == "") {
		return FALSE;
	}
	try {
		ptree ptLocal;
		xml_parser::read_xml(path + "\\" + FILE_SPOTS_LOCAL, ptLocal);
		// XML解釈
		if (optional<string>updateLocal = ptLocal.get_optional<string>("jmaweatherspots.update")) {
			ssLocal = (string)updateLocal.get();
//			cout << "local = " << ssLocal << endl;
		// XML解釈失敗→更新する
		} else {
			return FALSE;
		}
	// 読み込みエラー→更新する
	} catch(xml_parser_error& e) {
		return TRUE;
	}

	return (bool)(ssPahoo.compare(ssLocal) > 0);
}

/**
 * ローカルの予報地点情報ファイルが古ければ更新する．
 * pahoo.org上のファイルと比べて，updateが古ければダウンロードして差し替える．
 * @param	なし
 * @return	bool TRUE：更新した／FALSE：更新しないまたはエラー
 */
bool pahooWeather::updateFileSpots(void) {
	// 更新実行
	if (this->isExistsNewerFileSpots()) {
		string contents;
		int httpStatus = 0;
		if (readWebContents(FILE_SPOTS_PAHOO, UserAgent, &contents, &httpStatus, nullptr) == FALSE) {
			return FALSE;
		} else if ((httpStatus < 200) || (httpStatus > 299)) {
			return FALSE;
		}

		string path = getModulePath();
		if (path == "") {
			return FALSE;
		}
		ofstream outputfile(path + "\\" + FILE_SPOTS_LOCAL);
		outputfile << contents;
		outputfile.close();

		return TRUE;

	// 更新しない
	} else {
		return FALSE;
	}
}

// 気象情報（気象庁） =======================================================
/**
 * 予報地点情報を読み込む
 * @param	なし
 * @return	int 読み込んだ地点情報数／(-1)：読み込み失敗
 */
int pahooWeather::readJmaSpots(void) {
	int cnt = -1;
	try {
		// XMLファイル読み込み
		string path = getModulePath();
		ptree pt;
		xml_parser::read_xml(path + "\\" + FILE_SPOTS_LOCAL, pt);
		// XML解釈
		ptree tree;
		for (auto it : pt.get_child("jmaweatherspots")) {
			cnt++;
			Spots[cnt] = make_unique<_Spots>();
			// 電文コード
			if (optional<string>code = it.second.get_optional<string>("code")) {
				Spots[cnt]->code = code.value();
			}
			// ページ番号
			if (optional<string>page = it.second.get_optional<string>("page")) {
				Spots[cnt]->page = page.value();
			}
			// 地方コード
			if (optional<string>regionCode = it.second.get_optional<string>("regionCode")) {
				Spots[cnt]->regionCode = regionCode.value();
			}
			// 地方名
			if (optional<string>regionName = it.second.get_optional<string>("regionName")) {
				Spots[cnt]->regionName = _UW(regionName.value());
			}
			// 都道府県コード
			if (optional<string>prefCode = it.second.get_optional<string>("prefCode")) {
				Spots[cnt]->prefCode = prefCode.value();
			}
			// 都道府県名
			if (optional<string>prefName = it.second.get_optional<string>("prefName")) {
				Spots[cnt]->prefName = _UW(prefName.value());
			}
			// 地域コード
			if (optional<string>areaCode = it.second.get_optional<string>("areaCode")) {
				Spots[cnt]->areaCode = areaCode.value();
			}
			// 地域名
			if (optional<string>areaName = it.second.get_optional<string>("areaName")) {
				Spots[cnt]->areaName = _UW(areaName.value());
			}
			// 予報地点コード
			if (optional<string>stationCode = it.second.get_optional<string>("stationCode")) {
				Spots[cnt]->stationCode = stationCode.value();
			}
			// 予報地点名
			if (optional<string>stationName = it.second.get_optional<string>("stationName")) {
				Spots[cnt]->stationName = _UW(stationName.value());
			}
			// 所在地
			if (optional<string>location = it.second.get_optional<string>("location")) {
				Spots[cnt]->location = _UW(location.value());
			}
			//
			// 経度
			if (optional<string>longitude = it.second.get_optional<string>("longitude")) {
				Spots[cnt]->longitude = stod(longitude.value());
			}
			// 緯度
			if (optional<string>latitude = it.second.get_optional<string>("latitude")) {
				Spots[cnt]->latitude = stod(latitude.value());
			}
		}
	// 読み込みエラー
	} catch(xml_parser_error& e) {
		errmsg = _SW("予\報地点情報ファイルを読み込めません");
		return (-1);
	}

	return cnt;
}

#define rad2deg(a) ((double)(a)/M_PI * 180.0)		// ラジアン→角度
#define deg2rad(a) ((double)(a)/180.0 * M_PI)		// 角度→ラジアン

/**
 * 2地点間の直線距離を求める（Hubenyの簡易式による；日本測地系！）
 * @param	double long_a, lati_a  A地点の経度，緯度（世界測地系）
 * @param	double long_b, lati_b  B地点の経度，緯度（世界測地系）
 * @return	double 直線距離（メートル）
*/
double pahooWeather::distance(double long_a, double lati_a,
			double long_b, double lati_b) {
	// 西経の補正
	if (long_a < 0) {
		long_a += 360.0;
	}
	if (long_b < 0) {
		long_b += 360.0;
	}

	// ラジアンに変換
	long_a = deg2rad(long_a);
	lati_a = deg2rad(lati_a);
	long_b = deg2rad(long_b);
	lati_b = deg2rad(lati_b);

	double latave   = (double)(lati_a + lati_b) / 2.0;
	double latidiff = (double)lati_a - lati_b;
	double longdiff = (double)long_a - long_b;

	// 子午線曲率半径
	double meridian = (double)6335439.0 / sqrt(pow((double)1.0 - 0.006694 * sin(latave) * sin(latave), 3.0));
	// 卯酉線曲率半径
	double primevertical = (double)6378137.0 / sqrt((double)1.0 - 0.006694 * sin(latave) * sin(latave));

	// Hubenyの簡易式
	double x = meridian * latidiff;
	double y = primevertical * cos(latave) * longdiff;

	return sqrt(x * x + y * y);
}

/**
 * 指定した緯度・経度に最も近い予報地点コードを返す
 * @param	double latitude  緯度（世界測地系）
 * @param	double longitude 経度（世界測地系）
 * @param	int    forecast  0：天気予報，1：週間天気予報（省略時：1）
 * @param	float  distanceMax 予報地点からの最大距離（km）
 *						これより近い予報地点が見つからなければ空文字を返す
 * @return	string stationCode 予報地点コードを格納
 */
string pahooWeather::getJmaNearSpot(double longitude, double latitude, int forecast=1, double distanceMax=99999.9) {
	string stationCode = "";
	double d0  = 999999999.9;
	// 電文コード
	string code = "";
	if (forecast == 0) {
		code = "VPFD51";
	} else {
		code = "VPFW50";
	}

	// 最も近い予報地点を探す
	for (int i = 0; i < SIZE_SPOTS; i++) {
		if (Spots[i] == NULL) {
			break;
		}
		if (Spots[i]->code == code) {
			double d1 = this->distance(Spots[i]->longitude, Spots[i]->latitude, longitude, latitude);
			if (d1 < d0) {
				stationCode = Spots[i]->stationCode;
				d0 = d1;
			}
		}
	}

	// 戻り値
	if (d0 > distanceMax * 1000.0) {
		this->setError(_SW("予\報地点が見つかりません"));
		return "";
	} else {
		return stationCode;
	}
}

/**
 * 数字列の平均を計算する
 * @param	wstring ws 数字列（区切り文字は '/'）
 * @return	double 平均値
*/
double pahooWeather::waverage(wstring ws) {
	size_t i;
	vector<wstring> arr = wsplit(ws, L'/');
	double n = 0.0;
	wsmatch mt;;
	wregex re(_SW("[0-9]+"));
	int k = 0;

	for (i = 0; i < arr.size(); i++) {
		if (regex_search(arr[i], mt, re)) {
			n += stod(arr[i]);
			k++;
		}
	}
	return (double)(n / k);
}

/**
 * 天気予報の平仮名を省略してテキストを短縮
 * （情報XMLの予報情報を気象庁サイトの予報情報に合わせる）
 * @param	wstring weather 天気予報
 * @return	wstring 短縮後テキスト
*/
wstring pahooWeather::jmaShortWeather(wstring weather) {
	weather = regex_replace(weather, wregex(_SW("くもり")), _SW("曇"));
	weather = regex_replace(weather, wregex(_SW("[ぁ-ん]+")), _SW(""));

	return weather;
}

/**
 * 天気予報の記号（テロップ番号）を参照画像URLに変換
 * （情報XMLのテロップ番号を気象庁サイトのアイコン番号に変換）
 * @param	int telop テロップ番号
 * @param	int mode  0：日中，1：夜間（省略時＝0）
 *						夜間を選ぶと，晴れマーク（太陽）が月・星になる
 * @return	string 参照画像URL
*/
string pahooWeather::jma_telop2url(int telop, int mode=0) {
	static int table[118][3] = {
{ 100, 100, 500 },
{ 101, 101, 501 },
{ 102, 102, 502 },
{ 103, 102, 502 },
{ 104, 104, 504 },
{ 105, 104, 504 },
{ 106, 102, 502 },
{ 107, 102, 502 },
{ 108, 102, 502 },
{ 110, 110, 510 },
{ 111, 110, 510 },
{ 112, 112, 512 },
{ 113, 112, 512 },
{ 114, 112, 512 },
{ 115, 115, 515 },
{ 116, 115, 515 },
{ 117, 115, 515 },
{ 118, 112, 512 },
{ 119, 112, 512 },
{ 120, 102, 502 },
{ 121, 102, 502 },
{ 122, 112, 512 },
{ 123, 100, 500 },
{ 124, 100, 500 },
{ 125, 112, 512 },
{ 126, 112, 512 },
{ 127, 112, 512 },
{ 128, 112, 512 },
{ 130, 100, 500 },
{ 131, 100, 500 },
{ 132, 101, 501 },
{ 140, 102, 502 },
{ 160, 104, 504 },
{ 170, 104, 504 },
{ 181, 115, 515 },
{ 200, 200, 200 },
{ 201, 201, 601 },
{ 202, 202, 202 },
{ 203, 202, 202 },
{ 204, 204, 204 },
{ 205, 204, 204 },
{ 206, 202, 202 },
{ 207, 202, 202 },
{ 208, 202, 202 },
{ 209, 200, 200 },
{ 210, 210, 610 },
{ 211, 210, 610 },
{ 212, 212, 212 },
{ 213, 212, 212 },
{ 214, 212, 212 },
{ 215, 215, 215 },
{ 216, 215, 215 },
{ 217, 215, 215 },
{ 218, 212, 212 },
{ 219, 212, 212 },
{ 220, 202, 202 },
{ 221, 202, 202 },
{ 222, 212, 212 },
{ 223, 201, 601 },
{ 224, 212, 212 },
{ 225, 212, 212 },
{ 226, 212, 212 },
{ 228, 215, 215 },
{ 229, 215, 215 },
{ 230, 215, 215 },
{ 231, 200, 200 },
{ 240, 202, 202 },
{ 250, 204, 204 },
{ 260, 204, 204 },
{ 270, 204, 204 },
{ 281, 215, 215 },
{ 300, 300, 300 },
{ 301, 301, 701 },
{ 302, 302, 302 },
{ 303, 303, 303 },
{ 304, 300, 300 },
{ 306, 300, 300 },
{ 308, 308, 308 },
{ 309, 303, 303 },
{ 311, 311, 711 },
{ 313, 313, 313 },
{ 314, 314, 314 },
{ 315, 314, 314 },
{ 316, 311, 711 },
{ 317, 313, 313 },
{ 320, 311, 711 },
{ 321, 313, 313 },
{ 322, 303, 303 },
{ 323, 311, 711 },
{ 324, 311, 711 },
{ 325, 311, 711 },
{ 326, 314, 314 },
{ 327, 314, 314 },
{ 328, 300, 300 },
{ 329, 300, 300 },
{ 340, 400, 400 },
{ 350, 300, 300 },
{ 361, 411, 811 },
{ 371, 413, 413 },
{ 400, 400, 400 },
{ 401, 401, 801 },
{ 402, 402, 402 },
{ 403, 403, 403 },
{ 405, 400, 400 },
{ 406, 406, 406 },
{ 407, 406, 406 },
{ 409, 403, 403 },
{ 411, 411, 811 },
{ 413, 413, 413 },
{ 414, 414, 414 },
{ 420, 411, 811 },
{ 421, 413, 413 },
{ 422, 414, 414 },
{ 423, 414, 414 },
{ 425, 400, 400 },
{ 426, 400, 400 },
{ 427, 400, 400 },
{ 450, 400, 400 }
};

	string ss = "";
	for (int i = 0; i < 118; i++) {
		if (table[i][0] == telop) {
			if (mode == 0) {
				ss = to_string(table[i][1]);
			} else {
				ss = to_string(table[i][2]);
			}
			break;
		}
	}
	return "https://www.jma.go.jp/bosai/forecast/img/" + ss + ".svg";
}


/**
 * 気象庁防災情報XMLから最新の天気予報情報URLを取得
 * @param	long page ページ番号
 * @param	string *vpfd51 府県天気予報URL
 * @param	string *vpfw50 府県週間
 * @return	なし
 array(VPFD51, VPFW50)／FALSE：取得失敗
*/
void pahooWeather::jmaGetWeatherForecastURL(long page, string *vpfd51, string *vpfw50) {
	// URLパターン
	char buff1[SIZE_BUFF + 1], buff2[SIZE_BUFF + 1];
	snprintf(buff1, SIZE_BUFF, "https?\\:\\/\\/www\\.data\\.jma\\.go\\.jp\\/developer\\/xml\\/data\\/([0-9\\_]+)VPFD51\\_%06ld\\.xml", page);
	snprintf(buff2, SIZE_BUFF, "https?\\:\\/\\/www\\.data\\.jma\\.go\\.jp\\/developer\\/xml\\/data\\/([0-9\\_]+)VPFW50\\_%06ld\\.xml", page);
	regex re1(buff1);
	regex re2(buff2);
	smatch mt1, mt2;

	string contents = "";
	if (pCC->load(FEED_REGULAR_L, &contents) == FALSE) {
		errmsg = pCC->getError();
		return;
	}
	string vpfd51_url = "";
	string vpfd51_dt  = "";
	string vpfw50_url = "";
	string vpfw50_dt  = "";

//	cout << contents << endl;

	// 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")) {
					// 日時がより新しいURLを採用
					if (regex_search(url.value(), mt1, re1)) {
						if (mt1[1].str() > vpfd51_dt) {
							vpfd51_url = mt1[0].str();
							vpfd51_dt  = mt1[1].str();
						}
					} else if (regex_search(url.value(), mt2, re2)) {
						if (mt2[1].str() > vpfw50_dt) {
							vpfw50_url = mt2[0].str();
							vpfw50_dt  = mt2[1].str();
						}
					}
				}
			}
		// XML解釈エラー
		} catch(ptree_bad_path& e) {
			errmsg = _SW("気象庁防災情報XMLにアクセスできません");
			return;
		}
	// 読み込みエラー
	} catch(xml_parser_error& e) {
		errmsg = _SW("気象庁防災情報XMLにアクセスできません");
		return;
	}
	contents.clear();

	// ★
//	vpfw50_url = "https://www.pahoo.org/draft/20210315073848_0_VPFW50_130000.xml";
//	vpfd51_url = "https://www.pahoo.org/draft/20210315073539_0_VPFD51_130000.xml";
	// ★

	*vpfd51 = vpfd51_url;
	*vpfw50 = vpfw50_url;
}

/**
 * 気象庁防災情報XMLから天気予報情報を読み込む
 * @param	string station  予報地点コード
 * @param	int    forecast 0：天気予報，1：週間天気予報（省略時：1）
 *					0のとき‥‥jmaWeeklyWeather[0](本日)〜[6](6日後)に代入
 *					1のとき‥‥jmaWeeklyWeather[0](本日)〜[2](2日後)に代入
 * @return	bool TRUE：成功／FALSE：失敗
*/
bool pahooWeather::jmaGetWeatherForecast(string station, int forecast) {
	// 曜日
	static wstring week_name[] = {_SW("日"), _SW("月"), _SW("火"), _SW("水"), _SW("木"), _SW("金"), _SW("土")};

	// マッチングパターン
	static regex re11("([0-9]+)\\-([0-9]+)\\-([0-9]+)");				// 年月日
	static regex re12("([0-9]+)\\-([0-9]+)\\-([0-9]+)T([0-9]+)\\:");	// 年月日時
	static wregex re21(_SW("から"));								// 降水確率
	static wregex re31(_SW("最低気温"));							// 最低気温
	static wregex re32(_SW("^日中の最高気温"));						// 最高気温
	static regex re41("^[0-9]+$");									// 正数
	static smatch  mt1;
	static wsmatch mt2;
	int n;

	// 電文コード
	string code;
	if (forecast == 0) {
		code = "VPFD51";
	} else {
		code = "VPFW50";
	}

	// 予報地点コードの取得
	bool res = FALSE;
	int id;
	for (id = 0; id < SIZE_SPOTS; id++) {
		if (Spots[id] == NULL) {
			break;
		}
		if ((Spots[id]->code == code) && (Spots[id]->stationCode == station)) {
			res = TRUE;
			break;
		}
	}
	if (res == FALSE) {
		errmsg = _SW("予\報地点コードが見つかりません");
		return FALSE;
	}

	// 初日のみ初期化
	time_t now = time(NULL);
	struct tm* pnow = localtime(&now);
	namespace gr = boost::gregorian;
	gr::date dt(pnow->tm_year + 1900, pnow->tm_mon + 1, pnow->tm_mday);
	int i = 0, dd = 0, d2 = 0;
	// 天気予報アイコンを夜間にするかどうか
	int mode = 0;
	if (pnow->tm_hour >= 18) {
		mode = 1;
	}

	this->jmaWeeklyWeather[i].year  = dt.year();
	this->jmaWeeklyWeather[i].month = dt.month();
	this->jmaWeeklyWeather[i].day   = dt.day();
	this->jmaWeeklyWeather[i].day_of_week = week_name[dt.day_of_week()];
	this->jmaWeeklyWeather[i].stationName = Spots[id]->stationName;
	this->jmaWeeklyWeather[i].weather  = L"";
	this->jmaWeeklyWeather[i].image    = "";
	this->jmaWeeklyWeather[i].rainy    = "";
	this->jmaWeeklyWeather[i].temp_max = "";
	this->jmaWeeklyWeather[i].temp_min = "";

	// 最新の週間天気予報情報URLを取得
	long page           = stol(Spots[id]->page);
	string areaCode     = Spots[id]->areaCode;
	string stationCode  = Spots[id]->stationCode;
	wstring stationName = Spots[id]->stationName;
	wstring location    = Spots[id]->location;

	string vpfd51="", vpfw50="";
	jmaGetWeatherForecastURL(page, &vpfd51, &vpfw50);
	if (errmsg != L"") {
		return FALSE;
	}

	// VPFW51（府県週間天気予報）の解析
	string contents = "";
	if (forecast == 1) {
		// XML読み込み
		if (pCC->load(vpfw50, &contents) == FALSE) {
			errmsg = pCC->getError();
			return FALSE;
		}
		try {
			std::stringstream ss;
			ss << contents;
			ptree pt;
			xml_parser::read_xml(ss, pt);

			// XML解釈
			try {
				// 年月日取得
				for (auto it : pt.get_child("Report.Body.MeteorologicalInfos.TimeSeriesInfo.TimeDefines")) {
					if (optional<string>dti = it.second.get_optional<string>("DateTime")) {
						if (regex_search(dti.value(), mt1, re11)) {
							optional<string>tid = it.second.get_optional<string>("<xmlattr>.timeId");
//							cout << tid.value() << " : " << mt1[0].str() << endl;
							// 最初の情報は何時か
							i = stoi(tid.value());
							gr::date dt2(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));

							if (i == 1) {
								if (dt < dt2) {
									dd = 0;			// 明日
								} else if (dt == dt2) {
									dd = (-1);		// 今日
								} else {
									dd = (-2);		// 明日
								}
							}
							i += dd;
							if (i < 0) {
								continue;
							}
							// 予報1日分の初期化
							gr::date dt(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
							this->jmaWeeklyWeather[i].year  = stoi(mt1[1].str());
							this->jmaWeeklyWeather[i].month = stoi(mt1[2].str());
							this->jmaWeeklyWeather[i].day   = stoi(mt1[3].str());
							this->jmaWeeklyWeather[i].day_of_week = week_name[dt.day_of_week()];
							this->jmaWeeklyWeather[i].stationName = Spots[id]->stationName;
							this->jmaWeeklyWeather[i].weather  = L"";
							this->jmaWeeklyWeather[i].image    = "";
							this->jmaWeeklyWeather[i].rainy    = "";
							this->jmaWeeklyWeather[i].temp_max = "";
							this->jmaWeeklyWeather[i].temp_min = "";
						}
					}
				}

				for (auto it : pt.get_child("Report.Body")) {
					// 天気・降水確率の取得
					if (optional<string>type = it.second.get_optional<string>("<xmlattr>.type")) {
						if (_UW(type.value()) == _SW("区域予\報")) {
							for (auto it2 : it.second.get_child("TimeSeriesInfo")) {
								if (optional<string>acode = it2.second.get_optional<string>("Area.Code")) {
									if (acode.value() != areaCode) {
										continue;
									}
								}
								for (auto it3 : it2.second.get_child("")) {
									if (optional<string>type = it3.second.get_optional<string>("Property.Type")) {
										if (_UW(type.value()) == _SW("天気")) {
											// 天気予報の取得
											for (auto it4 : it3.second.get_child("Property.WeatherPart")) {
												if (optional<string>we = it4.second.get_optional<string>("")) {
													i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
													if (i < 0) {
														continue;
													}
													this->jmaWeeklyWeather[i].weather = jmaShortWeather(_UW(we.value()));
												}
											}
											// 天気予報用テロップ番号の取得
											for (auto it4 : it3.second.get_child("Property.WeatherCodePart")) {
												if (optional<string>we = it4.second.get_optional<string>("")) {
													i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
													if (i < 0) {
														continue;
													}
													if (i == 0) {
														this->jmaWeeklyWeather[i].image = jma_telop2url(stoi(we.value()), mode);
													} else {
														this->jmaWeeklyWeather[i].image = jma_telop2url(stoi(we.value()), 0);
													}
												}
											}
										} else if (_UW(type.value()) == _SW("降水確率")) {
											// 降水確率の取得
											for (auto it4 : it3.second.get_child("Property.ProbabilityOfPrecipitationPart")) {
												if (optional<string>we = it4.second.get_optional<string>("")) {
													i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
													if (i < 0) {
														continue;
													}
													this->jmaWeeklyWeather[i].rainy = we.value();
												}
											}
										}
									}
								}
							}
						} else if (_UW(type.value()) == _SW("地点予\報")) {
							for (auto it2 : it.second.get_child("TimeSeriesInfo")) {
								if (optional<string>scode = it2.second.get_optional<string>("Station.Code")) {
									if (scode.value() != stationCode) {
										continue;
									}
								}
								for (auto it3 : it2.second.get_child("")) {
									for (auto it4 : it3.second.get_child("")) {
										if (optional<string>type = it4.second.get_optional<string>("Type")) {
											if (_UW(type.value()) == _SW("最低気温")) {
												// 最低気温の取得
												for (auto it5 : it4.second.get_child("TemperaturePart")) {
													if (optional<string>we = it5.second.get_optional<string>("")) {
														i = stoi(it5.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
														if (i < 0) {
															continue;
														}
														this->jmaWeeklyWeather[i].temp_min = we.value();
													}
												}
											} else if (_UW(type.value()) == _SW("最高気温")) {
												// 最高気温の取得
												for (auto it5 : it4.second.get_child("TemperaturePart")) {
													if (optional<string>we = it5.second.get_optional<string>("")) {
														i = stoi(it5.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
														if (i < 0) {
															continue;
														}
														this->jmaWeeklyWeather[i].temp_max = we.value();
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}

			// XML解釈エラー
			} catch(ptree_bad_path& e) {
				errmsg = _SW("府県週間天気予\報情報を取得できません");
				return FALSE;
			}
		// 読み込みエラー
		} catch(xml_parser_error& e) {
			errmsg = _SW("府県週間天気予\報情報を取得できません");
			return FALSE;
		}
	}

	// VPFD51（府県天気予報 R1）の解析
	code = "VPFD51";
	// 予報地点コードの取得
	res = FALSE;
	for (id = 0; id < SIZE_SPOTS; id++) {
		if (Spots[id] == NULL) {
			break;
		}
		if ((Spots[id]->code == code) && (Spots[id]->stationCode == station)) {
			res = TRUE;
			break;
		}
	}
	if (res == FALSE) {
		errmsg = _SW("予\報地点コードが見つかりません");
		return FALSE;
	}
	areaCode = Spots[id]->areaCode;

	string rain_table[8] = { "-", "-", "-", "-", "-", "-", "-", "-", };
	int temp_table[5] = { 0, 0, 0, 0, 0 };
	// XML読み込み
	string contents2 = "";
	if (pCC->load(vpfd51, &contents2) == FALSE) {
		errmsg = pCC->getError();
		return FALSE;
	}
	try {
		std::stringstream ss;
		ss << contents2;
		ptree pt;
		xml_parser::read_xml(ss, pt);

		// XML解釈
		try {
			// 年月日取得
			for (auto it : pt.get_child("Report.Body")) {
				if (optional<string>type = it.second.get_optional<string>("<xmlattr>.type")) {
					if (_UW(type.value()) == _SW("区域予\報")) {
						// 日時
						for (auto it2 : it.second.get_child("")) {
							for (auto it3 : it2.second.get_child("")) {
								for (auto it4 : it3.second.get_child("")) {
									if (optional<string>name = it4.second.get_optional<string>("Name")) {
										wstring ws = _UW(name.value());

										if (regex_search(ws, mt2, re21)) {
											string ss = it4.second.get_optional<string>("DateTime").value();
											regex_search(ss, mt1, re12);
											optional<string>tid = it4.second.get_optional<string>("<xmlattr>.timeId");
//											cout << tid.value() << " : " << mt1[0].str() << endl;
											// 最初の情報は何時か
											i = stoi(tid.value());
											gr::date dt2(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
											if (i == 1) {
												if (dt < dt2) {
													dd = 0;			// 明日
												} else if (dt == dt2) {
													dd = (-1);		// 今日
												} else {
													dd = (-2);		// 明日
												}
												d2 = stoi(mt1[4].str()) / 6;		// 6時間毎
												if (dd == (-2)) {
													d2 = d2 - 4 + 1;
												}
											}
										} else if (optional<string>s2 = it4.second.get_optional<string>("DateTime")) {
											string ss = s2.value();
											if (regex_search(ss, mt1, re11)) {
												// 最初の情報は何時か
												optional<string>tid = it4.second.get_optional<string>("<xmlattr>.timeId");
												i = stoi(tid.value());
												gr::date dt2(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
												if (i == 1) {
													if (dt < dt2) {
														dd = 0;			// 明日
													} else if (dt == dt2) {
														dd = (-1);		// 今日
													} else {
														dd = (-2);		// 明日
													}
												}
											}
										}
									}
								}
							}
							// 地方コードは上何桁で判定するか
							bool flag = FALSE;
							for (n = 5; n >= 1; n--) {
								for (auto it3 : it2.second.get_child("")) {
									if (optional<string>code = it3.second.get_optional<string>("Area.Code")) {
										if (code.value().substr(0, n) != areaCode.substr(0, n)) {
											continue;
										}
										flag = TRUE;
									}
								}
								if (flag) {
									break;
								}
							}
//							cout <<  "N = " << n << endl;
//							cout <<  "areaCode = " << areaCode << endl;

							// 天気予報
							for (auto it3 : it2.second.get_child("")) {
								if (optional<string>code = it3.second.get_optional<string>("Area.Code")) {

									if (code.value().substr(0, n) != areaCode.substr(0, n)) {
										continue;
									}
								}
//								cout << it3.first << endl;
								if (optional<string>type = it3.second.get_optional<string>("Kind.Property.Type")) {
									if (_UW(type.value()) == _SW("天気")) {
										// 天気予報の取得
										for (auto it4 : it3.second.get_child("Kind.Property.WeatherPart")) {
											i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
											if (i < 0) {
												continue;
											}
											this->jmaWeeklyWeather[i].weather = jmaShortWeather(_UW(it4.second.get_optional<string>("").value()));
										}
										// 天気予報用テロップ番号の取得
										for (auto it4 : it3.second.get_child("Kind.Property.WeatherCodePart")) {
											i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
											if (i < 0) {
												continue;
											}
											if (i == 0) {
												this->jmaWeeklyWeather[i].image = jma_telop2url(stoi(it4.second.get_optional<string>("").value()), mode);
											} else {
												this->jmaWeeklyWeather[i].image = jma_telop2url(stoi(it4.second.get_optional<string>("").value()), 0);
											}
										}

									} else if (_UW(type.value()) == _SW("降水確率")) {
										// 降水確率の取得
										for (auto it4 : it3.second.get_child("Kind.Property.ProbabilityOfPrecipitationPart")) {
											i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd + d2;
											if (i < 0) {
												continue;
											}
											rain_table[i] = it4.second.get_optional<string>("").value();
										}
									}
								}
							}
						}
					} else if (_UW(type.value()) == _SW("地点予\報")) {
						// 日時
						int day0 = 0;
						for (auto it2 : it.second.get_child("")) {
							for (auto it3 : it2.second.get_child("")) {
								for (auto it4 : it3.second.get_child("")) {
									if (optional<string>s2 = it4.second.get_optional<string>("DateTime")) {
										string ss = s2.value();
										if (regex_search(ss, mt1, re11)) {
											// 最初の情報は何時か
											optional<string>tid = it4.second.get_optional<string>("<xmlattr>.timeId");
											i = stoi(tid.value());
											gr::date dt2(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
											if (i == 1) {
												if (dt < dt2) {
													dd = 0;			// 明日
												} else if (dt == dt2) {
													dd = (-1);		// 今日
												} else {
													dd = (-2);		// 明日
												}
												temp_table[i] = 0;
												day0 = stoi(mt1[3]);
											} else {
												if (day0 == stoi(mt1[3])) {
													temp_table[i] = 0;
												} else {
													temp_table[i] = 1;
												}
												day0 = stoi(mt1[3]);
											}
										}
									}
								}
							}
							// 最低気温・最高気温
							i = 1 + dd;
							for (auto it3 : it2.second.get_child("")) {
								if (optional<string>code = it3.second.get_optional<string>("Station.Code")) {

									if (code.value() != stationCode) {
										continue;
									}
								}
								// 最高気温・最低気温
								for (auto it4 : it3.second.get_child("")) {
									for (auto it5 : it4.second.get_child("")) {
										if (optional<string>ts = it5.second.get_optional<string>("Type")) {
											wstring ws = _UW(ts.value());
//											cout << _WS(ws) << endl;
											if (regex_search(ws, mt2, re31)) {
												int i2 = stoi(it5.second.get_optional<string>("TemperaturePart.jmx_eb:Temperature.<xmlattr>.refID").value());
												i += temp_table[i2];
												if (i < 0) {
													continue;
												}
												this->jmaWeeklyWeather[i].temp_min = it5.second.get_optional<string>("TemperaturePart.jmx_eb:Temperature").value();
											} else if (regex_search(ws, mt2, re32)) {
												int i2 = stoi(it5.second.get_optional<string>("TemperaturePart.jmx_eb:Temperature.<xmlattr>.refID").value());
												i += temp_table[i2];
												if (i < 0) {
													continue;
												}
												this->jmaWeeklyWeather[i].temp_max = it5.second.get_optional<string>("TemperaturePart.jmx_eb:Temperature").value();
											}
										}
									}
								}
							}
						}
					}
				}
			}

		// XML解釈エラー
		} catch(ptree_bad_path& e) {
			errmsg = _SW("府県天気予\報情報を取得できません");
			return FALSE;
		}
	// 読み込みエラー
	} catch(xml_parser_error& e) {
		errmsg = _SW("府県天気予\報情報を取得できません");
		return FALSE;
	}

	// 降水確率を天気予報情報へ
	for (int j = 0; j < 8; j++) {
		if ((dd == (-2)) && (j >= 4))	break;
		i = floor(j / 4.0);
		if (regex_search(jmaWeeklyWeather[i].rainy, mt1, re41)) {
			this->jmaWeeklyWeather[i].rainy = "";
		}
		this->jmaWeeklyWeather[i].rainy += rain_table[j];
		if (j % 4 != 3) {
			this->jmaWeeklyWeather[i].rainy += "/";
		}
	}

/** debug code
	for (int j = 0; j <= 7; j++) {
		cout << j << ":"
				<< _WS(this->jmaWeeklyWeather[j].stationName) << ":"
				<< this->jmaWeeklyWeather[j].year << "/"
				<< this->jmaWeeklyWeather[j].month << "/"
				<< this->jmaWeeklyWeather[j].day << "("
				<< _WS(this->jmaWeeklyWeather[j].day_of_week) << ") "
				<< _WS(this->jmaWeeklyWeather[j].weather) << " "
				<< this->jmaWeeklyWeather[j].image << " "
				<< this->jmaWeeklyWeather[j].rainy << "% "
				<< this->jmaWeeklyWeather[j].temp_max << "/"
				<< this->jmaWeeklyWeather[j].temp_min << "℃ "
		<< endl;
	}
*/

	contents.clear();
	contents2.clear();

	return TRUE;
}

/*
 ** バージョンアップ履歴 =====================================================
 *
 * @version 2.5.0 2025/03/22 readWebContents()引数追加に伴う変更
 * @version 2.4.0 2025/02/23 getJmaNearSpot() -- 引数 distanceMax 追加
 * @version 2.3.0 2024/05/06 isExistsNewerFileSpots(), updateFileSpots()追加
 * @version 2.2.1 2024/04/29 64ビット対応
 * @version 2.2   2022/03/12 気象庁防災情報XMLのhttps化に対応
 * @version 2.1   2021/04/14 キャッシュ・システム導入：pahooCacheクラス
 * @version 2.0   2021/03/16 気象庁防災情報XMLから情報取得に変更
 * @version 1.2   2020/12/20 bug-fix
 * @version 1.1   2020/12/05 _jma_readWeeklyWeather() 月日取得方式変更
 * @version 1.0   2020/10/10 初版
 */
