/*

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

Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org),
Copyright (c) 2000-07 Miranda ICQ/IM project,
Copyright (c) 2007 Artem Shpynov

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 "commonheaders.h"

static void sttApplySkin(MODERNOPTOBJECT *obj, TCHAR *fn)
{
	ProtoCallService(obj->lpzThemeModuleName, TS_SKIN_APPLY, NULL, (LPARAM)fn);
}

static TCHAR *sttGetActiveSkin(MODERNOPTOBJECT *obj)
{
	return ProtoServiceExists(obj->lpzThemeModuleName, TS_SKIN_ACTIVE) ?
		(TCHAR*)ProtoCallService(obj->lpzThemeModuleName, TS_SKIN_ACTIVE, 0, 0) : 0;
}

static void sttPreviewSkin(MODERNOPTOBJECT *obj, TCHAR *fn, LPDRAWITEMSTRUCT lps)
{
	if (!fn) return;

	if ( ProtoServiceExists(obj->lpzThemeModuleName, TS_SKIN_PREVIEW)) {
		ProtoCallService(obj->lpzThemeModuleName, TS_SKIN_PREVIEW, (WPARAM)lps, (LPARAM)fn);
		return;
	}

	char *afn = mir_t2a(fn);
	char *fnpreview = (char *)mir_alloc(lstrlenA(afn) + 10);
	lstrcpyA(fnpreview, afn);
	lstrcatA(fnpreview, ".png");
	HBITMAP hbmPreview = (HBITMAP)CallService(MS_UTILS_LOADBITMAP, 0, (LPARAM)fnpreview);
	mir_free(afn);
	mir_free(fnpreview);

	if (!hbmPreview) return;

	BITMAP bmp;
	GetObject(hbmPreview, sizeof(bmp), &bmp);

	SIZE szDst = { abs(bmp.bmWidth), abs(bmp.bmHeight) };
	if ((szDst.cx > lps->rcItem.right-lps->rcItem.left) || (szDst.cy > lps->rcItem.bottom-lps->rcItem.top)) {
		float q = min(
			float(lps->rcItem.right-lps->rcItem.left) / szDst.cx,
			float(lps->rcItem.bottom-lps->rcItem.top) / szDst.cy);
		szDst.cx *= q;
		szDst.cy *= q;
	}
	POINT ptDst = {
		(lps->rcItem.left+lps->rcItem.right-szDst.cx) / 2,
		(lps->rcItem.top+lps->rcItem.bottom-szDst.cy) / 2 };

	HDC hdc = CreateCompatibleDC(lps->hDC);
	SelectObject(hdc, hbmPreview);
	SetStretchBltMode(hdc, HALFTONE);
	StretchBlt(lps->hDC, ptDst.x, ptDst.y, szDst.cx, szDst.cy, hdc, 0, 0, abs(bmp.bmWidth), abs(bmp.bmHeight), SRCCOPY);
	DeleteDC(hdc);
	DeleteObject(hbmPreview);
}

struct TSkinListItem
{
	TCHAR *path;
	TCHAR *title;
	TCHAR *filename;

	TSkinListItem(TCHAR *fn)
	{
		title = mir_tstrdup(fn);
		if (TCHAR *p = _tcsrchr(title, _T('.'))) *p = 0;

		TCHAR curPath[MAX_PATH];
		GetCurrentDirectory(SIZEOF(curPath), curPath);

		path = (TCHAR *)mir_alloc(MAX_PATH * sizeof(TCHAR));
		PathToRelativeT(curPath, path);

		int length = lstrlen(curPath)+lstrlen(fn)+2;
		filename = (TCHAR *)mir_alloc(length * sizeof(TCHAR));
		mir_sntprintf(filename, length, _T("%s\\%s"), curPath, fn);
	}

	~TSkinListItem()
	{
		mir_free(path);
		mir_free(title);
		mir_free(filename);
	}
};

struct TSelectorData
{
	MODERNOPTOBJECT *obj;
	TCHAR *active;
	HBITMAP hbmpPreview;

	TSelectorData()
	{
		ZeroMemory(this, sizeof(*this));
	}
	~TSelectorData()
	{
		mir_free(active);
		DeleteObject(hbmpPreview);
	}
};

static bool CheckExt(TCHAR *fn, TCHAR *ext, int n)
{
	int l = lstrlen(fn);
	return (l > n) && !lstrcmp(fn + l - n, ext);
}

static void BuildSkinList(HWND hwndList, TCHAR *szExt, int nExtLength = -1, bool start = true)
{
	if (start) {
		static TCHAR mirPath[MAX_PATH];
		GetModuleFileName(NULL, mirPath, SIZEOF(mirPath));
		if (TCHAR *p = _tcsrchr(mirPath, _T('\\'))) *p = 0;
		SetCurrentDirectory(mirPath);
		SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
		nExtLength = lstrlen(szExt);
		SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
	}

	WIN32_FIND_DATA ffd = {0};
	HANDLE h = FindFirstFile(_T("*.*"), &ffd);
	if (h != INVALID_HANDLE_VALUE) {
		do {
			if (!lstrcmp(ffd.cFileName, _T("")) || !lstrcmp(ffd.cFileName, _T(".")) || !lstrcmp(ffd.cFileName, _T("..")))
				continue;

			if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				SetCurrentDirectory(ffd.cFileName);
				BuildSkinList(hwndList, szExt, nExtLength, false);
				SetCurrentDirectory(_T(".."));
			}
			else {
				if (CheckExt(ffd.cFileName, szExt, nExtLength)) {
					TSkinListItem *dat = new TSkinListItem(ffd.cFileName);
					DWORD dwItem = SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)ffd.cFileName);
					SendMessage(hwndList, LB_SETITEMDATA, dwItem, (LPARAM)dat);
			}	}
		}
			while (FindNextFile(h, &ffd));
		FindClose(h);
	}

	if (start) {
		SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
		RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE);
	}
}

static void CreatePreview(TSelectorData *sd, TCHAR *fn, LPDRAWITEMSTRUCT lps)
{
	HDC hdc = CreateCompatibleDC(lps->hDC);
	sd->hbmpPreview = CreateCompatibleBitmap(lps->hDC, lps->rcItem.right - lps->rcItem.left, lps->rcItem.bottom - lps->rcItem.top);
	SelectObject(hdc, sd->hbmpPreview);

	BITMAPINFO bi = {0};
	bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
	bi.bmiHeader.biWidth = 8;
	bi.bmiHeader.biHeight = -8;
	bi.bmiHeader.biPlanes = 1;
	bi.bmiHeader.biBitCount = 32;
	bi.bmiHeader.biCompression = BI_RGB;

	HBITMAP hBmpBrush = (HBITMAP)CreateDIBSection(0, &bi, DIB_RGB_COLORS, 0, 0, 0);
	HDC dcBmp = CreateCompatibleDC(0);
	HBITMAP hBmpSave = (HBITMAP)SelectObject(dcBmp, hBmpBrush);
	HBRUSH hbr = CreateSolidBrush(RGB(0xcc, 0xcc, 0xcc));
	RECT rc;
	SetRect(&rc, 0, 0, 8, 8);
	FillRect(dcBmp, &rc, hbr);
	DeleteObject(hbr);
	hbr = CreateSolidBrush(RGB(0xff, 0xff, 0xff));
	SetRect(&rc, 4, 0, 8, 4);
	FillRect(dcBmp, &rc, hbr);
	SetRect(&rc, 0, 4, 4, 8);
	FillRect(dcBmp, &rc, hbr);
	DeleteObject(hbr);
	SelectObject(dcBmp, hBmpSave);
	DeleteDC(dcBmp);

	rc = lps->rcItem;
	OffsetRect(&rc, -rc.left, -rc.top);
	hbr = CreatePatternBrush(hBmpBrush);
	SetBrushOrgEx(hdc, 1, 1, 0);
	FillRect(hdc, &rc, hbr);
	DeleteObject(hbr);
	DeleteObject(hBmpBrush);

	HDC hdcSave = lps->hDC;
	lps->hDC = hdc;
	sttPreviewSkin(sd->obj, fn, lps);
	lps->hDC = hdcSave;

	FrameRect(hdc, &rc, GetStockBrush(LTGRAY_BRUSH));
	DeleteDC(hdc);
}

INT_PTR CALLBACK ModernOptSelector_DlgProc(HWND hwndDlg, UINT  msg, WPARAM wParam, LPARAM lParam)
{
	TSelectorData *sd = (TSelectorData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
	MODERNOPTOBJECT *&obj = sd->obj;

	switch (msg) {
	case WM_INITDIALOG:
		{
			sd = new TSelectorData;
			MODERNOPTOBJECT *&obj = sd->obj;

			sd->obj = (MODERNOPTOBJECT *)lParam;
			sd->active = sttGetActiveSkin(obj);
			SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)sd);

			TCHAR *s = mir_a2t(obj->lpzThemeExtension);
			BuildSkinList(GetDlgItem(hwndDlg, IDC_SKINLIST), s);
			mir_free(s);
		}
		return FALSE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDC_SKINLIST) {
			switch (HIWORD(wParam)) {
			case LBN_SELCHANGE:
				DeleteObject(sd->hbmpPreview);
				sd->hbmpPreview = 0;
				RedrawWindow(GetDlgItem(hwndDlg, IDC_PREVIEW1), NULL, NULL, RDW_INVALIDATE);
				break;

			case LBN_DBLCLK:
				{
					int idx = SendDlgItemMessage(hwndDlg, IDC_SKINLIST, LB_GETCURSEL, 0, 0);
					if (idx >= 0)
					{
						TSkinListItem *dat = (TSkinListItem *)SendDlgItemMessage(hwndDlg, IDC_SKINLIST, LB_GETITEMDATA, idx, 0);
						sttApplySkin(obj, dat->filename);
						mir_free(sd->active);
						sd->active = sttGetActiveSkin(obj);
						RedrawWindow(GetDlgItem(hwndDlg, IDC_SKINLIST), NULL, NULL, RDW_INVALIDATE);
					}
					break;
			}	}
			break;
		}
		return FALSE;

	case WM_MEASUREITEM:
		{
			LPMEASUREITEMSTRUCT lps = (LPMEASUREITEMSTRUCT)lParam;
			if (lps->CtlID != IDC_SKINLIST)
				break;
			TSkinListItem *dat = (TSkinListItem *)lps->itemData;
			if (!dat) break;

			lps->itemWidth = 10;
			lps->itemHeight = 30;

			return FALSE;
		}

	case WM_DRAWITEM:
		{
			LPDRAWITEMSTRUCT lps = (LPDRAWITEMSTRUCT)lParam;
			if (lps->CtlID == IDC_SKINLIST) {
				TSkinListItem *dat = (TSkinListItem *)lps->itemData;
				if (!dat) break;

				SetBkMode(lps->hDC, TRANSPARENT);
				COLORREF clLine1, clLine2, clBack;
				if (lps->itemState & ODS_SELECTED) {
					FillRect(lps->hDC, &lps->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
					clBack = GetSysColor(COLOR_HIGHLIGHT);
					clLine1 = GetSysColor(COLOR_HIGHLIGHTTEXT);
				}
				else {
					FillRect(lps->hDC, &lps->rcItem, GetSysColorBrush(COLOR_WINDOW));
					clBack = GetSysColor(COLOR_WINDOW);
					clLine1 = GetSysColor(COLOR_WINDOWTEXT);
				}
				clLine2 = RGB(
						GetRValue(clLine1) * 0.66 + GetRValue(clBack) * 0.34,
						GetGValue(clLine1) * 0.66 + GetGValue(clBack) * 0.34,
						GetBValue(clLine1) * 0.66 + GetBValue(clBack) * 0.34
					);

				lps->rcItem.left += 2;
				lps->rcItem.top += 2;
				lps->rcItem.bottom -= 2;
				lps->rcItem.right -= 5;

				int cxIcon = GetSystemMetrics(SM_CXSMICON);
				int cyIcon = GetSystemMetrics(SM_CYSMICON);

				if (sd->active && !lstrcmp(sd->active, dat->filename)) {
					DrawIconEx(lps->hDC, lps->rcItem.left, (lps->rcItem.top+lps->rcItem.bottom-cyIcon)/2,
						LoadSkinnedIcon(SKINICON_OTHER_EMPTYBLOB),
						cxIcon, cyIcon, 0, NULL, DI_NORMAL);
				}
				else {
					DrawIconEx(lps->hDC, lps->rcItem.left, (lps->rcItem.top+lps->rcItem.bottom-cyIcon)/2,
						LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT),
						cxIcon, cyIcon, 0, NULL, DI_NORMAL);
				}
				lps->rcItem.left += cxIcon;
				lps->rcItem.left += 5;

//				SelectObject(lps->hDC, dat->hfntTitle);
				SetTextColor(lps->hDC, clLine1);
				DrawText(lps->hDC, dat->title, -1, &lps->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS|DT_TOP);
				lps->rcItem.left += cxIcon;

				SetTextColor(lps->hDC, clLine2);
				DrawText(lps->hDC, dat->path, -1, &lps->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_PATH_ELLIPSIS|DT_BOTTOM);
			}
			else if (lps->CtlID == IDC_PREVIEW1) {
				int idx = SendDlgItemMessage(hwndDlg, IDC_SKINLIST, LB_GETCURSEL, 0, 0);

				if (!sd->hbmpPreview) {
					if (idx >= 0) {
						TSkinListItem *dat = (TSkinListItem *)SendDlgItemMessage(hwndDlg, IDC_SKINLIST, LB_GETITEMDATA, idx, 0);
						CreatePreview(sd, dat->filename, lps);
						//sttPreviewSkin(obj, dat->filename, lps);
					}
					else CreatePreview(sd, NULL, lps);
				}

				if (sd->hbmpPreview) {
					HDC hdc = CreateCompatibleDC(lps->hDC);
					SelectObject(hdc, sd->hbmpPreview);
					BitBlt(lps->hDC,
						lps->rcItem.left, lps->rcItem.top,
						lps->rcItem.right - lps->rcItem.left, lps->rcItem.bottom - lps->rcItem.top,
						hdc, 0, 0, SRCCOPY);
					DeleteDC(hdc);
				}
			}

			return TRUE;
		}

	case WM_DELETEITEM:
		{
			LPDELETEITEMSTRUCT lps = (LPDELETEITEMSTRUCT)lParam;
			if (lps->CtlID != IDC_SKINLIST) break;
			TSkinListItem *dat = (TSkinListItem *)lps->itemData;
			if (dat) delete dat;
			return FALSE;
		}

	case WM_DESTROY:
		delete sd;
		return FALSE;
	}
	return FALSE;
}