#include "main.h"
#include "./statusbar.h"
#include "./graphics.h"
#include "./browserHost.h"
#include "../winamp/wa_dlg.h"
#include "../Plugins/General/gen_ml/ml_ipc_0313.h"

#include "./ifc_skinhelper.h"
#include "./ifc_wasabihelper.h"

#include <windows.h>
#include <shlwapi.h>
#include <strsafe.h>

#define TEXTMARGIN_LEFT		3
#define TEXTMARGIN_TOP		1
#define TEXTMARGIN_RIGHT		3
#define TEXTMARGIN_BOTTOM	1


#define SBT_INFLATE_ID			31
#define SBT_INFLATE_DELAY		50
#define SBT_INFLATE_INTERVAL	20

#define SBT_MOUSEROLL_ID			32
#define SBT_MOUSEROLL_DELAY		50
#define SBT_MOUSEROLL_INTERVAL	20

#define SBF_MOUSEROLL			0x00000001
#define SBF_CACHEDFONT			0x00000002

typedef struct __STATUSBAR
{
	UINT		flags;
	RECT		parentRect;
	SIZE		textSize;
	
	LPWSTR		pszText;
	INT			cchText;
	INT			cchTextMax;

	HFONT		textFont;
	COLORREF	rgbBk;
	COLORREF	rgbText;
	HBRUSH		brushBk;

	LONG		desiredCX;
	LONG		mouseY;
	LONG		desiredY;
	HRGN		windowRegion;
	HWND		hBrowser;

	DWORD		inflateTime;
} STATUSBAR;

typedef struct __STATUSBARMOUSEHOOK
{
	HHOOK hHook;
	HWND	 hwnd;
} STATUSBARMOUSEHOOK;

static size_t tlsIndex = -1;


#define GetStatusbar(__hwnd) ((STATUSBAR*)(LONG_PTR)(LONGX86)GetWindowLongPtr((__hwnd), 0))
static LRESULT CALLBACK Statusbar_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static void CALLBACK Statusbar_InflateTimer(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD timerId);
static void CALLBACK Statusbar_MouseRollTimer(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD timerId);
static LRESULT CALLBACK Statusbar_MouseHook(INT code, WPARAM wParam, LPARAM lParam);

BOOL Statusbar_RegisterClass(HINSTANCE hInstance)
{
	WNDCLASS wc;
	ATOM klassAtom;
	ifc_wasabihelper *wasabi;

	if (GetClassInfo(hInstance, NWC_ONLINEMEDIASTATUSBAR, &wc)) 
		return TRUE;

	ZeroMemory(&wc, sizeof(WNDCLASS));

	wc.hInstance		= hInstance;
	wc.lpszClassName	= NWC_ONLINEMEDIASTATUSBAR;
	wc.lpfnWndProc	= Statusbar_WindowProc;
	wc.style			= CS_PARENTDC | CS_SAVEBITS;
	wc.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= NULL;
	wc.cbWndExtra	= sizeof(STATUSBAR*);
	
	klassAtom = RegisterClassW(&wc);
	if (0 == klassAtom)
		return FALSE;
	
	if (SUCCEEDED(Plugin_GetWasabiHelper(&wasabi)))
	{
		api_application *application;
		if (SUCCEEDED(wasabi->GetApplicationApi(&application)))
		{
			application->DirectMouseWheel_RegisterSkipClass(klassAtom);
			application->Release();
		}
		wasabi->Release();
	}
	return TRUE;
}

static BOOL Statusbar_GetTextSize(HWND hwnd, HFONT textFont, LPCWSTR pszText, INT cchText, SIZE *textSize)
{
	HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
	if (NULL == hdc) return FALSE;
	
	HFONT originalFont = (HFONT)SelectObject(hdc, textFont);
	
	BOOL result;
	if (0 == cchText)
	{
		TEXTMETRIC tm;
		result = GetTextMetrics(hdc, &tm);
		if (FALSE != result)
		{
			textSize->cx = 0;
			textSize->cy = tm.tmHeight;
		}
	}
	else
	{
		result = GetTextExtentPoint32(hdc, pszText, cchText, textSize);
	}
			
	SelectObject(hdc, originalFont);
	ReleaseDC(hwnd, hdc);
	
	return result;
}


