/*

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

Copyright (�) 2012-17 Miranda NG project (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/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.
*/

/*
This file contains code related to new modern free positioned skinned buttons
*/

#include "stdafx.h"
#include "modern_clcpaint.h"

#define MODERNSKINBUTTONCLASS "MirandaModernSkinButtonClass"
BOOL ModernSkinButtonModuleIsLoaded = FALSE;
static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
int ModernSkinButtonUnloadModule(WPARAM wParam, LPARAM lParam);
HWND SetToolTip(HWND hwnd, wchar_t * tip);

typedef struct _ModernSkinButtonCtrl
{
	HWND    hwnd;
	BYTE    down; // button state
	BYTE    focus;   // has focus (1 or 0)
	BYTE    hover;
	BYTE    IsSwitcher;
	BOOL    fCallOnPress;
	char    * ID;
	char    * CommandService;
	char    * StateService;
	char    * HandleService;
	char    * ValueDBSection;
	char    * ValueTypeDef;
	int     Left, Top, Bottom, Right;
	HMENU   hMenu;
	wchar_t   * Hint;

} ModernSkinButtonCtrl;
typedef struct _HandleServiceParams
{
	HWND    hwnd;
	DWORD   msg;
	WPARAM  wParam;
	LPARAM  lParam;
	BOOL    handled;
} HandleServiceParams;

static mir_cs csTips;
static HWND hwndToolTips = nullptr;

int ModernSkinButtonLoadModule()
{
	WNDCLASSEX wc;
	memset(&wc, 0, sizeof(wc));
	wc.cbSize = sizeof(wc);
	wc.lpszClassName = _A2W(MODERNSKINBUTTONCLASS);
	wc.lpfnWndProc = ModernSkinButtonWndProc;
	wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wc.cbWndExtra = sizeof(ModernSkinButtonCtrl*);
	wc.hbrBackground = 0;
	wc.style = CS_GLOBALCLASS;
	RegisterClassEx(&wc);
	ModernSkinButtonModuleIsLoaded = TRUE;
	return 0;
}

int ModernSkinButtonUnloadModule(WPARAM, LPARAM)
{
	return 0;
}

