/** mystrings.cpp
 * 汎用文字列処理関数など（ソース部）
 *
 * @copyright	(c)studio pahoo
 * @author		パパぱふぅ
 * @動作環境	MinGW C++
 * @参考URL		http://www.pahoo.org/e-soul/webtech/cpp01-12-01.shtm
 */
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <time.h>
#include <string>
#include <vector>
#include <locale>
#include <regex>
#include <winsock2.h>
#include <windows.h>
#include <shlwapi.h>
#include <curl/curl.h>
#include "mystrings.h"

using namespace std;

// 外部変数 ================================================================
// 親ウィンドウ
extern HWND hParent;

// ファイル処理関連 ========================================================
/**
 * 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	string パス／""：取得失敗
 */
string getModulePath(void) {
	// バッファを確保して実行可能ファイルのパスを取得する．
	TCHAR buffer[MAX_PATH];
	GetModuleFileName(NULL, buffer, MAX_PATH);

	// パスからディレクトリを取得する．
	std::string _fullPath(buffer);
	std::string::size_type pos = _fullPath.find_last_of("\\/");
	if (pos != std::string::npos) {
		std::string directory = _fullPath.substr(0, pos);
		return directory;
	} else {
		return "";
	}
}

// クリップボード関連 ======================================================
/**
 * クリップボードに文字列をセット
 * @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;
}

// Windowsバージョン取得 ===================================================
typedef void (WINAPI *RtlGetVersion_FUNC)(OSVERSIONINFOEXW*);

/**
 * Windowsバージョン取得
 * @param	OSVERSIONINFOEX* os バージョン情報を格納
 * @return	bool TRUE/FALSEなし
 */
BOOL GetVersion2(OSVERSIONINFOEX* os) {
	HMODULE hMod;
	RtlGetVersion_FUNC func;
#ifdef UNICODE
	OSVERSIONINFOEXW* osw = os;
#else
	OSVERSIONINFOEXW o;
	OSVERSIONINFOEXW* osw = &o;
#endif

	hMod = LoadLibrary(TEXT("ntdll.dll"));
	if (hMod) {
		func = (RtlGetVersion_FUNC)GetProcAddress(hMod, "RtlGetVersion");
		if (func == 0) {
			FreeLibrary(hMod);
			return FALSE;
		}
		ZeroMemory(osw, sizeof(*osw));
		osw->dwOSVersionInfoSize = sizeof(*osw);
		func(osw);
#ifndef UNICODE
		os->dwBuildNumber = osw->dwBuildNumber;
		os->dwMajorVersion = osw->dwMajorVersion;
		os->dwMinorVersion = osw->dwMinorVersion;
		os->dwPlatformId = osw->dwPlatformId;
		os->dwOSVersionInfoSize = sizeof(*os);
//		DWORD sz = sizeof(os->szCSDVersion);
		WCHAR* src = osw->szCSDVersion;
		unsigned char* dtc = (unsigned char*)os->szCSDVersion;
		while (*src) {
			*dtc++ = (unsigned char)*src++;
		}
		*dtc = '\0';
#endif

	} else {
		return FALSE;
	}
	FreeLibrary(hMod);

return TRUE;
}

// 文字コード変換 =========================================================
/**
 * テキスト・コード変換：SJIS→wstring（Windows API使用）
 * @param  string src SJISテキスト
 * @return string 変換後テキスト
*/
std::wstring __sjis_wstring(std::string src) {
	auto const dest_size = ::MultiByteToWideChar(CP_ACP, 0U, src.data(), -1, nullptr, 0U);
	std::vector<wchar_t> dest(dest_size, L'\0');

	if (::MultiByteToWideChar(CP_ACP, 0U, src.data(), -1, dest.data(), dest.size()) == 0) {
		throw std::system_error{static_cast<int>(::GetLastError()), std::system_category()};
	}
	dest.resize(std::char_traits<wchar_t>::length(dest.data()));
	dest.shrink_to_fit();

	return std::wstring(dest.begin(), dest.end());
}
#define _SW(s)	__sjis_wstring(s)