static BOOL Statusbar_SetWindowPos(HWND hwnd, HWND hwndInsertAfter, INT x, INT y, INT cx, INT cy, UINT flags)
{
	STATUSBAR *statusbar = GetStatusbar(hwnd);
	if (NULL != statusbar)
	{
		INT k = cy/4;
		if (k > cx) k = 0;

		POINT szPoint[5] = {0};
		//szPoint[0].x = 0;
		//szPoint[0].y = 0;
		szPoint[1].x = cx - k;
		//szPoint[1].y = 0;
		szPoint[2].x = cx;
		szPoint[2].y = k;
		szPoint[3].x = cx;
		szPoint[3].y = cy;
		//szPoint[4].x = 0;
		szPoint[4].y = cy;

		HRGN rgn = CreatePolygonRgn(szPoint, ARRAYSIZE(szPoint), WINDING);
		if (0 != SetWindowRgn(hwnd, rgn, TRUE))
		{
			if (NULL != statusbar->windowRegion)
				DeleteObject(statusbar->windowRegion);
			statusbar->windowRegion = rgn;
		}
		else
		{
			if (NULL != rgn)
				DeleteObject(rgn);
		}
	}

	if (0 == SetWindowPos(hwnd, hwndInsertAfter, x, y, cx, cy, flags))
		return FALSE;
	
	return TRUE;
}


static void Statusbar_Inflate(HWND hwnd)
{
	STATUSBAR *statusbar = GetStatusbar(hwnd);
	if (NULL == statusbar) return;

	DWORD currentTime = GetTickCount();
	DWORD kMult = (currentTime - statusbar->inflateTime) / SBT_INFLATE_INTERVAL;
	if (kMult < 4) kMult = 1;

	statusbar->inflateTime = currentTime;

	RECT windowRect;
	if (!GetWindowRect(hwnd, &windowRect))
		return;

	DWORD windowStyle = GetWindowStyle(hwnd);

	LONG currentWidth = windowRect.right - windowRect.left;
	LONG targetCX = statusbar->desiredCX;

	if (0 == (SBS_ACTIVE & windowStyle) || targetCX < 16)
		targetCX = 0;

	if (currentWidth == targetCX)
		return;

	LONG width = currentWidth;
	LONG height = windowRect.bottom - windowRect.top;
	LONG step;

	while(kMult-- && width != targetCX)
	{
		if (width > targetCX)
		{
			step = (width - targetCX) / 3;
			if (step < 6) step = 6;
		
			width -= step;
			if (width < targetCX) width = targetCX;

		}
		else
		{
			step = (targetCX - width)*2 / 3;
			if (step < 48) step = 48;
			
			width += step;
			if (width > targetCX) width = targetCX;
		}
	}

	Statusbar_SetWindowPos(hwnd, HWND_TOP, 0, 0, width, height, 
		SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE);


	if (width < (windowRect.right - windowRect.left))
	{
		if (NULL != statusbar->hBrowser)
		{
			RECT invalidRect;
			SetRect(&invalidRect, windowRect.left + width - 4, windowRect.top, windowRect.right, windowRect.bottom);
			MapWindowPoints(HWND_DESKTOP, statusbar->hBrowser, (POINT*)&invalidRect, 2);
			RedrawWindow(statusbar->hBrowser, &invalidRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
		}
	}
	else
	{
		RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_FRAME);
	}

	if (0 == width && 0 == (SBS_ACTIVE & windowStyle))
	{
		ShowWindow(hwnd, SW_HIDE);
	}
	else
	{
		if (width != targetCX)
		{
			SetTimer(hwnd, SBT_INFLATE_ID, SBT_INFLATE_INTERVAL, Statusbar_InflateTimer);
		}
	}
}