static int ModernSkinButtonPaintWorker(HWND hwnd, HDC whdc)
{
	HDC hdc;
	RECT rc;
	ModernSkinButtonCtrl* bct = (ModernSkinButtonCtrl *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
	if (!bct) return 0;
	if (!IsWindowVisible(hwnd)) return 0;
	if (!whdc && !g_CluiData.fLayered) InvalidateRect(hwnd, nullptr, FALSE);

	if (whdc && g_CluiData.fLayered) hdc = whdc;
	else {
		//sdc = GetWindowDC(GetParent(hwnd));
		hdc = CreateCompatibleDC(nullptr);
	}
	GetClientRect(hwnd, &rc);
	HBITMAP bmp = ske_CreateDIB32(rc.right, rc.bottom);
	HBITMAP oldbmp = (HBITMAP)SelectObject(hdc, bmp);
	if (!g_CluiData.fLayered)
		ske_BltBackImage(bct->hwnd, hdc, nullptr);
	{
		MODERNMASK Request = { 0 };
		//   int res;
		//HBRUSH br = CreateSolidBrush(RGB(255,255,255));
		char * Value = nullptr;
		{
			if (bct->ValueDBSection && bct->ValueTypeDef) {
				char * key;
				char * section;
				DWORD defval = 0;
				char buf[20];
				key = mir_strdup(bct->ValueDBSection);
				section = key;
				if (bct->ValueTypeDef[0] != 's')
					defval = (DWORD)atol(bct->ValueTypeDef + 1);
				do {
					if (key[0] == '/') { key[0] = '\0'; key++; break; }
					key++;
				} while (key[0] != '\0');
				switch (bct->ValueTypeDef[0]) {
				case 's':
					{
						Value = db_get_sa(0, section, key);
						if (!Value)
							Value = mir_strdup(bct->ValueTypeDef + 1);
						break;
					}
				case 'd':
					defval = db_get_dw(0, section, key, defval);
					Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
					break;
				case 'w':
					defval = db_get_w(0, section, key, defval);
					Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
					break;
				case 'b':
					defval = db_get_b(0, section, key, defval);
					Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
					break;
				}
				mir_free(section);
			}

		}
		g_clcPainter.AddParam(&Request, mod_CalcHash("Module"), "MButton", 0);
		g_clcPainter.AddParam(&Request, mod_CalcHash("ID"), bct->ID, 0);
		g_clcPainter.AddParam(&Request, mod_CalcHash("Down"), bct->down ? "1" : "0", 0);
		g_clcPainter.AddParam(&Request, mod_CalcHash("Focused"), bct->focus ? "1" : "0", 0);
		g_clcPainter.AddParam(&Request, mod_CalcHash("Hovered"), bct->hover ? "1" : "0", 0);
		if (Value) {
			g_clcPainter.AddParam(&Request, mod_CalcHash("Value"), Value, 0);
			mir_free(Value);
		}
		SkinDrawGlyphMask(hdc, &rc, &rc, &Request);
		SkinSelector_DeleteMask(&Request);
	}

	if (!whdc && g_CluiData.fLayered) {
		RECT r;
		SetRect(&r, bct->Left, bct->Top, bct->Right, bct->Bottom);
		ske_DrawImageAt(hdc, &r);
		//CallingService to immeadeately update window with new image.
	}
	if (whdc && !g_CluiData.fLayered) {
		RECT r = { 0 };
		GetClientRect(bct->hwnd, &r);
		BitBlt(whdc, 0, 0, r.right, r.bottom, hdc, 0, 0, SRCCOPY);
	}
	SelectObject(hdc, oldbmp);
	DeleteObject(bmp);
	if (!whdc || !g_CluiData.fLayered) {
		SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
		DeleteDC(hdc);
	}
	//  if (sdc)
	//    ReleaseDC(GetParent(hwnd),sdc);
	return 0;
}

static int ModernSkinButtonToggleDBValue(char * ValueDBSection, char *ValueTypeDef)
{
	if (ValueDBSection && ValueTypeDef) {
		char * key;
		char * section;
		char * val;
		char * val2;
		char * Value;
		long l1 = 0, l2 = 0, curval;
		//        char buf[20];
		key = mir_strdup(ValueDBSection);
		section = key;
		do {
			if (key[0] == '/') { key[0] = '\0'; key++; break; }
			key++;
		} while (key[0] != '\0');

		val = mir_strdup(ValueTypeDef + 1);
		val2 = val;
		do {
			if (val2[0] == '/') { val2[0] = '\0'; val2++; break; }
			val2++;
		} while (val2[0] != '\0');

		if (ValueTypeDef[0] != 's') {
			l1 = (DWORD)atol(val);
			l2 = (DWORD)atol(val2);
		}

		switch (ValueTypeDef[0]) {
		case 's':
			Value = db_get_sa(0, section, key);
			if (!Value || (Value && !mir_strcmpi(Value, val2)))
				Value = mir_strdup(val);
			else
				Value = mir_strdup(val2);
			db_set_s(0, section, key, Value);
			mir_free(Value);
			break;

		case 'd':
			curval = db_get_dw(0, section, key, l2);
			curval = (curval == l2) ? l1 : l2;
			db_set_dw(0, section, key, (DWORD)curval);
			break;

		case 'w':
			curval = db_get_w(0, section, key, l2);
			curval = (curval == l2) ? l1 : l2;
			db_set_w(0, section, key, (WORD)curval);
			break;

		case 'b':
			curval = db_get_b(0, section, key, l2);
			curval = (curval == l2) ? l1 : l2;
			db_set_b(0, section, key, (BYTE)curval);
			break;
		}
		mir_free(section);
		mir_free(val);
	}
	return 0;
}

static char *_skipblank(char * str) //str will be modified;
{
	char * endstr = str + mir_strlen(str);
	while ((*str == ' ' || *str == '\t') && *str != '\0') str++;
	while ((*endstr == ' ' || *endstr == '\t') && *endstr != '\0' && endstr < str) endstr--;
	if (*endstr != '\0') {
		endstr++;
		*endstr = '\0';
	}
	return str;
}

static int _CallServiceStrParams(IN char * toParce, OUT int *Return)
{
	int paramCount = 0;
	int result = 0;

	char *pszService = mir_strdup(toParce);
	if (!pszService)
		return 0;
	if (mir_strlen(pszService) == 0) {
		mir_free(pszService);
		return 0;
	}
	char *param2 = strrchr(pszService, '%');
	if (param2) {
		paramCount++;
		*param2 = '\0';	param2++;
		_skipblank(param2);
		if (mir_strlen(param2) == 0)
			param2 = nullptr;
	}
	char *param1 = strrchr(pszService, '%');
	if (param1) {
		paramCount++;
		*param1 = '\0';	param1++;
		_skipblank(param1);
		if (mir_strlen(param1) == 0)
			param1 = nullptr;
	}
	if (param1 && *param1 == '\"') {
		param1++;
		*(param1 + mir_strlen(param1)) = '\0';
	}
	else if (param1) {
		param1 = (char*)atoi(param1);
	}
	if (param2 && *param2 == '\"') {
		param2++;
		*(param2 + mir_strlen(param2)) = '\0';
	}
	else if (param2)
		param2 = (char*)atoi(param2);

	if (paramCount == 1) {
		param1 = param2;
		param2 = nullptr;
	}
	if (!ServiceExists(pszService)) {
		result = 0;
	}
	else {
		result = 1;
		int ret = CallService(pszService, (WPARAM)param1, (WPARAM)param2);
		if (Return) *Return = ret;
	}
	mir_free(pszService);
	return result;
}


static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	ModernSkinButtonCtrl* bct = (msg != WM_NCCREATE) ? (ModernSkinButtonCtrl *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA) : 0;
	if (bct) {
		if (bct->HandleService && IsBadStringPtrA(bct->HandleService, 255))
			bct->HandleService = nullptr;
		else if (bct->HandleService && ServiceExists(bct->HandleService)) {
			HandleServiceParams MSG = { 0 };
			MSG.hwnd = hwndDlg;
			MSG.msg = msg;
			MSG.wParam = wParam;
			MSG.lParam = lParam;
			int t = CallService(bct->HandleService, (WPARAM)&MSG, 0);
			if (MSG.handled) return t;
		}
	}

	switch (msg) {
	case WM_NCCREATE:
		SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) | BS_OWNERDRAW);
		SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
		if (((CREATESTRUCT *)lParam)->lpszName) SetWindowText(hwndDlg, ((CREATESTRUCT *)lParam)->lpszName);
		return TRUE;

	case WM_DESTROY:
		if (bct == nullptr)
			break;

		if (hwndToolTips) {
			mir_cslock lck(csTips);
			TOOLINFO ti = { 0 };
			ti.cbSize = sizeof(ti);
			ti.uFlags = TTF_IDISHWND;
			ti.hwnd = bct->hwnd;
			ti.uId = (UINT_PTR)bct->hwnd;
			if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti))
				SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);

			if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) {
				DestroyWindow(hwndToolTips);
				hwndToolTips = nullptr;
			}
		}
		mir_free(bct->ID);
		mir_free(bct->CommandService);
		mir_free(bct->StateService);
		mir_free(bct->HandleService);
		mir_free(bct->Hint);
		mir_free(bct->ValueDBSection);
		mir_free(bct->ValueTypeDef);
		mir_free(bct);
		SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
		break;	// DONT! fall thru

	case WM_SETCURSOR:
		{
			HCURSOR hCurs1 = LoadCursor(nullptr, IDC_ARROW);
			if (hCurs1) SetCursor(hCurs1);
			if (bct) SetToolTip(hwndDlg, bct->Hint);
		}
		return 1;

	case WM_PRINT:
		if (IsWindowVisible(hwndDlg))
			ModernSkinButtonPaintWorker(hwndDlg, (HDC)wParam);
		break;

	case WM_PAINT:
		if (IsWindowVisible(hwndDlg) && !g_CluiData.fLayered) {
			PAINTSTRUCT ps = { 0 };
			BeginPaint(hwndDlg, &ps);
			ModernSkinButtonPaintWorker(hwndDlg, (HDC)ps.hdc);
			EndPaint(hwndDlg, &ps);
		}
		return DefWindowProc(hwndDlg, msg, wParam, lParam);

	case WM_CAPTURECHANGED:
		if (bct) {
			bct->hover = 0;
			bct->down = 0;
			ModernSkinButtonPaintWorker(bct->hwnd, 0);
		}
		break;

	case WM_MOUSEMOVE:
		if (bct) {
			if (!bct->hover) {
				SetCapture(bct->hwnd);
				bct->hover = 1;
				ModernSkinButtonPaintWorker(bct->hwnd, 0);
			}
			else {
				POINT t = UNPACK_POINT(lParam);
				ClientToScreen(bct->hwnd, &t);
				if (WindowFromPoint(t) != bct->hwnd)
					ReleaseCapture();
			}
		}
		return 0;

	case WM_LBUTTONDOWN:
		if (bct) {
			bct->down = 1;
			SetForegroundWindow(GetParent(bct->hwnd));
			ModernSkinButtonPaintWorker(bct->hwnd, 0);
			if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255))
				bct->CommandService = nullptr;
			if (bct->fCallOnPress) {
				if (bct->CommandService) {
					if (!_CallServiceStrParams(bct->CommandService, nullptr) && (bct->ValueDBSection && bct->ValueTypeDef))
						ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef);
				}
				bct->down = 0;

				ModernSkinButtonPaintWorker(bct->hwnd, 0);
			}
		}
		return 0;

	case WM_LBUTTONUP:
		if (bct && bct->down) {
			ReleaseCapture();
			bct->hover = 0;
			bct->down = 0;
			ModernSkinButtonPaintWorker(bct->hwnd, 0);
			if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255))
				bct->CommandService = nullptr;
			if (bct->CommandService)
				if (_CallServiceStrParams(bct->CommandService, nullptr)) {
				}
				else if (bct->ValueDBSection && bct->ValueTypeDef)
					ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef);
		}
	}
	return DefWindowProc(hwndDlg, msg, wParam, lParam);
}

