#include "main.h"
#include "./httpgrab.h"


static HHOOK g_hook = NULL;
static HWND g_hook_host = NULL;
static HWND g_hook_fwd = NULL;
static DWORD g_hook_flags = 0;
static HWND *g_hook_phwnd =NULL;

static LRESULT CALLBACK TargetWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK LabelWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

static BOOL IsDialog(HWND hwnd)
{
	wchar_t szName[256] = {0};
	return  (GetClassNameW(hwnd, szName, sizeof(szName)/sizeof(wchar_t)) &&
				CSTR_EQUAL == CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 
									NORM_IGNORECASE, szName, -1, L"#32770", -1));
}

static LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam) 
{
	if (HCBT_CREATEWND == code && IsDialog((HWND)wParam) && ((CBT_CREATEWND*)lParam)->lpcs->hwndParent == g_hook_host)
	{
		LRESULT result;
		
		result = CallNextHookEx(g_hook, code, wParam, lParam); 
		UnhookWindowsHookEx(g_hook);
		g_hook = NULL;
	
		if (0 == result)
		{
			((CBT_CREATEWND*)lParam)->lpcs->style &= ~WS_VISIBLE;
			WNDPROC oldProc = (WNDPROC)SetWindowLongPtrW((HWND)wParam, GWLP_WNDPROC, (LONG_PTR)TargetWndProc);
			if (oldProc)
			{
				SetPropW((HWND)wParam, L"WNDPROC", oldProc);
				if (g_hook_fwd) SetPropW((HWND)wParam, L"FWDHWND", g_hook_fwd);
				if (g_hook_flags) SetPropW((HWND)wParam, L"FLAGS", (HANDLE)(INT_PTR)g_hook_flags);
				if (g_hook_phwnd) *g_hook_phwnd = (HWND)wParam;
			}
		}
		else if (g_hook_host && IsWindow(g_hook_host)) DestroyWindow(g_hook_host);

		g_hook_fwd = NULL;
		g_hook_flags = 0;
		g_hook_host = NULL;
		return result;
	}
	return CallNextHookEx(g_hook, code, wParam, lParam); 
}

HWND BeginGrabHTTPText(HWND hwndFwd, UINT flags, HWND *phwndTarget)
{	

	g_hook_host = CreateWindowExW(0x08000000/*WS_EX_NOACTIVATE*/ | WS_EX_NOPARENTNOTIFY, L"STATIC", L"", WS_DISABLED | WS_POPUP, -10000, -10000, 0, 0, NULL, NULL, NULL, NULL); 
	if (!g_hook_host) return NULL;

	g_hook = SetWindowsHookEx(WH_CBT, HookProc, NULL, GetCurrentThreadId());
	if (!g_hook)
	{
		if (g_hook_host) DestroyWindow(g_hook_host);
		g_hook_host = NULL;
		g_hook_fwd = NULL;
		g_hook_flags = 0;
		g_hook_phwnd = NULL;
	}
	else
	{
		g_hook_fwd = hwndFwd;
		g_hook_flags = flags;
		g_hook_phwnd = phwndTarget;
	}
	return g_hook_host;
}

void EndGrabHTTPText(HWND hwndHost)
{
	if (g_hook_host == hwndHost)
	{
		if (g_hook) UnhookWindowsHookEx(g_hook);
		g_hook = NULL;
		g_hook_host = NULL;
		g_hook_fwd = NULL;
		g_hook_flags = 0;
	}
	if (IsWindow(hwndHost)) DestroyWindow(hwndHost);
}

