#include "main.h"
#include "./graphics.h"

BYTE 
Graphics_GetSysFontQuality()
{
	BOOL smoothingEnabled;
	if (FALSE == SystemParametersInfoW(SPI_GETFONTSMOOTHING, 0, &smoothingEnabled, 0) ||
		FALSE == smoothingEnabled)
	{
		return DEFAULT_QUALITY;
	}
    
    OSVERSIONINFOW vi;
    vi.dwOSVersionInfoSize = sizeof(vi);
    if (FALSE == GetVersionExW(&vi)) 
		return DEFAULT_QUALITY;

	if (vi.dwMajorVersion > 5 || (vi.dwMajorVersion == 5 && vi.dwMinorVersion >= 1))
	{
		UINT smootingType;
	    if (FALSE == SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &smootingType, 0))
			return DEFAULT_QUALITY;
	    
	    if (FE_FONTSMOOTHINGCLEARTYPE == smootingType)
			return CLEARTYPE_NATURAL_QUALITY/*CLEARTYPE_QUALITY*/;
	}

	return ANTIALIASED_QUALITY;
}

HFONT 
Graphics_CreateSysFont()
{
	LOGFONTW lf = {0};
	HFONT font = NULL;

	if (FALSE == SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0))
		return NULL;
	
	lf.lfQuality = Graphics_GetSysFontQuality();
	font = CreateFontIndirectW(&lf);
	
	return font;
}

HFONT 
Graphics_DuplicateFont(HFONT sourceFont, INT heightDeltaPt, BOOL forceBold, BOOL systemQuality)
{
	LOGFONTW lf = {0};

	if (NULL == sourceFont) 
		return NULL;
	
	
	if (sizeof(lf) != GetObjectW(sourceFont, sizeof(lf), &lf))
		return NULL;

	if (0 != heightDeltaPt)
	{
		HDC hdcTmp = NULL, hdc = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE | DCX_NORESETATTRS);

		if (NULL != hdc)
		{
			hdcTmp = CreateCompatibleDC(hdc);
			ReleaseDC(NULL, hdc);
		}
		
		if (NULL == hdcTmp)
			return NULL;
		
		LONG pixelsY = GetDeviceCaps(hdcTmp, LOGPIXELSY);
		HFONT prevFont = SelectFont(hdcTmp, sourceFont);
		
		TEXTMETRICW tm = {0};
		if (FALSE != GetTextMetricsW(hdcTmp, &tm))
		{
			INT basePt = MulDiv(tm.tmHeight - tm.tmInternalLeading, 96, pixelsY);
			lf.lfHeight = -MulDiv((basePt + heightDeltaPt), pixelsY, 96);

		}

		SelectObject(hdcTmp, prevFont);
		DeleteDC(hdcTmp);
	}

	if (FALSE != systemQuality)
		lf.lfQuality = Graphics_GetSysFontQuality();
	if (FALSE != forceBold && lf.lfWeight < FW_BOLD)
		lf.lfWeight = FW_BOLD;

	return CreateFontIndirectW(&lf);
}

long 
Graphics_GetFontHeight(HDC hdc)
{
	TEXTMETRICW tm;
	if (FALSE == GetTextMetricsW(hdc, &tm))
		return 0;

	return tm.tmHeight;
}

long
Graphics_GetAveStrWidth(HDC hdc, UINT cchLen)
{
	const char szTest[] = 
	{ 
		'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z',
		'a','b','c','d','e','f','g','h','i','j','k','l', 'm','n','o','p','q','r','s','t','u','v','w','x','y','z'
	};

	SIZE textSize;
	if (FALSE == GetTextExtentPointA(hdc, szTest, ARRAYSIZE(szTest) -1, &textSize))
		return 0;

	LONG result;
	if (1 == cchLen)
	{
		result = (textSize.cx + ARRAYSIZE(szTest)/2)/ARRAYSIZE(szTest);
	}
	else
	{
		result = MulDiv(cchLen, textSize.cx + ARRAYSIZE(szTest)/2, ARRAYSIZE(szTest));
		if (0 != result)
		{
			TEXTMETRICW tm;
			if (FALSE != GetTextMetricsW(hdc, &tm))
				result += tm.tmOverhang;
		}
	}
	return result;
}