HWND SetToolTip(HWND hwnd, wchar_t * tip)
{
	TOOLINFO ti;
	if (!tip) return 0;
	mir_cslock lck(csTips);
	if (!hwndToolTips) {
		hwndToolTips = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr,
			WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
			CW_USEDEFAULT, CW_USEDEFAULT,
			CW_USEDEFAULT, CW_USEDEFAULT,
			hwnd, nullptr, g_hMirApp, nullptr);

		SetWindowPos(hwndToolTips, HWND_TOPMOST, 0, 0, 0, 0,
			SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
	}

	memset(&ti, 0, sizeof(ti));
	ti.cbSize = sizeof(ti);
	ti.uFlags = TTF_IDISHWND;
	ti.hwnd = hwnd;
	ti.uId = (UINT_PTR)hwnd;
	if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
		SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
	}
	ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
	ti.uId = (UINT_PTR)hwnd;
	ti.lpszText = (wchar_t*)tip;
	SendMessage(hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);

	return hwndToolTips;
}



typedef struct _MButton
{
	HWND hwnd;
	BYTE    ConstrainPositionFrom;  //(BBRRTTLL)  L = 0 - from left, L = 1 from right, L = 2 from center
	int OrL, OrR, OrT, OrB;
	int minW, minH;
	ModernSkinButtonCtrl * bct;

} MButton;
MButton * Buttons = nullptr;
DWORD ButtonsCount = 0;