static void Statusbar_UpdateLayout(HWND hwnd, BOOL fForce)
{
	DWORD windowStyle = GetWindowStyle(hwnd);
	if (0 == (WS_VISIBLE & windowStyle) && FALSE == fForce)
	{
		if (0 == (SBS_UPDATELAYOUT & windowStyle))
			SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | SBS_UPDATELAYOUT);
		return;
	}

	if (0 != (SBS_UPDATELAYOUT & windowStyle))
		SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~SBS_UPDATELAYOUT);

	STATUSBAR *statusbar = GetStatusbar(hwnd);
	if (NULL == statusbar) return;

	if (!Statusbar_GetTextSize(hwnd, statusbar->textFont, statusbar->pszText, statusbar->cchText, &statusbar->textSize))
		ZeroMemory(&statusbar->textSize, sizeof(SIZE));
	
	RECT windowRect;
	SetRect(&windowRect, 
			statusbar->parentRect.left, 
			statusbar->parentRect.bottom - statusbar->textSize.cy + statusbar->mouseY, 
			statusbar->parentRect.left + statusbar->textSize.cx, 
			statusbar->parentRect.bottom + statusbar->mouseY);

	if (0 != statusbar->textSize.cy)
		windowRect.top -= (TEXTMARGIN_TOP + TEXTMARGIN_BOTTOM);
	if (0 != statusbar->textSize.cx)
		windowRect.right += (TEXTMARGIN_LEFT + TEXTMARGIN_RIGHT);
	
	if (windowRect.right > statusbar->parentRect.right - 2)
		windowRect.right = statusbar->parentRect.right - 2;

	if ((windowRect.right - windowRect.left) < 20)
		windowRect.right = windowRect.left;

	if (windowRect.top < statusbar->parentRect.top + 20)
		windowRect.top = statusbar->parentRect.bottom;

	RECT previousRect;
	if (!GetWindowRect(hwnd, &previousRect))
		SetRectEmpty(&previousRect);
			
	if (FALSE == EqualRect(&previousRect, &windowRect))
	{
		LONG width = windowRect.right - windowRect.left;
		LONG height = windowRect.bottom - windowRect.top;
		LONG prevWidth = previousRect.right - previousRect.left;
		LONG prevHeight = previousRect.bottom - previousRect.top;

		LONG widthAdjust = 0;
		if (statusbar->desiredCX != prevWidth)
		{
			widthAdjust = width - prevWidth;
		}

		KillTimer(hwnd, SBT_INFLATE_ID);
		statusbar->desiredCX = width;
		statusbar->inflateTime = GetTickCount();

		HWND hParent = GetParent(hwnd);
		MapWindowPoints(HWND_DESKTOP, hParent, (POINT*)&previousRect, 2);
		if (windowRect.left != previousRect.left || 	windowRect.top != previousRect.top || height != prevHeight || 0 != widthAdjust)
		{
			Statusbar_SetWindowPos(hwnd, HWND_TOP, windowRect.left, windowRect.top, prevWidth + widthAdjust, height, 
				SWP_NOACTIVATE | SWP_NOOWNERZORDER);

			if (widthAdjust < 0 && NULL != statusbar->hBrowser)
			{
				RECT invalidRect;
				SetRect(&invalidRect, previousRect.left + prevWidth + widthAdjust - 4, previousRect.top, previousRect.left + prevWidth, previousRect.bottom);
				MapWindowPoints(HWND_DESKTOP, statusbar->hBrowser, (POINT*)&invalidRect, 2);
				RedrawWindow(statusbar->hBrowser, &invalidRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
			}
			else if (0 != widthAdjust)
			{
				RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_FRAME);
			}
		}
		
		if (width > prevWidth)
		{
			Statusbar_Inflate(hwnd);
		}
		else
		{
			SetTimer(hwnd, SBT_INFLATE_ID, SBT_INFLATE_DELAY, Statusbar_InflateTimer);
			//Statusbar_Inflate(hwnd);
		}
	}
	else
	{
        InvalidateRect(hwnd, NULL, FALSE);
	}
}



