/** webbrowser.hpp
 * Webブラウザ・クラス
 *
 * @copyright	(c)studio pahoo
 * @author		パパぱふぅ
 * @動作環境	MinGW C++ + cwebpage.dll（Jeff Glatt）
 * @参考URL		http://www.pahoo.org/e-soul/webtech/cpp01-15-01.shtm
*/
#ifndef WEBBROWSER_H
#define WEBBROWSER_H
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <mshtml.h>
#include <exdisp.h>
#include <oleacc.h>
#include <string>
#include <locale>

typedef long WINAPI EmbedBrowserObjectPtr(HWND);
typedef long WINAPI UnEmbedBrowserObjectPtr(HWND);
typedef long WINAPI DisplayHTMLPagePtr(HWND, LPCTSTR);
typedef long WINAPI DisplayHTMLStrPtr(HWND, LPCTSTR);
typedef HRESULT WINAPI WaitOnReadyStatePtr(HWND, READYSTATE, DWORD, IWebBrowser2 *);
BOOL CALLBACK WebBrowserPProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

//WebBrowserコントロール・クラス
class WebBrowser {
	static bool dll_loaded;
	HINSTANCE cwebdll; 
	EmbedBrowserObjectPtr*		lpEmbedBrowserObject;
	UnEmbedBrowserObjectPtr*	lpUnEmbedBrowserObject;
	DisplayHTMLPagePtr*			lpDisplayHTMLPage;
	DisplayHTMLStrPtr*			lpDisplayHTMLStr;
	WaitOnReadyStatePtr*		lpWaitOnReadyState;
	HWND control, parent;

/**
 * 関数のエントリポイントを準備
 * @param	なし
 * @return	なし
*/
void loadOnce(void) {
	if (dll_loaded) {
		return;
	}
	cwebdll = LoadLibrary((LPCTSTR)"cwebpage.dll");
	if (cwebdll) {
		lpEmbedBrowserObject = (EmbedBrowserObjectPtr *)GetProcAddress((HINSTANCE)cwebdll, "EmbedBrowserObject");
		lpUnEmbedBrowserObject = (UnEmbedBrowserObjectPtr *)GetProcAddress((HINSTANCE)cwebdll, "UnEmbedBrowserObject");
		lpDisplayHTMLPage = (DisplayHTMLPagePtr *)GetProcAddress((HINSTANCE)cwebdll, "DisplayHTMLPage");
		lpDisplayHTMLStr = (DisplayHTMLStrPtr *)GetProcAddress((HINSTANCE)cwebdll, "DisplayHTMLStr");   
		lpWaitOnReadyState = (WaitOnReadyStatePtr *)GetProcAddress((HINSTANCE)cwebdll, "WaitOnReadyState"); 

		if (!lpEmbedBrowserObject || !lpUnEmbedBrowserObject
				|| !lpDisplayHTMLPage || !lpDisplayHTMLStr) {
			return;
		}
		dll_loaded = 1;
	}
}

public:
	double longitude, latitude;
	int zoom;
	std::wstring maptype;

//コンストラクタ
WebBrowser() {
//	HRESULT hr = ::CoInitialize(NULL);
	loadOnce();
}
WebBrowser(char *basic, int x, int y, int w, int h, HWND &par, int exStyle=WS_EX_CLIENTEDGE) {
	create(basic, x, y, w, h, par, exStyle);
}

//デストラクタ
~WebBrowser() {
	if (control) {
		(*lpUnEmbedBrowserObject)(control);
	}
	if (dll_loaded) {
		FreeLibrary(cwebdll);
	}
//	::CoUninitialize();
}

/**
 * IES（InternetExplorer_Server）のハンドルを返す
 * @param	なし
 * @return	HWND ハンドル
*/
HWND getIES(void) {
	TCHAR className[128];
	HWND hChild = GetWindow(control, GW_CHILD);
	while (hChild) {
		GetClassName(hChild, className, sizeof(className));
		if (_tcscmp(_T("Internet Explorer_Server"), className) == 0) {
			return hChild;
		}
		hChild = GetWindow(hChild, GW_CHILD);
	}
	return NULL;
}

/**
 * Webブラウザが準備できるまで待つ
 * @param	int msec 待ち自慢（ミリ秒）
 * @return	なし
*/
void waitOnReadyState(int msec) {
	(*lpWaitOnReadyState)(control, READYSTATE_COMPLETE, msec, NULL);
}

/**
 * Webブラウザ・コントロールを生成する
 * @param	char *basic  名称
 * @param	int x, y 開始XY座標
 * @param	int w, h ウィンドウの幅、高さ
 * @return	HWND ハンドル
 * @return	int exStyle 拡張スタイル
 * @return	なし
*/
void create(char *basic, int x, int y, int w, int h, HWND &par, int exStyle=WS_EX_CLIENTEDGE) {
	loadOnce();
	parent = par;
	control = CreateWindowEx(0, WC_DIALOG, NULL, WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN, x, y, w, h, parent, NULL, NULL, NULL);
	if (control) {
		SetWindowLongA(control, DWLP_DLGPROC, (WPARAM)WebBrowserPProc);
		SetWindowLongA(control, GWLP_USERDATA, (WPARAM)this);
		if ((*lpEmbedBrowserObject)(control)) {
			return;
		}
		HWND br = FindWindowEx(control, NULL, NULL, NULL);
		if (br) {
			//SetWindowLong(br, GWL_EXSTYLE, exStyle);
			//SetWindowPos(br, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER);	                       
		}
	}
}

/**
 * Webブラウザ・コントロールのサイズ変更
 * @param	int w, h ウィンドウの幅、高さ
 * @return	なし
*/
void resize(int w, int h) {
	MoveWindow(control, 0, 0, w, h, TRUE);
	UpdateWindow(control);
}

/**
 * Webブラウザ・コントロールの移動
 * @param	int x, y 移動先XY座標
 * @return	なし
*/
void move(int x, int y) {
	RECT rcWindow;
	GetWindowRect(control, &rcWindow);
	MoveWindow(control, x, y, x + rcWindow.right, y + rcWindow.bottom, TRUE);
	UpdateWindow(control);
}

void fitToParent() {
	RECT rcWindow;
	GetClientRect(parent, &rcWindow);
	MoveWindow(control, 0, 0, rcWindow.right, rcWindow.bottom, TRUE);
	UpdateWindow(control);
}

/**
 * Webブラウザ・コントロールにHTMLコンテンツ表示
 * @param	char *_txt HTMLコンテンツ
 * @return	なし
*/
void setString(char *_txt) {
	(*lpDisplayHTMLStr)(control,(LPCTSTR)_txt);
}

/**
 * Webブラウザ・コントロールにURLコンテンツ表示
 * @param	char *_url URL
 * @return	なし
*/
void loadPage(char *_url) {
	(*lpDisplayHTMLPage)(control,(LPCTSTR)_url);
}

/**
 * Webブラウザ・コントロールにある要素を取り出す（ID指定）
 * @param	wstring id ID
 * @param	IHTMLElement** element 要素を格納
 * @return	bool TRUE：要素があった／なかった
*/
bool getIHTMLElementById(std::wstring id, IHTMLElement** pElement) {
	HWND iES = getIES();
	static const UINT WM_HTML_GETOBJECT = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT"));
	LRESULT res = 0;
	SendMessageTimeout(iES, WM_HTML_GETOBJECT, 0, 0, SMTO_ABORTIFHUNG, 1000, reinterpret_cast<PDWORD_PTR>(&res));
	if (! res) {
		return FALSE;
	}

	//DLL 内のエクスポート関数のアドレスを取得
	HINSTANCE hInstance = LoadLibrary(_T("oleacc.dll"));
	LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hInstance, "ObjectFromLresult" );
	if (pfObjectFromLresult == NULL) {
		return FALSE;
	}