#define _center_h( rc ) (((rc)->right + (rc)->left ) >> 1)
#define _center_v( rc ) (((rc)->bottom + (rc)->top ) >> 1)

int ModernSkinButton_AddButton(HWND parent,
	char * ID,
	char * CommandService,
	char * StateDefService,
	char * HandeService,
	int Left,
	int Top,
	int Right,
	int Bottom,
	DWORD sbFlags,
	wchar_t * Hint,
	char * DBkey,
	char * TypeDef,
	int MinWidth, int MinHeight)
{
	//  if (!parent) return 0;
	if (!ModernSkinButtonModuleIsLoaded) return 0;
	if (!Buttons)
		Buttons = (MButton*)mir_alloc(sizeof(MButton));
	else
		Buttons = (MButton*)mir_realloc(Buttons, sizeof(MButton)*(ButtonsCount + 1));
	{
		//HWND hwnd;
		RECT rc = { 0 };
		ModernSkinButtonCtrl* bct;
		int l, r, b, t;
		if (parent) GetClientRect(parent, &rc);
		l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Left) :
			(sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Left) :
			(rc.left + Left);

		t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Top) :
			(sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Top) :
			(rc.top + Top);

		r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Right) :
			(sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Right) :
			(rc.left + Right);

		b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Bottom) :
			(sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Bottom) :
			(rc.top + Bottom);
		bct = (ModernSkinButtonCtrl *)mir_alloc(sizeof(ModernSkinButtonCtrl));
		memset(bct, 0, sizeof(ModernSkinButtonCtrl));
		bct->Left = l;
		bct->Right = r;
		bct->Top = t;
		bct->Bottom = b;
		bct->fCallOnPress = (sbFlags & SBF_CALL_ON_PRESS) != 0;
		bct->HandleService = mir_strdup(HandeService);
		bct->CommandService = mir_strdup(CommandService);
		bct->StateService = mir_strdup(StateDefService);
		if (DBkey && *DBkey != '\0') bct->ValueDBSection = mir_strdup(DBkey); else bct->ValueDBSection = nullptr;
		if (TypeDef && *TypeDef != '\0') bct->ValueTypeDef = mir_strdup(TypeDef); else bct->ValueTypeDef = mir_strdup("sDefault");
		bct->ID = mir_strdup(ID);
		bct->Hint = mir_wstrdup(Hint);
		Buttons[ButtonsCount].bct = bct;
		Buttons[ButtonsCount].hwnd = nullptr;
		Buttons[ButtonsCount].OrL = Left;
		Buttons[ButtonsCount].OrT = Top;
		Buttons[ButtonsCount].OrR = Right;
		Buttons[ButtonsCount].OrB = Bottom;
		Buttons[ButtonsCount].ConstrainPositionFrom = (BYTE)sbFlags;
		Buttons[ButtonsCount].minH = MinHeight;
		Buttons[ButtonsCount].minW = MinWidth;
		ButtonsCount++;
		//  CLUI_ShowWindowMod(hwnd,SW_SHOW);
	}
	return 0;
}