static BOOL Statusbar_InstallMouseHook(HWND hwnd)
{
	if (TLS_OUT_OF_INDEXES == tlsIndex)
	{
		tlsIndex = Plugin_TlsAlloc();
		if (TLS_OUT_OF_INDEXES == tlsIndex) 
			return FALSE;
	}

	STATUSBARMOUSEHOOK *hook = (STATUSBARMOUSEHOOK*)Plugin_TlsGetValue(tlsIndex);
    if (NULL != hook) return FALSE;

	hook = (STATUSBARMOUSEHOOK*)calloc(1, sizeof(STATUSBARMOUSEHOOK));
	if (NULL == hook) return FALSE;

	hook->hwnd = hwnd;
	hook->hHook = SetWindowsHookEx(WH_MOUSE, Statusbar_MouseHook, NULL, GetCurrentThreadId());
	if (NULL == hook->hHook)
	{
		free(hook);
		return FALSE;
	}
	
	Plugin_TlsSetValue(tlsIndex, hook); 
	return TRUE;
}

static void Statusbar_RemoveMouseHook()
{
	if (TLS_OUT_OF_INDEXES == tlsIndex) return;

	STATUSBARMOUSEHOOK *hook = (STATUSBARMOUSEHOOK*)Plugin_TlsGetValue(tlsIndex);
    if (NULL == hook) return;
	
	Plugin_TlsSetValue(tlsIndex, NULL); 

	if (NULL != hook->hHook) 
		UnhookWindowsHookEx(hook->hHook);
	free(hook);
}

static void CALLBACK Statusbar_InstallMouseHookApc(ULONG_PTR param)
{
	Statusbar_InstallMouseHook((HWND)param);
}

static void CALLBACK Statusbar_RemoveMouseHookApc(ULONG_PTR param)
{
	Statusbar_RemoveMouseHook();
}
static void Statusbar_MouseRoll(HWND hwnd)
{
	STATUSBAR *psb = GetStatusbar(hwnd);
	if (NULL == psb || 0 == (SBF_MOUSEROLL & psb->flags)) return;

	if (psb->desiredY == psb->mouseY)
	{
		psb->flags &= ~SBF_MOUSEROLL;
		return;
	}

	RECT windowRect;
	if (!GetWindowRect(hwnd, &windowRect))
		return;

	windowRect.top -= psb->mouseY;

	if (psb->mouseY > psb->desiredY)
	{
		psb->mouseY -= 4;
		if (psb->mouseY < psb->desiredY) psb->mouseY = psb->desiredY;
	}
	else
	{
		psb->mouseY += 4;
		if (psb->mouseY > psb->desiredY) psb->mouseY = psb->desiredY;
	}
			
	windowRect.top += psb->mouseY;
	HWND hParent = GetParent(hwnd);
	if (NULL != hParent)
	{
		MapWindowPoints(HWND_DESKTOP, hParent, (POINT*)&windowRect, 1);
		SetWindowPos(hwnd, HWND_TOP, windowRect.left, windowRect.top, 0, 0, 
			SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_ASYNCWINDOWPOS);
	}

	if (psb->desiredY == psb->mouseY ||
		0 == SetTimer(hwnd, SBT_MOUSEROLL_ID, SBT_MOUSEROLL_INTERVAL, Statusbar_MouseRollTimer))
	{
		psb->flags &= ~SBF_MOUSEROLL;
	}
}

static void Statusbar_MouseCheck(HWND hwnd, POINT pt)
{	
	STATUSBAR *psb = GetStatusbar(hwnd);
	if (NULL == psb) return;
	
	DWORD windowStyle = GetWindowStyle(hwnd);
	if (0 != (WS_DISABLED & windowStyle))
	{
		if (0 != psb->desiredY)
		{
			psb->desiredY = 0;
			if (0 == (SBF_MOUSEROLL & psb->flags) &&
				0 != SetTimer(hwnd, SBT_MOUSEROLL_ID, 0, Statusbar_MouseRollTimer))
			{
				psb->flags |= SBF_MOUSEROLL;
			}
		}
		return;
	}

	RECT windowRect;
	if (!GetWindowRect(hwnd, &windowRect))
		return;
			
	windowRect.right = windowRect.left + psb->desiredCX;
	windowRect.top -= psb->mouseY;
	windowRect.bottom -= psb->mouseY;
	LONG mouseY = psb->desiredY;
		
	if (pt.y < windowRect.bottom)
	{
		if (pt.y < (windowRect.bottom - 12))
		{
			pt.y += 12;
		}
		else
		{
			pt.y = windowRect.bottom - 1;
		}
	}

	if (PtInRect(&windowRect, pt))
	{					
		psb->desiredY = pt.y - windowRect.top + 1;
	}
	else
	{
		psb->desiredY = 0;
	}

	if (psb->desiredY != mouseY)
	{
		if (0 == (SBF_MOUSEROLL & psb->flags) &&
			0 != SetTimer(hwnd, SBT_MOUSEROLL_ID, 0, Statusbar_MouseRollTimer))
		{
			psb->flags |= SBF_MOUSEROLL;
		}
	}
}