/**
 * テキスト・コード変換：wstring→SJIS（Windows API使用）
 * @param  wstring src テキスト
 * @return string 変換後テキスト（SJIS）
*/
std::string __wstring_sjis(std::wstring src) {
	auto const dest_size = ::WideCharToMultiByte(CP_ACP, 0U, src.data(), -1, nullptr, 0, nullptr, nullptr);
	std::vector<char> dest(dest_size, '\0');
	if (::WideCharToMultiByte(CP_ACP, 0U, src.data(), -1, dest.data(), dest.size(), nullptr, nullptr) == 0) {
		throw std::system_error{static_cast<int>(::GetLastError()), std::system_category()};
	}
	dest.resize(std::char_traits<char>::length(dest.data()));
	dest.shrink_to_fit();

	return std::string(dest.begin(), dest.end());
}
#define _WS(s)	__wstring_sjis(s)

/**
 * テキスト・コード変換：UTF-8→wstring（Windows API使用）
 * @param  string src UTF-8テキスト
 * @return string 変換後テキスト
*/
std::wstring __utf8_wstring(std::string src) {
	auto const dest_size = ::MultiByteToWideChar(CP_UTF8, 0U, src.data(), -1, nullptr, 0U);
	std::vector<wchar_t> dest(dest_size, L'\0');
	if (::MultiByteToWideChar(CP_UTF8, 0U, src.data(), -1, dest.data(), dest.size()) == 0) {
		throw std::system_error{static_cast<int>(::GetLastError()), std::system_category()};
	}
	dest.resize(std::char_traits<wchar_t>::length(dest.data()));
	dest.shrink_to_fit();

	return std::wstring(dest.begin(), dest.end());
}
#define _UW(s)	__utf8_wstring(s)

/**
 * テキスト・コード変換：wstring→UTF-8（Windows API使用）
 * @param  wstring src テキスト
 * @return string 変換後テキスト（UTF-8）
*/
std::string __wstring_utf8(std::wstring src) {
	auto const dest_size = ::WideCharToMultiByte(CP_UTF8, 0U, src.data(), -1, nullptr, 0, nullptr, nullptr);
	std::vector<char> dest(dest_size, '\0');

	if (::WideCharToMultiByte(CP_UTF8, 0U, src.data(), -1, dest.data(), dest.size(), nullptr, nullptr) == 0) {
		throw std::system_error{static_cast<int>(::GetLastError()), std::system_category()};
	}
	dest.resize(std::char_traits<char>::length(dest.data()));
	dest.shrink_to_fit();

	return std::string(dest.begin(), dest.end());
}
#define _WU(s)	__wstring_utf8(s)

/**
 * テキスト・コード変換：SJIS→UTF-8（Windows API使用）
 * @param  wstring src SJISテキスト
 * @return string 変換後テキスト（UTF-8）
*/
std::string sjis_utf8(std::string src) {
	wstring const wide = __sjis_wstring(src);
	return __wstring_utf8(wide);
}

/**
 * テキスト・コード変換：UTF-8→SJIS（Windows API使用）
 * @param  wstring src UTF-8テキスト
 * @return string 変換後テキスト（UTF-8）
*/
std::string utf8_sjis(std::string src) {
	wstring const wide = __utf8_wstring(src);
	return __wstring_sjis(wide);
}
/**
 * 日本語テキスト変換（Win32 API利用）
 * @param	wstring sour   変換元テキスト
 * @param	DWORD   method 変換方式
 * @return	wstring 変換後テキスト
 */
wstring wconvString(wstring wsour, DWORD method) {
	// 濁点・半濁点は全角→半角で2文字に増えるので変換後領域は余裕を持たせる
	wchar_t wdest[wsour.length() * 2 + 1];
	for (size_t i = 0; i < wsour.length() * 2 + 1; i++) {
		wdest[i] = 0L;
	}
	// 変換実行
	LCMapStringW(LOCALE_SYSTEM_DEFAULT, method, (LPWSTR) wsour.c_str(),
			wsour.length(), (LPWSTR) wdest, wsour.length() * 2);

	return (wstring) wdest;
}

