/** address2geowin.cpp
 * 住所などから緯度・経度を求める
 *
 * @copyright	(c)studio pahoo
 * @author		パパぱふぅ
 * @動作環境	MinGW C++ + cURL + OpenSSL + Boost C++ Libraries
 *					+ Google Geocoding API / YOLPコンテンツジオコーダAPI /
 *					HeartRails Geo API / OSM Nominatim Search API / 
 *					国土地理院ジオコーディングAPI
 * @参考URL		https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-14-01.shtm
 */
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <sstream>
#include <string>
#include <locale>
#include <winsock2.h>
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include <curl/curl.h>
#include <boost/format.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include "mystrings.h"
#include "apikey.h"
#include "pahooGeocode.hpp"
#include "resource.h"

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

// 定数など ==================================================================
#define MAKER		"pahoo.org"				// 作成者
#define APPNAME		"address2geowin"		// アプリケーション名
#define APPNAMEJP	"住所などから緯度・経度を求める"
											// アプリケーション名（日本語）
#define APPVERSION	"1.3.2"					// バージョン
#define APPYEAR		"2020-25"				// 作成年
#define REFERENCE	"https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-14-01.shtm"	// 参考サイト

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

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

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

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

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

// 検索キー格納用
string Query;

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

// UserAgent
string UserAgent;

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

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

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

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

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

本アプリケーションは，下記のライブラリ，フレームワーク，APIを利用しています．
　MinGW(g++), Boost C++ライブラリ, cURL, OpenSSL，
　Google，Yahoo!JAPAN，HeartRails Geo API，OSM, 国土地理院ジオコーディングAPI
)")
% APPNAMEJP
% APPVERSION
% APPYEAR
).str();

// オプション読込／保存 ====================================================
/**
 * パラメータの初期化
 * @param	なし
 * @return	なし
 */
void initParameter(void) {
	Query     = "";
	hParent_X = 0;
	hParent_Y = 0;
}

/**
 * オプションの読み込み
 * @param	なし
 * @return	なし
 */