static void Statusbar_Paint(HWND hwnd, HDC hdc, const RECT *prcPaint, BOOL fErase)
{
	DWORD windowStyle = GetWindowStyle(hwnd);
	if (0 != (SBS_UPDATELAYOUT & windowStyle))
		Statusbar_UpdateLayout(hwnd, TRUE);

	STATUSBAR *statusbar = GetStatusbar(hwnd);
	if (NULL == statusbar) return;

	RECT clientRect, textRect;
	GetClientRect(hwnd, &clientRect);
	CopyRect(&textRect, &clientRect);
	
	SetBkColor(hdc, statusbar->rgbBk);
	SetTextColor(hdc, statusbar->rgbText);

	HRGN rgnBack = CreateRectRgnIndirect((NULL != prcPaint) ? prcPaint : &clientRect);
	HRGN rgn = NULL;

	if (0 != statusbar->cchText)
	{
		textRect.top += TEXTMARGIN_TOP;
		textRect.right -= TEXTMARGIN_RIGHT;

		SetBkMode(hdc, TRANSPARENT);
		SetTextAlign(hdc, TA_LEFT | TA_BOTTOM);
	
		HFONT origFont = (HFONT)SelectObject(hdc, statusbar->textFont);
		
		rgn = CreateRectRgnIndirect(&textRect);

		DWORD options = (FALSE != fErase) ? ETO_OPAQUE : 0;
		if (0 != ExtTextOut(hdc, textRect.left + TEXTMARGIN_LEFT, textRect.bottom - TEXTMARGIN_BOTTOM, 
					options, &textRect, statusbar->pszText, statusbar->cchText, NULL))
		{
			CombineRgn(rgnBack, rgnBack, rgn, RGN_DIFF);
		}
		SelectObject(hdc, origFont);
	}

	if (0 != fErase)
	{
		FillRgn(hdc, rgnBack, statusbar->brushBk);
	}

	if (NULL != rgn) DeleteObject(rgn);
	if (NULL != rgnBack) DeleteObject(rgnBack);

}

static LRESULT Statusbar_OnCreate(HWND hwnd, CREATESTRUCT *pcs)
{
	STATUSBAR *statusbar = (STATUSBAR*)calloc(1, sizeof(STATUSBAR));
	if (NULL != statusbar)
	{
		SetLastError(ERROR_SUCCESS);
		if (!SetWindowLongPtr(hwnd, 0, (LONGX86)(LONG_PTR)statusbar) && ERROR_SUCCESS != GetLastError())
		{
			free(statusbar);
			statusbar = NULL;
		}
	}

	if (NULL == statusbar)
	{
		DestroyWindow(hwnd);
		return -1;
	}

	return 0;
}

static void Statusbar_OnDestroy(HWND hwnd)
{
	STATUSBAR *statusbar = GetStatusbar(hwnd);
	SetWindowLongPtr(hwnd, 0, 0L);

	if (NULL == statusbar) return;

	if (NULL != statusbar->textFont && 0 == (SBF_CACHEDFONT & statusbar->flags))
		DeleteObject(statusbar->textFont);

	if (NULL != statusbar->windowRegion)
		DeleteObject(statusbar->windowRegion);

	if (NULL != statusbar->brushBk)
		DeleteObject(statusbar->brushBk);

	Statusbar_RemoveMouseHook();
	if (NULL != statusbar->hBrowser)
		PostMessage(statusbar->hBrowser, NBHM_QUEUEAPC, (WPARAM)hwnd, (LPARAM)Statusbar_RemoveMouseHookApc);

	free(statusbar);
}