static int ModernSkinButtonErase(int l, int t, int r, int b)
{
	DWORD i;
	if (!ModernSkinButtonModuleIsLoaded) return 0;
	if (!g_CluiData.fLayered) return 0;
	if (!g_pCachedWindow) return 0;
	if (!g_pCachedWindow->hImageDC || !g_pCachedWindow->hBackDC) return 0;
	if (!(l || r || t || b)) {
		for (i = 0; i < ButtonsCount; i++) {
			if (pcli->hwndContactList && Buttons[i].hwnd != nullptr) {
				//TODO: Erase button
				BitBlt(g_pCachedWindow->hImageDC, Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right - Buttons[i].bct->Left, Buttons[i].bct->Bottom - Buttons[i].bct->Top,
					g_pCachedWindow->hBackDC, Buttons[i].bct->Left, Buttons[i].bct->Top, SRCCOPY);
			}
		}
	}
	else {
		BitBlt(g_pCachedWindow->hImageDC, l, t, r - l, b - t, g_pCachedWindow->hBackDC, l, t, SRCCOPY);
	}
	return 0;
}

static HWND ModernSkinButtonCreateWindow(ModernSkinButtonCtrl * bct, HWND parent)
{
	HWND hwnd;

	if (bct == nullptr) return FALSE;
	{
		wchar_t *UnicodeID = mir_a2u(bct->ID);
		hwnd = CreateWindow(_A2W(MODERNSKINBUTTONCLASS), UnicodeID, WS_VISIBLE | WS_CHILD, bct->Left, bct->Top, bct->Right - bct->Left, bct->Bottom - bct->Top, parent, nullptr, g_hInst, nullptr);
		mir_free(UnicodeID);
	}

	bct->hwnd = hwnd;
	bct->focus = 0;
	SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)bct);
	return hwnd;
}