void loadParameter(void) {
	initParameter();
	ptree pt;

	// 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 == "query") {
					Query = utf8_sjis((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) {
			hParent_X = 0;
			hParent_Y = 0;
			Query = "";
			return;
		}
	// 読み込み失敗したら初期値設定
	} catch (xml_parser_error& e) {
		hParent_X = 0;
		hParent_Y = 0;
		Query = "";
		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", sjis_utf8(Query));
	child1.add("<xmlattr>.type", "query");
	ptree& child2 = pt.add("parameter.param", (string)to_string(hParent_X));
	child2.add("<xmlattr>.type", "wx");
	ptree& child3 = pt.add("parameter.param", (string)to_string(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	HWND hDlg	ウィンドウ・ハンドラ
 * @paramm	UINT uMsg
 * @param	WPARAM wParam
 * @paramL	PARAM lParam
 * @return	INT_PTR CALLBACK
*/
INT_PTR CALLBACK processHelp(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	string help;

	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	なし
 * @return	string 情報
 */
string makeInfo(void) {
	string lat = to_string(pGC->Ppoints[0].latitude);
	string lng = to_string(pGC->Ppoints[0].longitude);
	string address = _WS(pGC->Ppoints[0].address);
	string url = pGC->webapi;

	string info = (boost::format(R"(緯度：%1%
経度：%2%
住所：%3%
WebAPI：%4%

)")
% lat
% lng
% address
% url
).str();

	return info;
}

/**
 * 情報をTXTファイルに保存する
 * @param	const char* fname 保存ファイル名
 * @return	なし
 */
void __saveTXT(const char *fname) {
	ofstream outputfile(fname);

	outputfile << makeInfo() << endl;

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

/**
 * 情報をTXTファイルに保存する
 * @param	なし
 * @return	なし
 */
void saveTXT(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");
	// コモンダイアログの表示
	GetSaveFileName(&of);
	// ファイル保存
	__saveTXT(fname);
}

/**
 * イベントハンドラ：メインウィンドウ
 * @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;
	int cnt;

	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_QUERY, Query);
		// アプリケーション・ウィンドウ移動
		SetWindowPos(hParent, NULL, hParent_X, hParent_Y, 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER));
		break;

	// ボタン押下
	case WM_COMMAND:
		if (ErrorMessage != "")		break;
		switch (LOWORD(wParam)) {
		// 実行
		case IDM_EXEC:
		case IDC_BUTTON_EXEC:
			// カーソルを砂時計に
			SetCursor(LoadCursor(NULL, IDC_WAIT));
			Query = getStrEditBox(hDlg, IDC_EDIT_QUERY);
			if (Query.length() > 0) {
				// 緯度・経度検索
				cnt = pGC->searchPoints(_SW(Query), UserAgent, 0);
				setStrEditBox(hDlg, IDC_EDIT_LONGITUDE, to_string(pGC->Ppoints[0].longitude));
				setStrEditBox(hDlg, IDC_EDIT_LATITUDE, to_string(pGC->Ppoints[0].latitude));
				setStrEditBox(hDlg, IDC_EDIT_ADDRESS, _WS(pGC->Ppoints[0].address));
				setStrEditBox(hDlg, IDC_EDIT_WEBAPI, pGC->webapi);
				if (cnt == 0) {
					setStrEditBox(hDlg, IDC_EDIT_ADDRESS, "検索結果なし");
				} else if (pGC->isError()) {
					setStrEditBox(hDlg, IDC_EDIT_ADDRESS, _WS(pGC->getError()));
				}
			} else {
				setStrEditBox(hDlg, IDC_EDIT_ADDRESS, _WS(pGC->getError()));
			}
			pGC->resetError();
			break;
		// リセット
		case IDC_BUTTON_RESET:
		case IDM_RESET:
			setStrEditBox(hDlg, IDC_EDIT_QUERY, "");
			setStrEditBox(hDlg, IDC_EDIT_LONGITUDE, "");
			setStrEditBox(hDlg, IDC_EDIT_LATITUDE, "");
			setStrEditBox(hDlg, IDC_EDIT_ADDRESS, "");
			setStrEditBox(hDlg, IDC_EDIT_WEBAPI, "");
			pGC->resetError();
			break;
		// コピー
		case IDM_COPY:
		case IDC_BUTTON_COPY:
			setClipboardData(makeInfo());
			break;
		// 保存
		case IDC_BUTTON_SAVE:
		case IDM_SAVE:
			saveTXT();
			break;
		// GoogleAPIキー保存
		case IDM_GOOGLE_APIKEY:
			createSetAPIkey(hDlg, LABEL_GOOGLE_API, FNAME_GOOGLE_API, URL_GOOGLE_API, &pGC->GoogleAPIkey);
			pGC->readGoogleApiKey();
			break;
		// Yahoo!アプリケーションID保存
		case IDM_YAHOO_APIKEY:
			createSetAPIkey(hDlg, LABEL_YAHOO_API, FNAME_YAHOO_API, URL_YAHOO_API, &pGC->YahooAPIkey);
			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_QUIT:
			// パラメータ保存
			saveParameter();
			// オブジェクト解放
			delete pGC;
			EndDialog(hParent, 0);
			return 0;
		default:
			return 1;
		}
		break;

	// プログラム終了
	case WM_CLOSE:
		// パラメータ保存
		saveParameter();
		// オブジェクト解放
		delete pGC;
		EndDialog(hParent, 0);
		return 0;
	}
	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);

	hInst = hInstance;
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)processMain);
	return 0;
}

/*
 ** バージョンアップ履歴 =====================================================
 *
 * @version 1.3.2 2025/11/08 使用ライブラリ更新
 * @version 1.3.1 2025/07/17 使用ライブラリ更新
 * @version 1.3.0 2025/03/15 Leafletアクセス可否チェック追加，使用ライブラリ更新
 * @version 1.2.4 2024/11/30 使用ライブラリ更新
 * @version 1.2.3 2024/08/17 使用ライブラリ更新
 * @version 1.2.2 2024/03/23 使用ライブラリ更新
 * @version 1.2.1 2023/11/11 使用ライブラリ更新
 * @version 1.2.0 2023/07/02 国土地理院ジオコーディングAPIを使用出来るように
 * @version 1.1.2 2023/04/01 使用ライブラリ更新
 * @version 1.1.1 2022/11/20 使用ライブラリ更新
 * @version 1.1.0 2022/09/19 UserAgent追加，各種パラメータの保存
 * @version 1.0   2020/09/26 初版
 */