static void Statusbar_OnPaint(HWND hwnd)
{
	PAINTSTRUCT ps;
	if (BeginPaint(hwnd, &ps))
	{
		if (ps.rcPaint.left != ps.rcPaint.right)
			Statusbar_Paint(hwnd, ps.hdc, &ps.rcPaint, ps.fErase);
		EndPaint(hwnd, &ps);
	}
}

static void Statusbar_OnPrintClient(HWND hwnd, HDC hdc, UINT options)
{	
	RECT clientRect;
	if (GetClientRect(hwnd, &clientRect))
	Statusbar_Paint(hwnd, hdc, &clientRect, 0 != (PRF_ERASEBKGND & options));
}
static BOOL Statusbar_SetText(HWND hwnd, LPCWSTR pszText)
{
	STATUSBAR *statusbar = GetStatusbar(hwnd);
	if (NULL == statusbar) return FALSE;

	INT cchText;
	cchText = (NULL != pszText && FALSE == IS_INTRESOURCE(pszText)) ? lstrlen(pszText) : 0;

	if (cchText >= statusbar->cchTextMax)
	{
		statusbar->cchText = 0;
		statusbar->cchTextMax = 0;
		if (NULL != statusbar->pszText)
			Plugin_FreeString(statusbar->pszText);

		INT cchMalloc = ((cchText / 1024) + 1) * 1024;
		statusbar->pszText = Plugin_MallocString(cchMalloc);
		if (NULL == statusbar->pszText)
			return FALSE;

		statusbar->cchTextMax = cchMalloc;
	}

	statusbar->cchText = cchText;
	if (0 == cchText)
	{	
		statusbar->pszText[0] = L'\0';
	}
	else
	{
		if (FAILED(StringCchCopy(statusbar->pszText, statusbar->cchTextMax, pszText)))
		{
			statusbar->pszText[0] = L'\0';
			statusbar->cchText = 0;
			return FALSE;
		}
	}

	return TRUE;
}
static BOOL Statusbar_OnSetText(HWND hwnd, LPCWSTR pszText)
{	
	if (FALSE == Statusbar_SetText(hwnd, pszText))
		return FALSE;

	Statusbar_UpdateLayout(hwnd, FALSE);
	return TRUE;
}

static INT Statusbar_OnGetText(HWND hwnd, LPWSTR pszBuffer, INT cchBufferMax)
{
	if (NULL == pszBuffer || cchBufferMax)
		return 0;

	pszBuffer[0] = L'\0';

	STATUSBAR *statusbar = GetStatusbar(hwnd);
	if (NULL == statusbar) return 0;
	
	INT cchCopy = (statusbar ? statusbar->cchText : 0);
	if (NULL != statusbar && 0 != cchCopy)
	{		
		if (cchCopy >= cchBufferMax)
			cchCopy = (cchBufferMax - 1);

		StringCchCopyN(pszBuffer, cchBufferMax, statusbar->pszText, cchCopy);
	}
	return cchCopy;
}

static INT Statusbar_OnGetTextLength(HWND hwnd)
{
	STATUSBAR *statusbar = GetStatusbar(hwnd);
	return (NULL != statusbar) ? statusbar->cchText : 0;
}