static LRESULT CALLBACK TargetWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	WNDPROC fnOldProc = (WNDPROC)GetPropW(hwnd, L"WNDPROC");
	
	switch(uMsg)
	{
		case WM_INITDIALOG:
			{
				HWND hwndFwd;

				hwndFwd = (HWND)GetPropW(hwnd, L"FWDHWND");
				DWORD flags = (DWORD)(INT_PTR)GetPropW(hwnd, L"FLAGS");
				RemovePropW(hwnd, L"FLAGS");
				RemovePropW(hwnd, L"FWDHWND");

				if (hwndFwd)
				{
					HWND hwndText;
					if (HTTPGRAB_USESTATUSTEXT & flags)
					{
						hwndText = GetDlgItem(hwnd, 1180/*IDC_STATUS*/);
						WNDPROC oldProc = (WNDPROC)SetWindowLongPtrW(hwndText, GWLP_WNDPROC, (LONG_PTR)LabelWndProc);
						if (oldProc) SetPropW(hwndText, L"WNDPROC", oldProc);
						else hwndText = hwnd;
					}
					else hwndText = hwnd;
					SetPropW(hwndText, L"FWDTEXT", hwndFwd);

					WCHAR szText[256] = {0};
					GetWindowTextW(hwndText, szText, sizeof(szText)/sizeof(wchar_t)); 
					SetWindowTextW(hwndFwd, szText);
				}
			}
			break;
		case WM_DESTROY:
			{
				RemovePropW(hwnd, L"WNDPROC");
				RemovePropW(hwnd, L"FWDTEXT");
				if (fnOldProc) SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)fnOldProc);
				HWND hwndOwner;
				hwndOwner = GetWindow(hwnd, GW_OWNER);
				if (hwndOwner) DestroyWindow(hwndOwner);
			}
			break;		
		case WM_STYLECHANGING:
			if (GWL_STYLE & wParam) ((STYLESTRUCT*)lParam)->styleNew &= ~WS_VISIBLE;
			return 0;
		case WM_STYLECHANGED:
			if ((GWL_STYLE & wParam) && (WS_VISIBLE & ((STYLESTRUCT*)lParam)->styleNew)) 
					SetWindowLongPtrW(hwnd, GWL_STYLE, (((STYLESTRUCT*)lParam)->styleNew & ~WS_VISIBLE));
			return 0;
		case WM_WINDOWPOSCHANGING:
			((WINDOWPOS*)lParam)->flags = (((WINDOWPOS*)lParam)->flags & ~(SWP_SHOWWINDOW | SWP_FRAMECHANGED)) | SWP_NOACTIVATE | SWP_NOZORDER;
			return 0;
		case WM_SETTEXT: 
			{
				HWND hwndFwd = (HWND)GetPropW(hwnd, L"FWDTEXT");
				if (hwndFwd) 
				{
					(IsWindowUnicode(hwndFwd)) ? SendMessageW(hwndFwd, WM_SETTEXT, wParam, lParam) :
										SendMessageA(hwndFwd, WM_SETTEXT, wParam, lParam);
				}
			}
			break;
	}
	if (!fnOldProc) return DefWindowProc(hwnd, uMsg, wParam, lParam);
	return (IsWindowUnicode(hwnd)) ? CallWindowProcW(fnOldProc, hwnd, uMsg, wParam, lParam) : CallWindowProcA(fnOldProc, hwnd, uMsg, wParam, lParam);
}

static LRESULT CALLBACK LabelWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	WNDPROC fnOldProc = (WNDPROC)GetPropW(hwnd, L"WNDPROC");
	
	switch(uMsg)
	{
		case WM_SETTEXT: 
			{
				HWND hwndFwd = (HWND)GetPropW(hwnd, L"FWDTEXT");
				if (hwndFwd) 
				{
					(IsWindowUnicode(hwndFwd)) ? SendMessageW(hwndFwd, WM_SETTEXT, wParam, lParam) :
										SendMessageA(hwndFwd, WM_SETTEXT, wParam, lParam);
				}
			}
		case WM_DESTROY:
			RemovePropW(hwnd, L"WNDPROC");
			RemovePropW(hwnd, L"FWDTEXT");
			if (fnOldProc) SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)fnOldProc);
			break;						
	}
	if (!fnOldProc) return DefWindowProc(hwnd, uMsg, wParam, lParam);
	return (IsWindowUnicode(hwnd)) ? CallWindowProcW(fnOldProc, hwnd, uMsg, wParam, lParam) : CallWindowProcA(fnOldProc, hwnd, uMsg, wParam, lParam);
}