/**
 * 数字（wstring）を数値（double）に変換
 * @param	wstring sour   変換元テキスト
 * @return	double  変換後数値
 */
double w2f(wstring sour) {
	return atof(_WS(wconvString(sour, LCMAP_HALFWIDTH)).c_str());
}

/**
 * HTMLタグを取り除く
 * @param  wstring html  HTMLコンテンツ
 * @return wstring タグを取り除いた後のテキスト
 */
wstring wstrip_tags(wstring html) {
	bool inflag = FALSE;
	bool done = FALSE;
	size_t i, j;
	while (!done) {
		if (inflag) {
			i = html.find('>');
			if (i != wstring::npos) {
				inflag = FALSE;
				html.erase(0, i + 1);
			} else {
				done = TRUE;
				html.erase();
			}
		} else {
			i = html.find('<');
			if (i != wstring::npos) {
				j = html.find('>');
				if (j == string::npos) {
					inflag = TRUE;
					done = TRUE;
					html.erase(i);
				} else {
					html.erase(i, j - i + 1);
				}
			} else {
				done = TRUE;
			}
		}
	}
	return html;
}

/**
 * ワイド文字列中の改行を他の文字列に置換する
 * @param  wstring str 置換対象の文字列
 * @param  wstring rep 置換文字列
 * @return wstring 置換後の文字列
 */
wstring wrepNL(wstring str, wstring rep) {
	wstring strRet;
	wstring::iterator ite = str.begin();
	wstring::iterator iteEnd = str.end();

	if (0 < str.size()) {
		wchar_t bNextChar = *ite++;
		while (1) {
			if (L'\r' == bNextChar) {
				// 改行確定
				strRet += rep;
				// EOF判定
				if (ite == iteEnd) {
					break;
				}
				// 1文字取得
				bNextChar = *ite++;
				if (L'\n' == bNextChar) {
					// EOF判定
					if (ite == iteEnd) {
						break;
					}
					// 1文字取得
					bNextChar = *ite++;
				}
			} else if (L'\n' == bNextChar) {
				// 改行確定
				strRet += rep;
				// EOF判定
				if (ite == iteEnd) {
					break;
				}
				// 1文字取得
				bNextChar = *ite++;
				if (L'\r' == bNextChar) {
					// EOF判定
					if (ite == iteEnd) {
						break;
					}
					// 1文字取得
					bNextChar = *ite++;
				}
			} else {
				// 改行以外
				strRet += bNextChar;
				// EOF判定
				if (ite == iteEnd) {
					break;
				}
				// 1文字取得
				bNextChar = *ite++;
			}
		};
	}
	return strRet;
}

/**
 * 文字列をデリミタで分割する（ワイド文字）
 * @param  wstring src  分割文字列
 * @param  wstring deim デリミタ
 * @return vector<wstring> 分割後ｎ文字列
 */
vector<wstring> wsplit(const wstring src, const wchar_t delim) {
	vector<wstring> vec;
	size_t len = src.length();
	size_t n;

	for (size_t i = 0; i < len; i = n + 1) {
		n = src.find_first_of(delim, i);
		if (n == wstring::npos) {
			n = len;
		}
		vec.push_back(src.substr(i, n - i));
	}
    return vec;
}

/**
 * ワイド文字列中の特殊文字をHTMLエンティティに変換する
 * @param  wstring str 置換対象の文字列
 * @return wstring 置換後の文字列
 */
wstring htmlspecialchars(wstring str) {
	wregex re1(_SW("\""));
	wregex re2(_SW("'"));
	wregex re3(_SW("&"));
	wregex re4(_SW("<"));
	wregex re5(_SW(">"));
	wsmatch mt;

	str = regex_replace(str, re1, L"&quot;");
	str = regex_replace(str, re2, L"&#039;");
	str = regex_replace(str, re3, L"&amp;");
	str = regex_replace(str, re4, L"&lt;");
	str = regex_replace(str, re5, L"&gt;");

	return str;
}

