/*

Miranda NG: the free IM client for Microsoft* Windows*

Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "..\..\core\commonheaders.h"

struct HyperlinkWndData {
	HFONT hEnableFont, hDisableFont;
	RECT rcText;
	COLORREF enableColor, disableColor, focusColor;
	BYTE flags; /* see HLKF_* */
};

/* flags */
#define HLKF_HASENABLECOLOR   0x1 /* dat->enableColor is not system default */
#define HLKF_HASDISABLECOLOR  0x2 /* dat->disableColor is not system default */

/* internal messages */
#define HLK_MEASURETEXT  (WM_USER+1)
#define HLK_INVALIDATE   (WM_USER+2)

static LRESULT CALLBACK HyperlinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	struct HyperlinkWndData *dat = (struct HyperlinkWndData*)GetWindowLongPtr(hwnd, 0);
	switch(msg) {
		case WM_NCCREATE:
			dat = (struct HyperlinkWndData*)mir_calloc(sizeof(struct HyperlinkWndData));
			if (dat == NULL) return FALSE; /* fail creation */
			SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); /* always succeeds */
			/* fall thru */
		case WM_SYSCOLORCHANGE:
			if (!(dat->flags&HLKF_HASENABLECOLOR)) {
				if (GetSysColorBrush(COLOR_HOTLIGHT) == NULL) dat->enableColor = RGB(0, 0, 255);
				else dat->enableColor = GetSysColor(COLOR_HOTLIGHT);
				dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2);
			}
			if (!(dat->flags&HLKF_HASDISABLECOLOR))
				dat->disableColor = GetSysColor(COLOR_GRAYTEXT);
			break;

		case WM_SETFOCUS:
		case WM_KILLFOCUS:
			RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
			break;
		case WM_MOUSEACTIVATE:
			SetFocus(hwnd);
			return MA_ACTIVATE;
		case WM_GETDLGCODE:
		{
			if (lParam)
			{
				MSG *msg = (MSG *) lParam;
				if (msg->message == WM_KEYDOWN)
				{
					if (msg->wParam == VK_TAB)
						return 0;
					if (msg->wParam == VK_ESCAPE)
						return 0;
				} else
				if (msg->message == WM_CHAR)
				{
					if (msg->wParam == '\t')
						return 0;
					if (msg->wParam == 27)
						return 0;
				}
			}
			return DLGC_WANTMESSAGE;
		}

		case WM_KEYDOWN:
		{
			switch (wParam)
			{
			case VK_SPACE:
			case VK_RETURN:
				SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd);
				break;
			}
			return 0;
		}

		case WM_LBUTTONDOWN:
		{	POINT pt;
			POINTSTOPOINT(pt, MAKEPOINTS(lParam));
			if (!PtInRect(&dat->rcText, pt)) break;
			SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd);
			return 0;
		}
		case WM_SETFONT:
		{	LOGFONT lf;
			HFONT hFont;
			if ((HFONT)wParam == NULL) { /* use default system color */
				dat->hEnableFont = dat->hDisableFont = NULL;
				return 0;
			}
			if (GetObject((HFONT)wParam, sizeof(lf), &lf)) {
				lf.lfUnderline = 1;
				hFont = CreateFontIndirect(&lf);
				if (hFont != NULL) {
					dat->hEnableFont = hFont;
					dat->hDisableFont = (HFONT)wParam;
					if (LOWORD(lParam)) SendMessage(hwnd, HLK_INVALIDATE, 0, 0);
					SendMessage(hwnd, HLK_MEASURETEXT, 0, 0);
				}
			}
			return 0;
		}
		case WM_ERASEBKGND:
			return TRUE;
		case WM_ENABLE:
		case HLK_INVALIDATE:
		{	RECT rcWnd;
			POINT pt;
			HWND hwndParent;
			if (!GetWindowRect(hwnd, &rcWnd)) break;
			pt.x = rcWnd.left;
			pt.y = rcWnd.top;
			hwndParent = GetParent(hwnd);
			if (hwndParent == NULL) hwndParent = hwnd;
			if (!ScreenToClient(hwndParent, &pt)) break;
			rcWnd.right = pt.x+(rcWnd.right-rcWnd.left);
			rcWnd.bottom = pt.y+(rcWnd.bottom-rcWnd.top);
			rcWnd.left = pt.x;
			rcWnd.top = pt.y;
			InvalidateRect(hwndParent, &rcWnd, TRUE);
			return 0;
		}
		case WM_GETFONT:
			return (LRESULT)dat->hDisableFont;
		case WM_CREATE:
		case HLK_MEASURETEXT:
		{	TCHAR szText[256];
			if (!GetWindowText(hwnd, szText, SIZEOF(szText))) return 0;
			lParam = (LPARAM)szText;
			/* fall thru */
		case WM_SETTEXT:
		{	HFONT hPrevFont = NULL;
			SIZE textSize;
			RECT rc;
			HDC hdc;
			LONG style;
			BOOL fMeasured = FALSE;
			hdc = GetDC(hwnd);
			if (hdc == NULL) return 0; /* text change failed */
			if (dat->hEnableFont != NULL) hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont);
			if (dat->hEnableFont == NULL || hPrevFont != NULL) /* select failed? */
				if (GetTextExtentPoint32(hdc, (TCHAR*)lParam, lstrlen((TCHAR*)lParam), &textSize))
					if (GetClientRect(hwnd, &rc)) {
						dat->rcText.top = 0;
						dat->rcText.bottom = dat->rcText.top+textSize.cy;
						style = GetWindowLongPtr(hwnd, GWL_STYLE);
						if (style&SS_CENTER) dat->rcText.left = (rc.right-textSize.cx)/2;
						else if (style&SS_RIGHT) dat->rcText.left = rc.right-textSize.cx;
						else dat->rcText.left = 0;
						dat->rcText.right = dat->rcText.left+textSize.cx;
						fMeasured = TRUE;
					}
			if (dat->hEnableFont != NULL && hPrevFont != NULL) SelectObject(hdc, hPrevFont);
			ReleaseDC(hwnd, hdc);
			if (!fMeasured) return 0; /* text change failed */
			SendMessage(hwnd, HLK_INVALIDATE, 0, 0);
			break;
		}}
		case WM_SETCURSOR:
		{	POINT pt;
			HCURSOR hCursor;
			if (!GetCursorPos(&pt)) return FALSE;
			if (!ScreenToClient(hwnd, &pt)) return FALSE;
			if (PtInRect(&dat->rcText, pt)) {
				hCursor = (HCURSOR)GetClassLongPtr(hwnd, GCLP_HCURSOR);
				if (hCursor == NULL) hCursor = LoadCursor(NULL, IDC_HAND); /* Win2000+ */
			}
			else hCursor = LoadCursor(NULL, IDC_ARROW);
			SetCursor(hCursor);
			return TRUE;
		}
		case HLK_SETENABLECOLOUR:
		{	COLORREF prevColor = dat->enableColor;
			dat->enableColor = (COLORREF)wParam;
			dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2);
			dat->flags|=HLKF_HASENABLECOLOR;
			return (LRESULT)prevColor;
		}
		case HLK_SETDISABLECOLOUR:
		{	COLORREF prevColor = dat->disableColor;
			dat->disableColor = (COLORREF)wParam;
			dat->flags|=HLKF_HASDISABLECOLOR;
			return (LRESULT)prevColor;
		}
		case WM_NCPAINT:
			return 0;
		case WM_PAINT:
		{	HFONT hPrevFont;
			RECT rc;
			TCHAR szText[256];
			UINT alignFlag;
			COLORREF textColor;
			PAINTSTRUCT ps;
			HDC hdc;

			hdc = BeginPaint(hwnd, &ps);
			if (hdc != NULL) {
				if (IsWindowEnabled(hwnd)) {
					hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont);
					textColor = (GetFocus() == hwnd) ? dat->focusColor : dat->enableColor;
				} else {
					hPrevFont = (HFONT)SelectObject(hdc, dat->hDisableFont);
					textColor = dat->disableColor;
				}
				if (GetClientRect(hwnd, &rc) && GetWindowText(hwnd, szText, SIZEOF(szText))) {
					BOOL fSmoothing;
					UINT fSmoothingType;
					SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fSmoothing, 0);
					SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fSmoothingType, 0);
					if (fSmoothing && fSmoothingType == FE_FONTSMOOTHINGCLEARTYPE)
						DrawThemeParentBackground(hwnd, hdc, &rc);
					SetBkMode(hdc, TRANSPARENT);
					SetTextColor(hdc, textColor);
					alignFlag = (GetWindowLongPtr(hwnd, GWL_STYLE)&(SS_CENTER|SS_RIGHT|SS_LEFT));
					DrawText(hdc, szText, -1, &rc, alignFlag|DT_NOPREFIX|DT_SINGLELINE|DT_TOP);
				}
				if (hPrevFont != NULL) SelectObject(hdc, hPrevFont);
				EndPaint(hwnd, &ps);
			}
			return 0;
		}
		case WM_NCDESTROY:
			if (dat->hEnableFont != NULL) DeleteObject(dat->hEnableFont);
			mir_free(dat);
			break;
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

int InitHyperlink(void)
{
	WNDCLASS wcl;

	wcl.lpfnWndProc = HyperlinkWndProc;
	wcl.cbClsExtra = 0;
	wcl.cbWndExtra = sizeof(struct HyperlinkWndData*);
	wcl.hInstance = hInst;
	wcl.hCursor = NULL;
	wcl.lpszClassName = WNDCLASS_HYPERLINK;
	wcl.hbrBackground = NULL;
	wcl.hIcon = NULL;
	wcl.lpszMenuName = NULL;
	wcl.style = CS_HREDRAW|CS_VREDRAW|CS_GLOBALCLASS|CS_PARENTDC;
	RegisterClass(&wcl);  /* automatically unregistered on exit */
	return 0;
}