static LRESULT Statusbar_OnShowWindow(HWND hwnd, BOOL fShow, UINT nState)
{
	STATUSBAR *psb = GetStatusbar(hwnd);
	if (NULL != psb)
	{
		if (FALSE != fShow)
		{
			Statusbar_InstallMouseHook(hwnd);
			if (NULL != psb->hBrowser)
				PostMessage(psb->hBrowser, NBHM_QUEUEAPC, (WPARAM)hwnd, (LPARAM)Statusbar_InstallMouseHookApc);
			
		}
		else
		{
			Statusbar_RemoveMouseHook();
			if (NULL != psb->hBrowser)
				PostMessage(psb->hBrowser, NBHM_QUEUEAPC, (WPARAM)hwnd, (LPARAM)Statusbar_RemoveMouseHookApc);
		}
	}

	return DefWindowProcW(hwnd, WM_SHOWWINDOW, (WPARAM)fShow, (LPARAM)nState);
}
static void Statusbar_OnUpdateSkin(HWND hwnd, BOOL fRedraw)
{
	STATUSBAR *statusbar = GetStatusbar(hwnd);
	if (NULL == statusbar) return;

	ifc_skinhelper *skin;
	if (FAILED(Plugin_GetSkinHelper(&skin))) 
		skin = NULL;
	
	if (NULL == skin || FAILED(skin->GetColor(WADLG_WNDBG, &statusbar->rgbBk)))
		statusbar->rgbBk = GetSysColor(COLOR_WINDOW);
	
	if (NULL == skin || FAILED(skin->GetColor(WADLG_WNDFG, &statusbar->rgbText)))
		statusbar->rgbText = GetSysColor(COLOR_WINDOWTEXT);
	statusbar->rgbText = BlendColors(statusbar->rgbText, statusbar->rgbBk, 127);

	if (NULL != statusbar->textFont)
	{
		if (0 == (SBF_CACHEDFONT & statusbar->flags))
			DeleteObject(statusbar->textFont);
		statusbar->textFont = NULL; 
	}

	if (NULL != skin)
		statusbar->textFont = skin->GetFont();

	if (NULL != statusbar->textFont) 
		statusbar->flags |= SBF_CACHEDFONT;
	else
	{
		statusbar->flags &= ~SBF_CACHEDFONT;
		LOGFONT lf;
		SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0);
		StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Tahoma");
		statusbar->textFont = CreateFontIndirect(&lf);
	}
		
	if (NULL != skin)
		skin->Release();

	if (NULL != statusbar->brushBk)
		DeleteObject(statusbar->brushBk);

	statusbar->brushBk = CreateSolidBrush(statusbar->rgbBk);

	Statusbar_UpdateLayout(hwnd, FALSE);
}

static void Statusbar_OnSetParentRect(HWND hwnd, const RECT *parentRect)
{
	STATUSBAR *statusbar = GetStatusbar(hwnd);
	if (NULL == statusbar || NULL == parentRect) return;

	CopyRect(&statusbar->parentRect, parentRect);
	Statusbar_UpdateLayout(hwnd, FALSE);
}

static BOOL Statusbar_OnSetActive(HWND hwnd, BOOL fActive)
{
	DWORD windowStyle = GetWindowStyle(hwnd);
	if (0 == (SBS_ACTIVE & windowStyle) == (FALSE == fActive))
		return TRUE;

	if (FALSE == fActive)
		windowStyle &= ~SBS_ACTIVE;
	else
		windowStyle |= SBS_ACTIVE;

	SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle);

	if (0 != (SBS_ACTIVE & windowStyle))
	{
		if (0 == (WS_VISIBLE & windowStyle))
		{
			Statusbar_UpdateLayout(hwnd, TRUE);
			ShowWindow(hwnd, SW_SHOWNA);
		}
	}
	else
	{
		if (0 != (WS_VISIBLE & windowStyle))
		{
			STATUSBAR *statusbar = GetStatusbar(hwnd);
			if (NULL != statusbar)
			{
				KillTimer(hwnd, SBT_INFLATE_ID);
				statusbar->desiredCX = 0;
				statusbar->inflateTime = GetTickCount();
				SetTimer(hwnd, SBT_INFLATE_ID, SBT_INFLATE_DELAY, Statusbar_InflateTimer);
			}
		}
	}
	return TRUE;
}