/**
 * ワイド文字列をURLエンコードする
 * @param  wstring str ワイド文字列
 * @return string エンコード結果
 */
string urlencode(wstring str) {
	static char buff1[SIZE_BUFF + 1], buff2[SIZE_BUFF + 1];

	strncpy(buff1, _WU(str).c_str(), SIZE_BUFF);
	CURL *curl = curl_easy_init();
	strncpy(buff2, curl_easy_escape(curl, (const char *)buff1, strlen(buff1)), SIZE_BUFF);
	curl_easy_cleanup(curl);

	return (string)buff2;
}

/**
 * cURL用コールバック関数
 */
size_t callBackFuncCURL(char* ptr, size_t size, size_t nmemb, string* stream) {
	int realsize = size * nmemb;
	stream->append(ptr, realsize);
	return realsize;
}

/**
 * cURLによるWebコンテンツ取得
 * @param  string url アクセスURL
 * @param  string ua  UserAgent
 * @param  string*    contents コンテンツを格納
 * @param  char*      post POST渡しする文字列（例 user=pahoo&psw=1234）
 * @param  int*       htpStatus HTTPステータスコードを格納する
 * @return bool TRUE：読み込み成功／FALSE：失敗
 */
bool readWebContents(const string url, const string ua, string* contents, int* httpStatus, char* post) {
	// cURLによる読み込み
	bool ret = FALSE;
	CURL *curl;
	CURLcode res = (CURLcode)0;
	*httpStatus = 0;
	char errbuf[CURL_ERROR_SIZE] = {0};

	// 前処理
	curl_global_init(CURL_GLOBAL_DEFAULT);
	curl = curl_easy_init();

	if (curl) {
		struct curl_slist *hs=NULL;
		hs = curl_slist_append(hs, "Content-Type: application/x-www-form-urlencoded");
		// 基本設定
		curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);	// リダイレクト有効
		curl_easy_setopt(curl, CURLOPT_USERAGENT, ua.c_str());
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs);
		// TLS
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
		curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem");
		if (post != NULL) {
			curl_easy_setopt(curl, CURLOPT_POST, 1);
			curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
			curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(post));
		}
		// 受信
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callBackFuncCURL);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, contents);
		// エラー取得
		curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
		// 実行
		res = curl_easy_perform(curl);
	}
	// エラー・チェック，HTTPステータスコード取得
    if (res == CURLE_OK) {
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, httpStatus);
        ret = TRUE;
	}

	// 後処理
    curl_easy_cleanup(curl);
    curl_global_cleanup();

//	std::cerr << "cURL error: " << errbuf << "\n";

	return ret;
}

/**
 * テンポラリファイル名を取得（Windows API使用＋拡張子.gif）
 * @param	char* tpmname  テンポラリファイル名を格納（フルパス）
 * @return	なし
 */
void getTempFname(char *tmpname) {
	char szTempPath[MAX_PATH + 1];

	GetTempPath(sizeof(szTempPath) / sizeof(szTempPath[0]), szTempPath);
	GetTempFileName(szTempPath, "map", 0, tmpname);
	remove(tmpname);		// ごみファイル削除
	strcat(tmpname, ".html");
}

// 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];
	GetDlgItemText(hDlg, id, buff, sizeof(buff));
	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);
}

/*
 ** バージョンアップ履歴 =====================================================
 *
 * @version 1.4.0  2026/03/01 readWebContents() cURL 8.18.0対応
 * @version 1.3.1  2025/03/16 readWebContents() リダイレクト有効に
 * @version 1.3.0  2025/03/16 readWebContents() 引数httpStatus追加
 * @version 1.2.0  2024/05/06 getModulePath() 追加 
 * @version 1.12   2021/01/31 readWebContents() 引数post追加
 * @version 1.11   2020/10/17 htmlspecialchars() 追加
 * @version 1.1    2020/10/17 GetVersion2()追加，readWebContents() 引数ua追加
 * @version 1.01   2020/10/03 setClipboardData() bug-fix
 * @version 1.0    2020/09/22 初版
 */