BOOL 
Graphics_GetWindowBaseUnits(HWND hwnd, LONG *baseUnitX, LONG *baseUnitY)
{
	BOOL result;
	result = FALSE;

	if (NULL != hwnd)
	{		
		HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
		if (NULL != hdc)
		{
			TEXTMETRICW tm;
			HFONT font, prevFont;
			
			font = (HFONT)SNDMSG(hwnd, WM_GETFONT, 0, 0L);
			prevFont = SelectFont(hdc, font);
			
			if (FALSE != GetTextMetricsW(hdc, &tm))
			{
				if (NULL != baseUnitX) 
					*baseUnitX = Graphics_GetAveStrWidth(hdc, 1);

				if (NULL != baseUnitY) 
					*baseUnitY = tm.tmHeight;

				result = TRUE;
			}

			SelectFont(hdc, prevFont);
			ReleaseDC(hwnd, hdc); 
		}
	}
	return result;
}


typedef int (*SkinColorFunc)(int idx);

COLORREF Graphics_GetSkinColor(unsigned int colorIndex)
{
	static SkinColorFunc GetSkinColor = NULL;
	
	if (NULL == GetSkinColor)
	{
		GetSkinColor = (SkinColorFunc)SENDMLIPC(Plugin_GetLibraryWindow(), ML_IPC_SKIN_WADLG_GETFUNC, 1);
		if (NULL == GetSkinColor)
			return RGB(255, 0, 255);
	}

	return GetSkinColor(colorIndex);
}

COLORREF 
Graphics_BlendColors(COLORREF rgbTop, COLORREF rgbBottom, INT alpha)
{
	if (alpha > 254) return rgbTop;
	if (alpha < 0) return rgbBottom;

	WORD k = (WORD)(((255 - alpha)*255 + 127)/255);
	
	return RGB( (GetRValue(rgbTop)*alpha + k*GetRValue(rgbBottom) + 127)/255, 
				(GetGValue(rgbTop)*alpha + k*GetGValue(rgbBottom) + 127)/255, 
				(GetBValue(rgbTop)*alpha + k*GetBValue(rgbBottom) + 127)/255);
}

INT 
Graphics_GetColorDistance(COLORREF rgb1, COLORREF rgb2)
{
	return (1000 * ((GetRValue(rgb1) - GetRValue(rgb2)) + 
			(GetGValue(rgb1) - GetGValue(rgb2)) +
			(GetBValue(rgb1) - GetBValue(rgb2))))/ (3 * 255);
}

void 
Graphics_ClampRect(RECT *rect, const RECT *boxRect)
{
	if (rect->left < boxRect->left)
		rect->left = boxRect->left;
	
	if (rect->top < boxRect->top)
		rect->top = boxRect->top;

	if (rect->right > boxRect->right)
		rect->right = boxRect->right;
	
	if (rect->bottom > boxRect->bottom)
		rect->bottom = boxRect->bottom;
}

void 
Graphics_NormalizeRect(RECT *rect)
{
	if (rect->top > rect->bottom)
		rect->bottom = rect->top;

	if (rect->left > rect->right)
		rect->right = rect->left;
}

void 
Graphics_GetRectSizeNormalized(const RECT *rect, SIZE *size)
{
	size->cx = rect->right - rect->left;
	if (size->cx < 0) 
		size->cx = 0;

	size->cy = rect->bottom - rect->top;
	if (size->cy < 0) 
		size->cy = 0;
}

BOOL 
Graphics_IsRectFit(const RECT *rect, const RECT *boxRect)
{
	if (rect->left < boxRect->left ||
		rect->top < boxRect->top ||
		rect->right > boxRect->right ||
		rect->bottom > boxRect->bottom)
	{
		return FALSE;
	}

	return TRUE;
}


BOOL SetSizeEmpty(SIZE *size)
{
	if (NULL == size)
		return FALSE;

	ZeroMemory(size, sizeof(SIZE));
	return TRUE;
}

BOOL IsSizeEmpty(SIZE *size)
{
	return (NULL == size || 0 == size->cx  || 0 == size->cy);
}

BOOL SetSize(SIZE *size, long width, long height)
{
	if (NULL == size)
		return FALSE;

	size->cx = width;
	size->cy = height;

	return TRUE;
}

BOOL SetPoint(POINT *pt, long x, long y)
{
	if (NULL == pt)
		return FALSE;

	pt->x = x;
	pt->y = y;

	return TRUE;
}

BOOL MakeRectPolygon(POINT vertices[4], long left, long top, long right, long bottom)
{
	if (NULL == vertices)
		return FALSE;

	vertices[0].x = left;
	vertices[0].y = top;
	vertices[1].x = right;
	vertices[1].y = top;
	vertices[2].x = right;
	vertices[2].y = bottom;
	vertices[3].x = left;
	vertices[3].y = bottom;

	return TRUE;
}