static BOOL Statusbar_OnUpdate(HWND hwnd, LPCWSTR pszText)
{
	STATUSBAR *psb = GetStatusbar(hwnd);
	if (NULL == psb) return FALSE;

	if (NULL == pszText || L'\0' == *pszText)
		return TRUE;

	if (NULL == pszText)
	{
		if (NULL == psb->pszText)
		{
			return TRUE;
		}
		pszText = L"";
	}

	if (NULL != psb->pszText &&
		CSTR_EQUAL == CompareString(CSTR_INVARIANT, 0, psb->pszText, -1, pszText, -1))
	{
		return TRUE;
	}

	Statusbar_SetText(hwnd, pszText);

	DWORD windowStyle = GetWindowStyle(hwnd);
	if (0 != (WS_VISIBLE & windowStyle))
	{
		Statusbar_UpdateLayout(hwnd, FALSE);
		InvalidateRect(hwnd, NULL, TRUE);
	}
			
	return TRUE;
}

static BOOL Statusbar_OnSetBrowserHost(HWND hwnd, HWND hBrowser)
{
	STATUSBAR *psb = GetStatusbar(hwnd);
	if (NULL == psb) return FALSE;

	psb->hBrowser = hBrowser;
	return TRUE;
}

static LRESULT Statusbar_OnEnable(HWND hwnd, BOOL fEnable)
{
	UINT windowStyle = GetWindowStyle(hwnd);
	UINT newStyle = windowStyle;
	
	if (FALSE == fEnable)
		newStyle |= WS_DISABLED;
	else
		newStyle &= ~WS_DISABLED;

	if(newStyle == windowStyle)
		return fEnable;
	
	SetWindowLongPtr(hwnd, GWL_STYLE, newStyle);

	return !fEnable;
}

static LRESULT CALLBACK Statusbar_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		case WM_CREATE:				return Statusbar_OnCreate(hwnd, (CREATESTRUCT*)lParam);
		case WM_DESTROY:				Statusbar_OnDestroy(hwnd); break;
		case WM_PAINT:				Statusbar_OnPaint(hwnd); return 0;
		case WM_PRINTCLIENT:			Statusbar_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0;
		case WM_ERASEBKGND:			return 0;
		case WM_SETTEXT:				return Statusbar_OnSetText(hwnd, (LPCWSTR)lParam);
		case WM_GETTEXT:				return Statusbar_OnGetText(hwnd, (LPWSTR)lParam, (INT)wParam);
		case WM_GETTEXTLENGTH:		return Statusbar_OnGetTextLength(hwnd);
		case WM_SHOWWINDOW:			return Statusbar_OnShowWindow(hwnd, (BOOL)wParam, (UINT)lParam);

		case SBM_UPDATESKIN:			Statusbar_OnUpdateSkin(hwnd, (BOOL)lParam); return 0;
		case SBM_SETPARENTRECT:		Statusbar_OnSetParentRect(hwnd, (const RECT*)lParam); return 0;
		case SBM_SETACTIVE:			return Statusbar_OnSetActive(hwnd, (BOOL)wParam); 
		case SBM_UPDATE:				return Statusbar_OnUpdate(hwnd, (LPCWSTR)lParam); 
		case SBM_SETBROWSERHOST:		return Statusbar_OnSetBrowserHost(hwnd, (HWND)lParam);
		case SBM_ENABLE:				return Statusbar_OnEnable(hwnd, (BOOL)lParam);
	
	}
	return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
static void CALLBACK Statusbar_InflateTimer(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD timerId)
{
	KillTimer(hwnd, eventId);
	Statusbar_Inflate(hwnd);
}

static void CALLBACK Statusbar_MouseRollTimer(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD timerId)
{
	KillTimer(hwnd, eventId);
	Statusbar_MouseRoll(hwnd);
}

static LRESULT CALLBACK Statusbar_MouseHook(INT code, WPARAM wParam, LPARAM lParam)
{
	STATUSBARMOUSEHOOK *hook = (STATUSBARMOUSEHOOK*)Plugin_TlsGetValue(tlsIndex);
	if (NULL == hook || NULL == hook->hHook) return FALSE;
	
	if (code >= 0)
	{
		MOUSEHOOKSTRUCT *mouseHook = (MOUSEHOOKSTRUCT*)lParam;
		if (NULL != hook->hwnd)
		{
			Statusbar_MouseCheck(hook->hwnd, mouseHook->pt);
		}
	}		
	return CallNextHookEx(hook->hHook, code, wParam, lParam);
}