	 //IHTMLDocument3 のポインタを取得
	IHTMLDocument3* pHTMLDocument3;
	(*pfObjectFromLresult)(res, IID_IHTMLDocument3, 0, (void **)&pHTMLDocument3);
	if (! pHTMLDocument3) {
		return FALSE;
	}

	 //IHTMLElement のポインタを取得
	BSTR bstrId;
	IHTMLElement* element;
	bstrId = SysAllocString(id.c_str());
	pHTMLDocument3->getElementById(bstrId, &element);

	*pElement = element;
	if (! element) {
		return FALSE;
	}
	return TRUE;
}

/**
 * Webブラウザ・コントロールにあるINPUT要素のValueを取り出す（ID指定）
 * @param	wstring id ID
 * @param	wstring *val Valueを格納
 * @return	bool TRUE：要素があった／なかった
*/
bool getInputById(std::wstring id, std::wstring* val) {
	IHTMLElement* pElement;
	BSTR bstrAttribute;
	VARIANT var;

	if (getIHTMLElementById(id, &pElement) == FALSE) {
		return FALSE;
	}
	bstrAttribute = SysAllocString(L"value");
	VariantInit(&var);
	pElement->getAttribute(bstrAttribute, 0, &var);

	*val = (std::wstring)var.bstrVal;
	if (*val == L"") {
		return FALSE;
	}
	return TRUE;
}
};
bool WebBrowser::dll_loaded(0);

/**
 * イベントハンドラ：Webブラウザ・コントロール
 * @param	HWND hwnd			親ウィンドウ・ハンドラ
 * @paramm	UINT message		メッセージ識別子
 * @param	WPARAM wParam		メッセージの最初のパラメータ
 * @paramL	PARAM lParam		メッセージの2番目のパラメータ
*/
BOOL CALLBACK WebBrowserPProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
	WebBrowser *me = reinterpret_cast<WebBrowser*>(GetWindowLongA(hwnd, GWLP_USERDATA));
	std::wstring val;
	if (me->getInputById(L"longitude", &val)) {
		me->longitude = stod(val);
	}
	if (me->getInputById(L"latitude", &val)) {
		me->latitude  = stod(val);
	}
	if (me->getInputById(L"zoom", &val)) {
		me->zoom  = stoi(val);
	}
	if (me->getInputById(L"maptype", &val)) {
		me->maptype = val;
	}
	if (message == WM_SIZE) {
		HWND br = FindWindowEx(hwnd, NULL, NULL, NULL);
		if (br) {
			MoveWindow(br, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
			UpdateWindow(br);
		}
	}
	return FALSE;
}
#endif

/*
 ** バージョンアップ履歴 =====================================================
 *
 * @version  1.0  2020/10/03
 */