int ModernSkinButtonRedrawAll()
{
	if (!ModernSkinButtonModuleIsLoaded) return 0;
	g_mutex_bLockUpdating++;
	for (DWORD i = 0; i < ButtonsCount; i++) {
		if (pcli->hwndContactList && Buttons[i].hwnd == nullptr)
			Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, pcli->hwndContactList);
		ModernSkinButtonPaintWorker(Buttons[i].hwnd, 0);
	}
	g_mutex_bLockUpdating--;
	return 0;
}

int ModernSkinButtonDeleteAll()
{
	if (!ModernSkinButtonModuleIsLoaded)
		return 0;

	for (size_t i = 0; i < ButtonsCount; i++)
		if (Buttons[i].hwnd)
			DestroyWindow(Buttons[i].hwnd);

	mir_free_and_nil(Buttons);
	ButtonsCount = 0;
	return 0;
}

int ModernSkinButton_ReposButtons(HWND parent, BYTE draw, RECT *pRect)
{
	RECT rc, clr, rd;
	BOOL altDraw = FALSE;
	static SIZE oldWndSize = { 0 };
	if (!ModernSkinButtonModuleIsLoaded) return 0;
	GetWindowRect(parent, &rd);
	GetClientRect(parent, &clr);
	if (!pRect)
		GetWindowRect(parent, &rc);
	else
		rc = *pRect;
	
	if (g_CluiData.fLayered && (draw & SBRF_DO_ALT_DRAW)) {
		int sx, sy;
		sx = rd.right - rd.left;
		sy = rd.bottom - rd.top;
		if (sx != oldWndSize.cx || sy != oldWndSize.cy)
			altDraw = TRUE;//EraseButtons();
		oldWndSize.cx = sx;
		oldWndSize.cy = sy;
	}

	OffsetRect(&rc, -rc.left, -rc.top);
	rc.right = rc.left + (clr.right - clr.left);
	rc.bottom = rc.top + (clr.bottom - clr.top);
	for (DWORD i = 0; i < ButtonsCount; i++) {
		int sbFlags = Buttons[i].ConstrainPositionFrom;
		if (parent && Buttons[i].hwnd == nullptr) {
			Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, parent);
			altDraw = FALSE;
		}

		int l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Buttons[i].OrL) :
			(sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Buttons[i].OrL) :
			(rc.left + Buttons[i].OrL);

		int t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Buttons[i].OrT) :
			(sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Buttons[i].OrT) :
			(rc.top + Buttons[i].OrT);

		int r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Buttons[i].OrR) :
			(sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Buttons[i].OrR) :
			(rc.left + Buttons[i].OrR);

		int b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Buttons[i].OrB) :
			(sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Buttons[i].OrB) :
			(rc.top + Buttons[i].OrB);

		SetWindowPos(Buttons[i].hwnd, HWND_TOP, l, t, r - l, b - t, 0);
		if (rc.right - rc.left < Buttons[i].minW || rc.bottom - rc.top < Buttons[i].minH)
			CLUI_ShowWindowMod(Buttons[i].hwnd, SW_HIDE);
		else
			CLUI_ShowWindowMod(Buttons[i].hwnd, SW_SHOW);
		if ((1 || altDraw) &&
			(Buttons[i].bct->Left != l ||
				Buttons[i].bct->Top != t ||
				Buttons[i].bct->Right != r ||
				Buttons[i].bct->Bottom != b)) {
			//Need to erase in old location
			ModernSkinButtonErase(Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right, Buttons[i].bct->Bottom);
		}

		Buttons[i].bct->Left = l;
		Buttons[i].bct->Top = t;
		Buttons[i].bct->Right = r;
		Buttons[i].bct->Bottom = b;
	}

	if (draw & SBRF_DO_REDRAW_ALL)
		ModernSkinButtonRedrawAll();
	return 0;
}