/*
Fingerprint Mod+ (client version) icons module for Miranda IM

Copyright � 2006-2007 Artem Shpynov aka FYR, Bio, Faith Healer. 2009-2010 HierOS

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.
*/

//Start of header
#include "global.h"
#include "fingerprints.h"		//Including of fingerprint masks

#define LIB_REG		2
#define LIB_USE		3

//#define USE_MY_SERVER
#define REGISTER_BETA
//#define REGISTER_AUTO

//#define mir_strncpy(a, b, c)		{ strncpy(a, b, c)[c - 1] = 0; }
//#define mir_tcsncpy_s(a, b, c, d)	{ _tcsncpy_s(a, b, c, d); c[d - 1] = 0; }
//#define _qtoupper(_c) (((_c) >= 'a' && (_c) <= 'z') ? ((_c) - 'a' + 'A') : (_c))
//#define _qtoupper(_c) (((_c) >= 'a' && (_c) <= 'z') ? ((_c) & 0x5F) : (_c))

void InitFingerEvents();
void FASTCALL ClearFI();

int OnIconsChanged(WPARAM wParam, LPARAM lParam);
int OnExtraIconClick(WPARAM wParam, LPARAM lParam);
int OnExtraIconListRebuild(WPARAM wParam, LPARAM lParam);
int OnExtraImageApply(WPARAM wParam, LPARAM lParam);
int OnContactSettingChanged(WPARAM wParam, LPARAM lParam);
int OnOptInitialise(WPARAM wParam, LPARAM lParam);
int OnModulesLoaded(WPARAM wParam, LPARAM lParam);
int OnPreShutdown(WPARAM wParam, LPARAM lParam);

INT_PTR ServiceSameClientsA(WPARAM wParam, LPARAM lParam);
INT_PTR ServiceGetClientIconA(WPARAM wParam, LPARAM lParam);
INT_PTR ServiceSameClientsW(WPARAM wParam, LPARAM lParam);
INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM lParam);


HICON FASTCALL CreateJoinedIcon(HICON hBottom, HICON hTop);
HBITMAP __inline CreateBitmap32(int cx, int cy);
HBITMAP FASTCALL CreateBitmap32Point(int cx, int cy, LPVOID* bits);
HANDLE FASTCALL GetIconIndexFromFI(LPTSTR szMirVer);

HICON FASTCALL LoadIconFromExternalFile(LPTSTR filename, int nLibrary, LPSTR IconName, int flag, LPTSTR Description, int internalidx, LPBOOL NeedFree);
BOOL FASTCALL WildCompareA(LPSTR name, LPSTR mask);
BOOL FASTCALL WildCompareW(LPWSTR name, LPWSTR mask);
BOOL __inline WildCompareProcA(LPSTR name, LPSTR mask);
BOOL __inline WildCompareProcW(LPWSTR name, LPWSTR mask);

#define WildCompare		WildCompareW
#define GetIconsIndexes	GetIconsIndexesW

HINSTANCE g_hInst;
int       hLangpack;

BOOL g_bExtraIcon_Register_ServiceExist		= FALSE;
BOOL g_bCList_Extra_Set_Icon_ServiceExist	= FALSE;

HANDLE hHeap					= NULL;
HANDLE hExtraImageListRebuild	= NULL;		// hook event handle for ME_CLIST_EXTRA_LIST_REBUILD
HANDLE hExtraImageApply			= NULL;		// hook event handle for ME_CLIST_EXTRA_IMAGE_APPLY
HANDLE hExtraIconClick			= NULL;		// hook event handle for ME_CLIST_EXTRA_CLICK

HANDLE compClientServA			= NULL;
HANDLE getClientIconA			= NULL;

HANDLE compClientServW			= NULL;
HANDLE getClientIconW			= NULL;
LPSTR  g_szClientDescription	= NULL;

HANDLE hStaticHooks[1]			= { NULL };
HANDLE hExtraIcon				= NULL;
HANDLE hFolderChanged			= NULL,		hIconFolder	= NULL;
TCHAR g_szSkinLib[MAX_PATH];

BYTE bColumn		= EXTRA_ICON_CLIENT;

FOUNDINFO* fiList	= NULL;
int nFICount		= 0;
BYTE gbUnicodeAPI;
UINT g_LPCodePage;

//End of header

// PluginInfo & PluginInfoEx
PLUGININFOEX pluginInfoEx = {
	sizeof(PLUGININFOEX),
	__INTERNAL_NAME_STRING,
	__VERSION_DWORD,
	"Fingerprint Mod+ (client version) icons module set extra icon of your buddies according to their client version.",
	"Artem Shpynov aka FYR, Bio, Faith Healer, HierOS",
	"crazy.hieros@gmail.com",
	__LEGAL_COPYRIGHT_STRING,
	"http://code.google.com/p/fingerprintmod/",
	UNICODE_AWARE,
	MIID_THIS_PLUGIN
};

static void LoadDBCheckState(HWND hwndDlg, int idCtrl, LPCSTR szSetting, BYTE bDef)
{
	CheckDlgButton(hwndDlg, idCtrl, DBGetContactSettingByte(NULL, "Finger", szSetting, bDef));
}

static void StoreDBCheckState(HWND hwndDlg, int idCtrl, LPCSTR szSetting)
{
	DBWriteContactSettingByte(NULL, "Finger", szSetting, (BYTE)IsDlgButtonChecked(hwndDlg, idCtrl));
}

static void OptDlgChanged(HWND hwndDlg, BOOL show)
{
	if (show)
		ShowWindow(GetDlgItem(hwndDlg, IDC_OPTCHANGENOTE), SW_SHOW);
	SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	g_hInst = hinstDLL;
	return TRUE;
}

extern "C"		__declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
{
	return &pluginInfoEx;
}

extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_FINGERPRINT, MIID_LAST };

extern "C" int	__declspec(dllexport) Load(void)
{

	mir_getLP(&pluginInfoEx);

	hStaticHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
	HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreShutdown);
	compClientServA = CreateServiceFunction(MS_FP_SAMECLIENTS, ServiceSameClientsA);
	getClientIconA = CreateServiceFunction(MS_FP_GETCLIENTICON, ServiceGetClientIconA);

	compClientServW = CreateServiceFunction(MS_FP_SAMECLIENTSW, ServiceSameClientsW);
	getClientIconW = CreateServiceFunction(MS_FP_GETCLIENTICONW, ServiceGetClientIconW);

	hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
	gbUnicodeAPI = IsWinVerNT();
	return 0;
}

extern "C" int	__declspec(dllexport) Unload()
{
	size_t i;

	if(g_szClientDescription != NULL) mir_free(g_szClientDescription);

	HeapDestroy(hHeap);
	ClearFI();

	for(i = 0; i < SIZEOF(hStaticHooks); i++)
	{
		UnhookEvent(hStaticHooks[i]);
		hStaticHooks[i] = NULL;
	}
	return 0;
}

/*
*	FreeIcon
*	for mode > 0 it releases icon from iconlib
*	for mode < 0 it destroys icon
*/
void FASTCALL FreeIcon(HICON hIcon, BOOL mode)
{
	if (!mode) return;
	if(mode > 0)
		CallService(MS_SKIN2_RELEASEICON, (WPARAM)hIcon, (WPARAM)0);
	else
		DestroyIcon(hIcon);
}

/*
*	Prepare
*	prepares upperstring masks and registers them in IcoLib
*/
void FASTCALL Prepare(KN_FP_MASK* mask)
{
	if (!mask->szMask) return;
	size_t iMaskLen;
	LPTSTR pszNewMask;
	LPTSTR pszTranslatedMask;

	if(mask == &def_kn_fp_mask[UNKNOWN_MASK_NUMBER])
		pszTranslatedMask = TranslateTS(mask->szMask);
	else
		pszTranslatedMask = mask->szMask;

	iMaskLen = _tcslen(pszTranslatedMask) + 1;
	pszNewMask = (LPTSTR)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, iMaskLen * sizeof(TCHAR));

	_tcscpy_s(pszNewMask, iMaskLen, pszTranslatedMask);
	_tcsupr_s(pszNewMask, iMaskLen);
	mask->szMask = pszNewMask;
	LoadIconFromExternalFile(mask->szIconFileName, LIB_REG, mask->szIconName, mask->iSectionFlag, mask->szClientDescription, mask->iIconIndex, NULL);
}

/*
*	OnModulesLoaded
*	Hook necessary events here
*/
int OnModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	int i;

	g_LPCodePage = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0);

	g_bExtraIcon_Register_ServiceExist		= ServiceExists(MS_EXTRAICON_REGISTER);
	g_bCList_Extra_Set_Icon_ServiceExist	= ServiceExists(MS_CLIST_EXTRA_SET_ICON);

	//Hook necessary events
	HookEvent(ME_SKIN2_ICONSCHANGED, OnIconsChanged);
	HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged);
	HookEvent(ME_OPT_INITIALISE, OnOptInitialise);

	if(g_bExtraIcon_Register_ServiceExist)
	{
		EXTRAICON_INFO ico = { 0 };

		ico.cbSize = sizeof(ico);
		ico.type = EXTRAICON_TYPE_CALLBACK;
		ico.RebuildIcons = OnExtraIconListRebuild;
		ico.ApplyIcon = OnExtraImageApply;
		ico.OnClick = (MIRANDAHOOKPARAM)OnExtraIconClick;
		ico.name = "Client";
		ico.description= "Fingerprint";
		ico.descIcon = "client_Miranda_Unknown";
		hExtraIcon = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
	}
	else
	{
		InitFingerEvents();
	}

	if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
	{
		hIconFolder = FoldersRegisterCustomPathT("Fingerprint", "Icons", _T(MIRANDA_PATH) _T("\\") DEFAULT_SKIN_FOLDER);
		FoldersGetCustomPathT(hIconFolder, g_szSkinLib, SIZEOF(g_szSkinLib), _T(""));
	}
	else
		CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)DEFAULT_SKIN_FOLDER, (LPARAM)g_szSkinLib);


	// prepare masks
	KN_FP_MASK* mask;

	for(i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++)
	{
		mask = &def_kn_fp_mask[i];
		if(mask) Prepare(mask);
	}
	if(DBGetContactSettingByte(NULL, "Finger", "Overlay1", 1))
	{
		for(i = 0; i < DEFAULT_KN_FP_OVERLAYS_COUNT; i++)
		{
			mask = &def_kn_fp_overlays_mask[i];
			if(mask) Prepare(mask);
		}
	}
	if(DBGetContactSettingByte(NULL, "Finger", "Overlay2", 1))
	{
		if (DBGetContactSettingByte(NULL, "Finger", "ShowVersion", 0))
		{
			for(i = 0; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
			{
				mask = &def_kn_fp_overlays2_mask[i];
				if(mask) Prepare(mask);
			}
		}
		else
		{
			for(i = 0; i < DEFAULT_KN_FP_OVERLAYS2_NO_VER_COUNT; i++)
			{
				mask = &def_kn_fp_overlays2_mask[i];
				if(mask) Prepare(mask);
			}
		}
	}
	if(DBGetContactSettingByte(NULL, "Finger", "Overlay3", 1))
	{
		for(i = 0; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
		{
			mask = &def_kn_fp_overlays3_mask[i];
			if(mask) Prepare(mask);
		}
	}
	return 0;
}

int OnExtraIconClicked(WPARAM wParam, LPARAM lParam)
{
	if(lParam == bColumn)
		CallService(MS_USERINFO_SHOWDIALOG, wParam, NULL);
	return 0;
}

/*
*	 OnPreShutdown
*	 Unhook events here (this is valid place to unhook all events to prevent crash on exiting)
*/
int OnPreShutdown(WPARAM wParam, LPARAM lParam)
{
	DestroyServiceFunction(compClientServA);
	DestroyServiceFunction(getClientIconA);
	DestroyServiceFunction(compClientServW);
	DestroyServiceFunction(getClientIconW);

	return 0;
}

/*	ApplyFingerprintImage
*	 1)Try to find appropriate mask
*	 2)Register icon in extraimage list if not yet registered (0xFF)
*	 3)Set ExtraImage for contact
*/
int FASTCALL ApplyFingerprintImage(HANDLE hContact, LPTSTR szMirVer)
{
	HANDLE hImage = INVALID_HANDLE_VALUE;

	if(szMirVer)
		hImage = GetIconIndexFromFI(szMirVer);

	if(hContact == NULL) return 0;

	if(g_bCList_Extra_Set_Icon_ServiceExist && !g_bExtraIcon_Register_ServiceExist)
	{
		IconExtraColumn iec;
		WORD bColumn = DBGetContactSettingWord(NULL, "Finger", "Column", EXTRA_ICON_CLIENT);

		if(bColumn <= 0 || bColumn > EXTRA_ICON_COUNT)
		{
			bColumn = EXTRA_ICON_CLIENT;
		}

		iec.cbSize = sizeof(IconExtraColumn);
		iec.hImage = hImage;
		iec.ColumnType = bColumn;

		CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
	}
	else if(g_bExtraIcon_Register_ServiceExist && hExtraIcon != INVALID_HANDLE_VALUE && hExtraIcon != NULL)
	{
		EXTRAICON ei = { 0 };

		ei.cbSize = sizeof(ei);
		ei.hExtraIcon = hExtraIcon;
		ei.hContact = hContact;
		ei.hImage = hImage;

		CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ei, 0);
	}
	return 0;
}

void InitFingerEvents()
{
	HookEvent(ME_CLIST_EXTRA_CLICK, OnExtraIconClicked);
	HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, OnExtraIconListRebuild);
	HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, OnExtraImageApply);
}

int OnExtraIconClick(WPARAM wParam, LPARAM lParam)
{
	CallService(MS_USERINFO_SHOWDIALOG, wParam, NULL);
	return 0;
}

/*
*	OnExtraIconListRebuild
*	Set all registered indexes in array to 0xFF (unregistered icon)
*/
int OnExtraIconListRebuild(WPARAM wParam, LPARAM lParam)
{
	ClearFI();
	return 0;
}

/*
*	OnIconsChanged
*/
int OnIconsChanged(WPARAM wParam, LPARAM lParam)
{
	ClearFI();
	return 0;
}

/*
*	 OnExtraImageApply
*	 Try to get MirVer value from db for contact and if success calls ApplyFingerprintImage
*/
int OnExtraImageApply(WPARAM wParam, LPARAM lParam)
{
	HANDLE hContact = (HANDLE)wParam;

	if(hContact == NULL) return 0;
	char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0);

	if(szProto!=NULL)
	{
		DBVARIANT dbvMirVer = { 0 };

		if (!DBGetContactSettingTString(hContact, szProto, "MirVer", &dbvMirVer))
		{
			ApplyFingerprintImage(hContact, dbvMirVer.ptszVal);
			DBFreeVariant(&dbvMirVer);
		}
		else
			ApplyFingerprintImage(hContact, NULL);
	}
	else
		ApplyFingerprintImage(hContact, NULL);
	return 0;
}

/*
*	 OnContactSettingChanged
*	 if contact settings changed apply new image or remove it
*/
int OnContactSettingChanged(WPARAM wParam, LPARAM lParam)
{
	if ((HANDLE)wParam == NULL)	return 0;
	{
		DBCONTACTWRITESETTING* cws = (DBCONTACTWRITESETTING*)lParam;
		if(cws && cws->szSetting && !strcmp(cws->szSetting, "MirVer"))
		{
			if(cws->value.type == DBVT_UTF8)
			{

				LPWSTR wszVal = NULL;
				int iValLen;

				iValLen = MultiByteToWideChar(CP_UTF8, 0, cws->value.pszVal, -1, NULL, 0);
				if(iValLen > 0)
				{
					wszVal = (LPWSTR)mir_alloc(iValLen * sizeof(WCHAR));
					MultiByteToWideChar(CP_UTF8, 0, cws->value.pszVal, -1, wszVal, iValLen);
				}
				ApplyFingerprintImage((HANDLE)wParam, wszVal);
				mir_free(wszVal);

			}
			else if(cws->value.type == DBVT_ASCIIZ)
			{

				LPWSTR wszVal = NULL;
				int iValLen;

				iValLen = MultiByteToWideChar(g_LPCodePage, 0, cws->value.pszVal, -1, NULL, 0);
				if(iValLen > 0)
				{
					wszVal = (LPWSTR)mir_alloc(iValLen * sizeof(WCHAR));
					MultiByteToWideChar(g_LPCodePage, 0, cws->value.pszVal, -1, wszVal, iValLen);
				}
				ApplyFingerprintImage((HANDLE)wParam, wszVal);
				mir_free(wszVal);

			}
			else if(cws->value.type == DBVT_WCHAR)
			{

				ApplyFingerprintImage((HANDLE)wParam, cws->value.pwszVal);

			}
			else
				ApplyFingerprintImage((HANDLE)wParam, NULL);
		}
	}
	return 0;
}

/*
*	 LoadIconFromExternalFile
*	 If iconlib module presents register icon there
*	 Register and return icon within iconlib
*	 or from resourse
*/
//	 TO DO: Extracting icons from clienticons.dll or other external files require futher
//	 destroying of icon... need to add field to list, modify it and remove icon on unload
//	 Otherwise it will cause gdi resources leaking.
//	 So nowtime it is commented out

HICON FASTCALL LoadIconFromExternalFile(LPTSTR filename, int nLibrary, LPSTR IconName, int flag, LPTSTR Description, int internalidx, LPBOOL NeedFree)
{
	HICON hIcon = NULL;
	TCHAR destfile[MAX_PATH];

	if(IconName == NULL) return NULL;

	if (filename == _T(""))
		GetModuleFileName(g_hInst,destfile,MAX_PATH);
	else
	{
		mir_sntprintf(destfile, SIZEOF(destfile), _T("%s\\%s.dll"), g_szSkinLib, filename);
		struct _stat64i32 stFileInfo;

		if (_tstat(destfile, &stFileInfo) == -1)
			return hIcon;
	}

	if(nLibrary == LIB_USE)
	{
		hIcon = ((HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)IconName));
		if (!hIcon)
		{
			ExtractIconEx(destfile, -internalidx, NULL, &hIcon, 1);
			return hIcon;
		}
	}
	else
	{
		SKINICONDESC sid;
		LPTSTR SectName = NULL;

		switch(flag)
		{
			#include "finger_groups.h"
		}

		if(SectName == NULL)
			return hIcon;

		ZeroMemory(&sid, sizeof(sid));

		sid.cbSize = sizeof(sid);
		sid.flags = SIDF_ALL_TCHAR;
		sid.ptszSection = SectName;
		sid.pszName = IconName;
		sid.ptszDescription = Description;
		sid.ptszDefaultFile = destfile;
		sid.iDefaultIndex = -internalidx;
		sid.cx = sid.cy = 16;
		Skin_AddIcon(&sid);

		// hIcon = ((HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)IconName));
	}

	if(NeedFree)
		*NeedFree = (BOOL)hIcon;
	return hIcon;
}

/*
*	WildCompareA
*	Compare 'name' string with 'mask' strings.
*	Masks can contain '*' or '?' wild symbols
*	Asterics '*' symbol covers 'empty' symbol too e.g WildCompare("Tst","T*st*"), returns TRUE
*	In order to handle situation 'at least one any sybol' use "?*" combination:
*	e.g WildCompare("Tst","T?*st*"), returns FALSE, but both WildCompare("Test","T?*st*") and
*	WildCompare("Teeest","T?*st*") return TRUE.
*
*	Function is case sensitive! so convert input or modify func to use _qtoupper()
*
*	Mask can contain several submasks. In this case each submask (including first)
*	should start from '|' e.g: "|first*submask|second*mask".
*
*	Dec 25, 2006 by FYR:
*	Added Exception to masks: the mask "|^mask3|mask2|mask1" means:
*	if NOT according to mask 3 AND (mask1 OR mask2)
*	EXCEPTION should be BEFORE main mask:
*		IF Exception match - the comparing stops as FALSE
*		IF Exception does not match - comparing continue
*		IF Mask match - comparing stops as TRUE
*		IF Mask does not not match comparing continue
*/
BOOL FASTCALL WildCompareA(LPSTR szName, LPSTR szMask)
{
	if (*szMask != '|') return WildCompareProcA(szName, szMask);
	{
		size_t s = 1, e = 1;
//		static char temp[100];		//lets made temp static local var - should be faster than dynamic
		LPSTR szTemp = (LPSTR)_alloca(strlen(szMask) * sizeof(CHAR) + sizeof(CHAR));
		BOOL bExcept;

		while(szMask[e] != '\0')
		{
			s = e;
			while(szMask[e] != '\0' && szMask[e] != '|') e++;

			// exception mask
			bExcept = (*(szMask + s) == '^');
			if(bExcept) s++;

			memcpy(szTemp, szMask + s, (e - s) * sizeof(CHAR));
			szTemp[e - s] = '\0';

			if(WildCompareProcA(szName, szTemp))
				return !bExcept;

			if(szMask[e] != '\0')
				e++;
			else
				return FALSE;
		}
		return FALSE;
	}
}

/*
*	WildCompareW
*	Compare 'name' string with 'mask' strings.
*	Masks can contain '*' or '?' wild symbols
*	Asterics '*' symbol covers 'empty' symbol too e.g WildCompare("Tst","T*st*"), returns TRUE
*	In order to handle situation 'at least one any sybol' use "?*" combination:
*	e.g WildCompare("Tst","T?*st*"), returns FALSE, but both WildCompare("Test","T?*st*") and
*	WildCompare("Teeest","T?*st*") return TRUE.
*
*	Function is case sensitive! so convert input or modify func to use _qtoupper()
*
*	Mask can contain several submasks. In this case each submask (including first)
*	should start from '|' e.g: "|first*submask|second*mask".
*
*	Dec 25, 2006 by FYR:
*	Added Exception to masks: the mask "|^mask3|mask2|mask1" means:
*	if NOT according to mask 3 AND (mask1 OR mask2)
*	EXCEPTION should be BEFORE main mask:
*		IF Exception match - the comparing stops as FALSE
*		IF Exception does not match - comparing continue
*		IF Mask match - comparing stops as TRUE
*		IF Mask does not not match comparing continue
*/
BOOL FASTCALL WildCompareW(LPWSTR wszName, LPWSTR wszMask)
{
	if (*wszMask != L'|') return WildCompareProcW(wszName, wszMask);
	{
		size_t s = 1, e = 1;
//		static char temp[100];		//lets made temp static local var - should be faster than dynamic
		LPWSTR wszTemp = (LPWSTR)_alloca(wcslen(wszMask) * sizeof(WCHAR) + sizeof(WCHAR));
		BOOL bExcept;

		while(wszMask[e] != L'\0')
		{
			s = e;
			while(wszMask[e] != L'\0' && wszMask[e] != L'|') e++;

			// exception mask
			bExcept = (*(wszMask + s) == L'^');
			if(bExcept) s++;

			memcpy(wszTemp, wszMask + s, (e - s) * sizeof(WCHAR));
			wszTemp[e - s] = L'\0';

			if(WildCompareProcW(wszName, wszTemp))
				return !bExcept;

			if(wszMask[e] != L'\0')
				e++;
			else
				return FALSE;
		}
		return FALSE;
	}
}

INT_PTR CALLBACK DlgProcOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_INITDIALOG:
		{
			TranslateDialogDefault(hwndDlg);
			for (int i = 0; i < DEFAULT_SETTINGS_COUNT; i++)
			{
				if (lstrcmpA(settings[i].szSetName, "ShowVersion") == 0)
					LoadDBCheckState(hwndDlg, settings[i].idCtrl, settings[i].szSetName, 0);
				else
					LoadDBCheckState(hwndDlg, settings[i].idCtrl, settings[i].szSetName, 1);
			}
			LPTSTR CIdComboBox[] =
			{
				_T("Email"),
				_T("Protocol"),
				_T("SMS"),
				_T("Advanced 1"),
				_T("Advanced 2"),
				_T("Web"),
				_T("Client (default)"),
				_T("VisMode"),
				_T("Advanced 3"),
				_T("Advanced 4")
			};

			for(int i = 0; i < SIZEOF(CIdComboBox); i++)
				ComboBoxAddString(GetDlgItem(hwndDlg, IDC_ADVICON), CIdComboBox[i], i);

			SendDlgItemMessage(hwndDlg, IDC_ADVICON, CB_SETCURSEL, (DBGetContactSettingWord(NULL, "Finger", "Column", EXTRA_ICON_CLIENT)) - 1, 0);
			if(g_bExtraIcon_Register_ServiceExist)
				EnableWindow(GetDlgItem(hwndDlg, IDC_ADVICON), FALSE);

			ShowWindow(GetDlgItem(hwndDlg, IDC_OPTCHANGENOTE), SW_HIDE);
		}
		break;
	case WM_COMMAND:
		switch(LOWORD(wParam))
		{
			BOOL show;
		case IDC_ADVICON:
			if(HIWORD(wParam) == CBN_SELCHANGE)
				OptDlgChanged(hwndDlg, true);
			break;
		case IDC_OVERLAY1:
		case IDC_OVERLAY2:
		case IDC_OVERLAY3:
		case IDC_VERSION:
			show = true;
			OptDlgChanged(hwndDlg, show);
			break;

		case IDC_GROUPMIRANDA:
		case IDC_GROUPMULTI:
		case IDC_GROUPPACKS:
		case IDC_GROUPOTHERS:
		case IDC_GROUPAIM:
		case IDC_GROUPGADU:
		case IDC_GROUPICQ:
		case IDC_GROUPIRC:
		case IDC_GROUPJABBER:
		case IDC_GROUPMAIL:
		case IDC_GROUPMSN:
		case IDC_GROUPQQ:
		case IDC_GROUPRSS:
		case IDC_GROUPSKYPE:
		case IDC_GROUPTLEN:
		case IDC_GROUPVOIP:
		case IDC_GROUPWEATHER:
		case IDC_GROUPYAHOO:
			show = false;
			OptDlgChanged(hwndDlg, show);
			break;
		default:
			return 0;
		}
		break;
	case WM_NOTIFY:
		{
			NMHDR *hdr = (NMHDR *)lParam;
			if (hdr && hdr->code == PSN_APPLY)
			{
				int i = SendDlgItemMessage(hwndDlg, IDC_ADVICON, CB_GETCURSEL, 0, 0) + 1;
				DBWriteContactSettingWord(NULL, "Finger", "Column", (WORD)i);

				// prepare masks
				KN_FP_MASK* mask;

				for(i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++)
				{
					mask = &def_kn_fp_mask[i];
					if(mask) CallService(MS_SKIN2_REMOVEICON, 0, (LPARAM)mask->szIconName);
				}
				if(DBGetContactSettingByte(NULL, "Finger", "Overlay1", 1))
				{
					for(i = 0; i < DEFAULT_KN_FP_OVERLAYS_COUNT; i++)
					{
						mask = &def_kn_fp_overlays_mask[i];
						if(mask) CallService(MS_SKIN2_REMOVEICON, 0, (LPARAM)mask->szIconName);
					}
				}
				if(DBGetContactSettingByte(NULL, "Finger", "Overlay2", 1))
				{
					if (DBGetContactSettingByte(NULL, "Finger", "ShowVersion", 0))
					{
						for(i = 0; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
						{
							mask = &def_kn_fp_overlays2_mask[i];
							if(mask) CallService(MS_SKIN2_REMOVEICON, 0, (LPARAM)mask->szIconName);
						}
					}
					else
					{
						for(i = 0; i < DEFAULT_KN_FP_OVERLAYS2_NO_VER_COUNT; i++)
						{
							mask = &def_kn_fp_overlays2_mask[i];
							if(mask) CallService(MS_SKIN2_REMOVEICON, 0, (LPARAM)mask->szIconName);
						}
					}
				}
				if(DBGetContactSettingByte(NULL, "Finger", "Overlay3", 1))
				{
					for(i = 0; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
					{
						mask = &def_kn_fp_overlays3_mask[i];
						if(mask) CallService(MS_SKIN2_REMOVEICON, 0, (LPARAM)mask->szIconName);
					}
				}

				for (int i = 0; i < DEFAULT_SETTINGS_COUNT; i++)
					StoreDBCheckState(hwndDlg, settings[i].idCtrl, settings[i].szSetName);

				for(i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++)
				{
					mask = &def_kn_fp_mask[i];
					if(mask) Prepare(mask);
				}
				if(DBGetContactSettingByte(NULL, "Finger", "Overlay1", 1))
				{
					for(i = 0; i < DEFAULT_KN_FP_OVERLAYS_COUNT; i++)
					{
						mask = &def_kn_fp_overlays_mask[i];
						if(mask) Prepare(mask);
					}
				}
				if(DBGetContactSettingByte(NULL, "Finger", "Overlay2", 1))
				{
					if (DBGetContactSettingByte(NULL, "Finger", "ShowVersion", 0))
					{
						for(i = 0; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
						{
							mask = &def_kn_fp_overlays2_mask[i];
							if(mask) Prepare(mask);
						}
					}
					else
					{
						for(i = 0; i < DEFAULT_KN_FP_OVERLAYS2_NO_VER_COUNT; i++)
						{
							mask = &def_kn_fp_overlays2_mask[i];
							if(mask) Prepare(mask);
						}
					}
				}
				if(DBGetContactSettingByte(NULL, "Finger", "Overlay3", 1))
				{
					for(i = 0; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
					{
						mask = &def_kn_fp_overlays3_mask[i];
						if(mask) Prepare(mask);
					}
				}
			}
			break;
		}
	case WM_DESTROY:
		break;
	}
	return FALSE;
}
int OnOptInitialise(WPARAM wParam, LPARAM lParam)
{
	OPTIONSDIALOGPAGE odp = { 0 };
	odp.cbSize = sizeof(odp);
	odp.hInstance = g_hInst;
	odp.pszGroup = LPGEN("Customize");
	odp.pszTemplate = MAKEINTRESOURCEA(IDD_DIALOG);
	odp.pszTitle = LPGEN("Fingerprint");
	odp.pfnDlgProc = DlgProcOptions;
	odp.flags = ODPF_BOLDGROUPS;
	Options_AddPage(wParam, &odp);

	return 0;
}

BOOL __inline WildCompareProcA(LPSTR szName, LPSTR szMask)
{
	LPSTR szLast = NULL;
	for (;; szMask++, szName++)
	{
		if (*szMask != '?' && *szMask != *szName) break;
		if (*szName == '\0') return ((BOOL)!*szMask);
	}
	if (*szMask != '*') return FALSE;
	for (;; szMask++, szName++)
	{
		while(*szMask == '*')
		{
			szLast = szMask++;
			if (*szMask == '\0') return ((BOOL)!*szMask);	/* true */
		}
		if (*szName == '\0') return ((BOOL)!*szMask);		/* *mask == EOS */
		if (*szMask != '?' && *szMask != *szName && szLast != NULL)
		{
			szName -= (size_t)(szMask - szLast) - 1;
			szMask = szLast;
		}
	}
}

BOOL __inline WildCompareProcW(LPWSTR wszName, LPWSTR wszMask)
{
	LPWSTR wszLast = NULL;
	for (;; wszMask++, wszName++)
	{
		if (*wszMask != L'?' && *wszMask != *wszName) break;
		if (*wszName == L'\0') return ((BOOL)!*wszMask);
	}
	if (*wszMask != L'*') return FALSE;
	for (;; wszMask++, wszName++)
	{
		while(*wszMask == L'*')
		{
			wszLast = wszMask++;
			if (*wszMask == L'\0') return ((BOOL)!*wszMask);	/* true */
		}
		if (*wszName == L'\0') return ((BOOL)!*wszMask);		/* *mask == EOS */
		if (*wszMask != L'?' && *wszMask != *wszName && wszLast != NULL)
		{
			wszName -= (size_t)(wszMask - wszLast) - 1;
			wszMask = wszLast;
		}
	}
}

/*	GetIconsIndexesA
*	Retrieves Icons indexes by Mirver
*/
void FASTCALL GetIconsIndexesA(LPSTR szMirVer, short *base, short *overlay,short *overlay2,short *overlay3)
{
	LPTSTR tszMirVerUp;
	int iMirVerUpLen;
	short i = 0, j = -1, k = -1, n = -1;

	if(strcmp(szMirVer, "?") == 0)
	{
		*base = UNKNOWN_MASK_NUMBER;
		*overlay = -1;
		*overlay2 = -1;
		*overlay3 = -1;
		return;
	}


	iMirVerUpLen = MultiByteToWideChar(g_LPCodePage, 0, szMirVer, -1, NULL, 0);

	tszMirVerUp = (LPTSTR)mir_alloc(iMirVerUpLen * sizeof(TCHAR));

	MultiByteToWideChar(g_LPCodePage, 0, szMirVer, -1, tszMirVerUp, iMirVerUpLen);

	_tcsupr_s(tszMirVerUp, iMirVerUpLen);

	while(i < DEFAULT_KN_FP_MASK_COUNT)
	{
		if(WildCompare(tszMirVerUp, def_kn_fp_mask[i].szMask))
		{
			if (def_kn_fp_mask[i].szIconFileName != _T(""))
			{
				TCHAR destfile[MAX_PATH];
				mir_sntprintf(destfile, SIZEOF(destfile), _T("%s\\%s.dll"), g_szSkinLib, def_kn_fp_mask[i].szIconFileName);
				struct _stat64i32 stFileInfo;

				if (_tstat(destfile, &stFileInfo) == -1)
					i = NOTFOUND_MASK_NUMBER;
			}
			break;
		}
		i++;
	}
	if (!def_kn_fp_mask[i].fNotUseOverlay && i < DEFAULT_KN_FP_MASK_COUNT)
	{
		j = 0;
		while(j < DEFAULT_KN_FP_OVERLAYS_COUNT)
		{
			if(WildCompare(tszMirVerUp, def_kn_fp_overlays_mask[j].szMask))
			{
				if (def_kn_fp_overlays_mask[j].szIconFileName == _T("ClientIcons_Packs"))
				{
					TCHAR destfile[MAX_PATH];
					mir_sntprintf(destfile, SIZEOF(destfile), _T("%s\\%s.dll"), g_szSkinLib, def_kn_fp_overlays_mask[i].szIconFileName);
					struct _stat64i32 stFileInfo;

					if (_tstat(destfile, &stFileInfo) == -1)
						j++;
					else
						break;
				}
				else
					break;
			}
			j++;
		}
		k = 0;
		while(k < DEFAULT_KN_FP_OVERLAYS2_COUNT)
		{
			if(WildCompare(tszMirVerUp, def_kn_fp_overlays2_mask[k].szMask)) break;
			k++;
		}
		n = 0;
		while(n < DEFAULT_KN_FP_OVERLAYS3_COUNT)
		{
			if(WildCompare(tszMirVerUp, def_kn_fp_overlays3_mask[n].szMask)) break;
			n++;
		}
	}
	*base = (i < DEFAULT_KN_FP_MASK_COUNT) ? i : -1;
	*overlay = (j < DEFAULT_KN_FP_OVERLAYS_COUNT) ? j : -1;
	*overlay2 = (k < DEFAULT_KN_FP_OVERLAYS2_COUNT) ? k : -1;
	*overlay3 = (n < DEFAULT_KN_FP_OVERLAYS3_COUNT) ? n : -1;

	mir_free(tszMirVerUp);
}

/*	GetIconsIndexesW
*	Retrieves Icons indexes by Mirver
*/

void FASTCALL GetIconsIndexesW(LPWSTR wszMirVer, short *base, short *overlay,short *overlay2,short *overlay3)
{
	LPWSTR wszMirVerUp;
	size_t iMirVerUpLen;
	short i = 0, j = -1, k = -1, n = -1;

	if(wcscmp(wszMirVer, L"?") == 0)
	{
		*base = UNKNOWN_MASK_NUMBER;
		*overlay = -1;
		*overlay2 = -1;
		*overlay3 = -1;
		return;
	}

	iMirVerUpLen = wcslen(wszMirVer) + 1;
	wszMirVerUp = (LPWSTR)mir_alloc(iMirVerUpLen * sizeof(WCHAR));

	wcscpy_s(wszMirVerUp, iMirVerUpLen, wszMirVer);
	_wcsupr_s(wszMirVerUp, iMirVerUpLen);

	while(i < DEFAULT_KN_FP_MASK_COUNT)
	{
		if(WildCompareW(wszMirVerUp, def_kn_fp_mask[i].szMask))
		{
			if (def_kn_fp_mask[i].szIconFileName != _T(""))
			{
				TCHAR destfile[MAX_PATH];
				mir_sntprintf(destfile, SIZEOF(destfile), _T("%s\\%s.dll"), g_szSkinLib, def_kn_fp_mask[i].szIconFileName);
				struct _stat64i32 stFileInfo;

				if (_tstat(destfile, &stFileInfo) == -1)
					i = NOTFOUND_MASK_NUMBER;
			}
			break;
		}
		i++;
	}
	if (!def_kn_fp_mask[i].fNotUseOverlay && i < DEFAULT_KN_FP_MASK_COUNT)
	{
		j = 0;
		while(j < DEFAULT_KN_FP_OVERLAYS_COUNT)
		{
			if(WildCompare(wszMirVerUp, def_kn_fp_overlays_mask[j].szMask))
			{
				if (def_kn_fp_overlays_mask[j].szIconFileName == _T("ClientIcons_Packs"))
				{
					TCHAR destfile[MAX_PATH];
					mir_sntprintf(destfile, SIZEOF(destfile), _T("%s\\%s.dll"), g_szSkinLib, def_kn_fp_overlays_mask[j].szIconFileName);
					struct _stat64i32 stFileInfo;

					if (_tstat(destfile, &stFileInfo) == -1)
						j++;
					else
						break;
				}
				else
					break;
			}
			j++;
		}
		k = 0;
		while(k < DEFAULT_KN_FP_OVERLAYS2_COUNT)
		{
			if(WildCompareW(wszMirVerUp, def_kn_fp_overlays2_mask[k].szMask)) break;
			k++;
		}
		n = 0;
		while(n < DEFAULT_KN_FP_OVERLAYS3_COUNT)
		{
			if(WildCompareW(wszMirVerUp, def_kn_fp_overlays3_mask[n].szMask)) break;
			n++;
		}
	}
	*base = (i < DEFAULT_KN_FP_MASK_COUNT) ? i : -1;
	*overlay = (j < DEFAULT_KN_FP_OVERLAYS_COUNT) ? j : -1;
	*overlay2 = (k < DEFAULT_KN_FP_OVERLAYS2_COUNT) ? k : -1;
	*overlay3 = (n < DEFAULT_KN_FP_OVERLAYS3_COUNT) ? n : -1;

	mir_free(wszMirVerUp);
}


/*
* CreateIconFromIndexes
* returns hIcon of joined icon by given indexes
*/
HICON FASTCALL CreateIconFromIndexes(short base, short overlay, short overlay2, short overlay3)
{
	HICON hIcon = NULL;	// returned HICON
	HICON hTmp = NULL;
	HICON icMain = NULL;
	HICON icOverlay = NULL;
	HICON icOverlay2 = NULL;
	HICON icOverlay3 = NULL;

	BOOL needFreeBase = FALSE;
	BOOL needFreeOverlay = FALSE;
	BOOL needFreeOverlay2 = FALSE;
	BOOL needFreeOverlay3 = FALSE;

	KN_FP_MASK* mainMask = &(def_kn_fp_mask[base]);
	icMain = LoadIconFromExternalFile(mainMask->szIconFileName, LIB_USE, mainMask->szIconName, mainMask->iSectionFlag, mainMask->szClientDescription, mainMask->iIconIndex, &needFreeBase);

	if(icMain)
	{
		KN_FP_MASK* overlayMask = (overlay != -1) ? &(def_kn_fp_overlays_mask[overlay]) : NULL;
		KN_FP_MASK* overlay2Mask = (overlay2 != -1) ? &(def_kn_fp_overlays2_mask[overlay2]) : NULL;
		KN_FP_MASK* overlay3Mask = (overlay3 != -1) ? &(def_kn_fp_overlays3_mask[overlay3]) : NULL;
		icOverlay = (overlayMask == NULL) ? NULL : LoadIconFromExternalFile(overlayMask->szIconFileName, LIB_USE, overlayMask->szIconName, overlayMask->iSectionFlag, overlayMask->szClientDescription, overlayMask->iIconIndex, &needFreeOverlay);
		icOverlay2 = (overlay2Mask == NULL) ? NULL : LoadIconFromExternalFile(overlay2Mask->szIconFileName, LIB_USE, overlay2Mask->szIconName, overlay2Mask->iSectionFlag, overlay2Mask->szClientDescription, overlay2Mask->iIconIndex, &needFreeOverlay2);
		icOverlay3 = (overlay3Mask == NULL) ? NULL : LoadIconFromExternalFile(overlay3Mask->szIconFileName, LIB_USE, overlay3Mask->szIconName, overlay3Mask->iSectionFlag, overlay3Mask->szClientDescription, overlay3Mask->iIconIndex, &needFreeOverlay3);

		hIcon = icMain;

		if(overlayMask)
		{
			hIcon = CreateJoinedIcon(hIcon, icOverlay);
			hTmp = hIcon;
		}

		if(overlay2Mask)
		{
			hIcon = CreateJoinedIcon(hIcon, icOverlay2);
			if(hTmp) DestroyIcon(hTmp);
			hTmp = hIcon;
		}

		if(overlay3Mask)
		{
			hIcon = CreateJoinedIcon(hIcon, icOverlay3);
			if(hTmp) DestroyIcon(hTmp);
		}
	}

	if(hIcon == icMain)
		hIcon = CopyIcon(icMain);

	FreeIcon(icMain, needFreeBase);
	FreeIcon(icOverlay, needFreeOverlay);
	FreeIcon(icOverlay2, needFreeOverlay2);
	FreeIcon(icOverlay3, needFreeOverlay3);

	return hIcon;
}

/*
*	ServiceGetClientIconA
*	MS_FP_GETCLIENTICON service implementation.
*	wParam - char * MirVer value to get client for.
*	lParam - int noCopy - if wParam is equal to "1"	will return icon handler without copiing icon.
*	ICON IS ALWAYS COPIED!!!
*/
INT_PTR ServiceGetClientIconA(WPARAM wParam, LPARAM lParam)
{
	LPSTR szMirVer = (LPSTR)wParam;			// MirVer value to get client for.
/*
	static HICON hIcon = NULL;	// returned HICON
	if(hIcon)
	{
		DestroyIcon(hIcon);
		hIcon = NULL;
	}
*/

	if(szMirVer == NULL) return 0;

	{
		HICON hIcon = NULL;			// returned HICON
		int NoCopy = (int)lParam;	// noCopy
		short base, overlay, overlay2, overlay3;

		GetIconsIndexesA(szMirVer, &base, &overlay, &overlay2, &overlay3);

		if(base != -1)
		{
			hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3);
		}
/*
		if(hIcon && !NoCopy)
			return (INT_PTR)CopyIcon(hIcon);
*/
		return (INT_PTR)hIcon;
	}
}

/*
 *	 ServiceSameClientA
 *	 MS_FP_SAMECLIENTS service implementation.
 *	 wParam - char * first MirVer value
 *	 lParam - char * second MirVer value
 *	 return pointer to char string - client desription (do not destroy) if clients are same
 */
INT_PTR ServiceSameClientsA(WPARAM wParam, LPARAM lParam)
{
	LPSTR szMirVerFirst = (LPSTR)wParam;	// MirVer value to get client for.
	LPSTR szMirVerSecond = (LPSTR)lParam;	// MirVer value to get client for.
	int firstIndex, secondIndex;
	BOOL Result = FALSE;

	firstIndex = secondIndex = 0;
	if (!szMirVerFirst || !szMirVerSecond) return (INT_PTR)NULL;	//one of its is not null

	{
		LPTSTR tszMirVerFirstUp, tszMirVerSecondUp;
		int iMirVerFirstUpLen, iMirVerSecondUpLen;

		iMirVerFirstUpLen = MultiByteToWideChar(g_LPCodePage, 0, szMirVerFirst, -1, NULL, 0);
		iMirVerSecondUpLen = MultiByteToWideChar(g_LPCodePage, 0, szMirVerSecond, -1, NULL, 0);

		tszMirVerFirstUp = (LPTSTR)mir_alloc(iMirVerFirstUpLen * sizeof(TCHAR));
		tszMirVerSecondUp = (LPTSTR)mir_alloc(iMirVerSecondUpLen * sizeof(TCHAR));

		MultiByteToWideChar(g_LPCodePage, 0, szMirVerFirst, -1, tszMirVerFirstUp, iMirVerFirstUpLen);
		MultiByteToWideChar(g_LPCodePage, 0, szMirVerSecond, -1, tszMirVerSecondUp, iMirVerSecondUpLen);

		_tcsupr_s(tszMirVerFirstUp, iMirVerFirstUpLen);
		_tcsupr_s(tszMirVerSecondUp, iMirVerSecondUpLen);

		if(_tcscmp(tszMirVerFirstUp, _T("?")) == 0)
			firstIndex = UNKNOWN_MASK_NUMBER;
		else
			while(firstIndex < DEFAULT_KN_FP_MASK_COUNT)
			{
				if(WildCompare(tszMirVerFirstUp, def_kn_fp_mask[firstIndex].szMask))
					break;
				firstIndex++;
			}
		if(_tcscmp(tszMirVerSecondUp, _T("?")) == 0)
			secondIndex = UNKNOWN_MASK_NUMBER;
		else
			while(secondIndex < DEFAULT_KN_FP_MASK_COUNT)
			{
				if(WildCompare(tszMirVerSecondUp, def_kn_fp_mask[secondIndex].szMask))
					break;
	 			secondIndex++;
			}

		mir_free(tszMirVerFirstUp);
		mir_free(tszMirVerSecondUp);

		if(firstIndex == secondIndex && firstIndex < DEFAULT_KN_FP_MASK_COUNT)
		{

			int iClientDescriptionLen = WideCharToMultiByte(g_LPCodePage, 0, def_kn_fp_mask[firstIndex].szClientDescription, -1, NULL, 0, NULL, NULL);
			if(iClientDescriptionLen > 0)
				g_szClientDescription = (LPSTR)mir_realloc(g_szClientDescription, iClientDescriptionLen * sizeof(CHAR));
			else
				return (INT_PTR)NULL;
			WideCharToMultiByte(g_LPCodePage, 0, def_kn_fp_mask[firstIndex].szClientDescription, -1, g_szClientDescription, iClientDescriptionLen, NULL, NULL);
			return (INT_PTR)g_szClientDescription;

		}
	}
	return (INT_PTR)NULL;
}

/*
*	ServiceGetClientIconW
*	MS_FP_GETCLIENTICONW service implementation.
*	wParam - LPWSTR MirVer value to get client for.
*	lParam - int noCopy - if wParam is equal to "1"	will return icon handler without copiing icon.
*	ICON IS ALWAYS COPIED!!!
*/

INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM lParam)
{
	LPWSTR wszMirVer = (LPWSTR)wParam;			// MirVer value to get client for.
/*
	static HICON hIcon = NULL;	// returned HICON
	if(hIcon)
	{
		DestroyIcon(hIcon);
		hIcon = NULL;
	}
*/

	if(wszMirVer == NULL) return 0;

	{
		HICON hIcon = NULL;			// returned HICON
		int NoCopy = (int)lParam;	// noCopy
		short base, overlay, overlay2, overlay3;

		GetIconsIndexesW(wszMirVer, &base, &overlay, &overlay2, &overlay3);

		if(base != -1)
		{
			hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3);
		}
/*
		if(hIcon && !NoCopy)
			return (INT_PTR)CopyIcon(hIcon);
*/
		return (INT_PTR)hIcon;
	}
}

/*
 *	 ServiceSameClientW
 *	 MS_FP_SAMECLIENTSW service implementation.
 *	 wParam - LPWSTR first MirVer value
 *	 lParam - LPWSTR second MirVer value
 *	 return pointer to char string - client desription (do not destroy) if clients are same
 */
INT_PTR ServiceSameClientsW(WPARAM wParam, LPARAM lParam)
{
	LPWSTR wszMirVerFirst = (LPWSTR)wParam;	// MirVer value to get client for.
	LPWSTR wszMirVerSecond = (LPWSTR)lParam;	// MirVer value to get client for.
	int firstIndex, secondIndex;
	BOOL Result = FALSE;

	firstIndex = secondIndex = 0;
	if (!wszMirVerFirst || !wszMirVerSecond) return (INT_PTR)NULL;	//one of its is not null

	{
		LPWSTR wszMirVerFirstUp, wszMirVerSecondUp;
		size_t iMirVerFirstUpLen, iMirVerSecondUpLen;

		iMirVerFirstUpLen = wcslen(wszMirVerFirst) + 1;
		iMirVerSecondUpLen = wcslen(wszMirVerSecond) + 1;

		wszMirVerFirstUp = (LPWSTR)mir_alloc(iMirVerFirstUpLen * sizeof(WCHAR));
		wszMirVerSecondUp = (LPWSTR)mir_alloc(iMirVerSecondUpLen * sizeof(WCHAR));

		wcscpy_s(wszMirVerFirstUp, iMirVerFirstUpLen, wszMirVerFirst);
		wcscpy_s(wszMirVerSecondUp, iMirVerSecondUpLen, wszMirVerSecond);

		_wcsupr_s(wszMirVerFirstUp, iMirVerFirstUpLen);
		_wcsupr_s(wszMirVerSecondUp, iMirVerSecondUpLen);

		if(wcscmp(wszMirVerFirstUp, L"?") == 0)
			firstIndex = UNKNOWN_MASK_NUMBER;
		else
			while(firstIndex < DEFAULT_KN_FP_MASK_COUNT)
			{
				if(WildCompareW(wszMirVerFirstUp, def_kn_fp_mask[firstIndex].szMask))
					break;
				firstIndex++;
			}
		if(wcscmp(wszMirVerSecondUp, L"?") == 0)
			secondIndex = UNKNOWN_MASK_NUMBER;
		else
			while(secondIndex < DEFAULT_KN_FP_MASK_COUNT)
			{
				if(WildCompareW(wszMirVerSecondUp, def_kn_fp_mask[secondIndex].szMask))
					break;
	 			secondIndex++;
			}

		mir_free(wszMirVerFirstUp);
		mir_free(wszMirVerSecondUp);

		if(firstIndex == secondIndex && firstIndex < DEFAULT_KN_FP_MASK_COUNT)
		{
			return (INT_PTR)def_kn_fp_mask[firstIndex].szClientDescription;
		}
	}
	return (INT_PTR)NULL;
}

/******************************************************************************
 *	Futher routines is for creating joined 'overlay' icons.
 ******************************************************************************/

 /*
 *	CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy
 */

HBITMAP __inline CreateBitmap32(int cx, int cy)
{
	return CreateBitmap32Point(cx, cy, NULL);
}

 /*
 *	CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy and put reference
 *				to new bitmap pixel image memory area to void ** bits
 */
HBITMAP FASTCALL CreateBitmap32Point(int cx, int cy, LPVOID* bits)
{
	BITMAPINFO bmpi = { 0 };
	LPVOID ptPixels = NULL;
	HBITMAP DirectBitmap;

	if(cx < 0 || cy < 0) return NULL;

	bmpi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmpi.bmiHeader.biWidth = cx;
	bmpi.bmiHeader.biHeight = cy;
	bmpi.bmiHeader.biPlanes = 1;
//	bmpi.bmiHeader.biCompression = BI_RGB;
	bmpi.bmiHeader.biBitCount = 32;

	DirectBitmap = CreateDIBSection(NULL,
					&bmpi,
					DIB_RGB_COLORS,
					&ptPixels,
					NULL, 0);
 	GdiFlush();
	if(ptPixels) memset(ptPixels, 0, cx * cy * 4);
	if(bits != NULL) *bits = ptPixels;

	return DirectBitmap;
}

/*
*	 checkHasAlfa - checks if image has at least one BYTE in alpha channel
*				 that is not a 0. (is image real 32 bit or just 24 bit)
*/
BOOL FASTCALL checkHasAlfa(LPBYTE from, int width, int height)
{
	LPDWORD pt = (LPDWORD)from;
	LPDWORD lim = pt + width * height;
	while(pt < lim)
	{
		if (*pt & 0xFF000000)
			return TRUE;
		pt++;
	}

	return FALSE;
}

/*
*	 checkMaskUsed - checks if mask image has at least one that is not a 0.
*	 Not sure is it required or not
*/
BOOL FASTCALL checkMaskUsed(LPBYTE from)
{
	int i;
	for(i = 0; i < 16 * 16 / 8; i++)
	{
		if(from[i] != 0) return TRUE;
	}
	return FALSE;
}

/*
*	 GetMaskBit - return value of apropriate mask bit in line at x position
*/
BOOL __inline GetMaskBit(LPBYTE line, int x)
{
	return ((*(line + (x >> 3))) & (0x01 << (7 - (x & 0x07)))) != 0;
}

/*
*	blend	- alpha blend ARGB values of 2 pixels. X1 - underlaying,
*	 X2 - overlaying points.
*/
DWORD FASTCALL blend(DWORD X1,DWORD X2)
{
	RGBA* q1 = (RGBA*)&X1;
	RGBA* q2 = (RGBA*)&X2;
	BYTE a_1 = ~q1->a;
	BYTE a_2 = ~q2->a;
	WORD am = q1->a * a_2;

	WORD ar = q1->a + ((a_1 * q2->a) / 255);
	// if a2 more than 0 than result should be more
	// or equal (if a1==0) to a2, else in combination
	// with mask we can get here black points

	ar = (q2->a > ar) ? q2->a : ar;

	if(ar == 0) return 0;

	{
		WORD arm = ar * 255;
		WORD rr = ((q1->r * am + q2->r * q2->a * 255)) / arm;
		WORD gr = ((q1->g * am + q2->g * q2->a * 255)) / arm;
		WORD br = ((q1->b * am + q2->b * q2->a * 255)) / arm;
		return (ar << 24) | (rr << 16) | (gr << 8) | br;
	}
}

/*
*	CreateJoinedIcon	- creates new icon by drawing hTop over hBottom.
*/
HICON FASTCALL CreateJoinedIcon(HICON hBottom, HICON hTop)
{
	BOOL drawn = FALSE;
	HDC tempDC, tempDC2, tempDC3;
	HICON res = NULL;
	HBITMAP oImage,nImage;
	HBITMAP nMask, hbm, obmp, obmp2;
	LPBYTE ptPixels = NULL;
	ICONINFO iNew = { 0 };
	BYTE p[32] = { 0 };

	tempDC = CreateCompatibleDC(NULL);
	nImage = CreateBitmap32Point(16, 16, (LPVOID*)&ptPixels);
	oImage = (HBITMAP)SelectObject(tempDC, nImage);

//	if(ptPixels) memset(ptPixels, 0, 16 * 16 * 4);

	if(IsWinVerXPPlus())
	{
		ICONINFO iciBottom = { 0 };
		ICONINFO iciTop = { 0 };

		BITMAP bmp_top = { 0 };
		BITMAP bmp_top_mask = { 0 };

		BITMAP bmp_bottom = { 0 };
		BITMAP bmp_bottom_mask = { 0 };

		GetIconInfo(hBottom, &iciBottom);
		GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom);
		GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask);

		GetIconInfo(hTop, &iciTop);
		GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top);
		GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask);

		if(bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32)
		{
			LPBYTE BottomBuffer, TopBuffer, BottomMaskBuffer, TopMaskBuffer;
			LPBYTE bb, tb, bmb, tmb;
			LPBYTE db = ptPixels;
			int vstep_d = 16 * 4;
			int vstep_b = bmp_bottom.bmWidthBytes;
			int vstep_t = bmp_top.bmWidthBytes;
			int vstep_bm = bmp_bottom_mask.bmWidthBytes;
			int vstep_tm = bmp_top_mask.bmWidthBytes;

			if(bmp_bottom.bmBits)
				bb = BottomBuffer = (LPBYTE)bmp_bottom.bmBits;
			else
			{
				BottomBuffer = (LPBYTE)_alloca(bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes);
				GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes, BottomBuffer);
				bb = BottomBuffer + vstep_b * (bmp_bottom.bmHeight - 1);
				vstep_b = -vstep_b;
			}
			if(bmp_top.bmBits)
				tb = TopBuffer = (LPBYTE)bmp_top.bmBits;
			else
			{
				TopBuffer = (LPBYTE)_alloca(bmp_top.bmHeight * bmp_top.bmWidthBytes);
				GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight * bmp_top.bmWidthBytes, TopBuffer);
				tb = TopBuffer + vstep_t * (bmp_top.bmHeight - 1);
				vstep_t = -vstep_t;
			}
			if(bmp_bottom_mask.bmBits)
				bmb = BottomMaskBuffer = (LPBYTE)bmp_bottom_mask.bmBits;
			else
			{
				BottomMaskBuffer = (LPBYTE)_alloca(bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes);
				GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer);
				bmb = BottomMaskBuffer + vstep_bm * (bmp_bottom_mask.bmHeight - 1);
				vstep_bm = -vstep_bm;
			}
			if(bmp_top_mask.bmBits)
				tmb = TopMaskBuffer = (LPBYTE)bmp_top_mask.bmBits;
			else
			{
				TopMaskBuffer = (LPBYTE)_alloca(bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes);
				GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes, TopMaskBuffer);
				tmb = TopMaskBuffer + vstep_tm * (bmp_top_mask.bmHeight - 1);
				vstep_tm = -vstep_tm;
			}
			{
				int x; int y;
				BOOL topHasAlpha = checkHasAlfa(TopBuffer, bmp_top.bmWidth, bmp_top.bmHeight);
				BOOL bottomHasAlpha = checkHasAlfa(BottomBuffer, bmp_bottom.bmWidth, bmp_bottom.bmHeight);
				BOOL topMaskUsed = !topHasAlpha && checkMaskUsed(TopMaskBuffer);
				BOOL bottomMaskUsed = !bottomHasAlpha && checkMaskUsed(BottomMaskBuffer);

				for(y = 0; y < 16; y++)
				{
					for(x = 0; x < 16; x++)
					{
						DWORD bottom_d = ((LPDWORD)bb)[x];
						DWORD top_d = ((LPDWORD)tb)[x];

						if(topMaskUsed)
						{
							if(GetMaskBit(tmb, x))
								top_d &= 0x00FFFFFF;
							else
								top_d |= 0xFF000000;
						}
						else if (!topHasAlpha)
							top_d |= 0xFF000000;

						if(bottomMaskUsed)
						{
							if(GetMaskBit(bmb, x))
								bottom_d &= 0x00FFFFFF;
							else
								bottom_d |= 0xFF000000;
						}
						else if (!bottomHasAlpha)
							bottom_d |= 0xFF000000;

						((LPDWORD)db)[x] = blend(bottom_d, top_d);
					}
					bb += vstep_b;
					tb += vstep_t;
					bmb += vstep_bm;
					tmb += vstep_tm;
					db += vstep_d;
				}
			}

			drawn = TRUE;
		}

		DeleteObject(iciBottom.hbmColor);
		DeleteObject(iciTop.hbmColor);
		DeleteObject(iciBottom.hbmMask);
		DeleteObject(iciTop.hbmMask);
	}

	if (!drawn)
	{
		DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, NULL, DI_NORMAL);
		DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, NULL, DI_NORMAL);
	}

	nMask = CreateBitmap(16, 16, 1, 1, p);
	tempDC2 = CreateCompatibleDC(NULL);
	tempDC3 = CreateCompatibleDC(NULL);
	hbm = CreateCompatibleBitmap(tempDC3, 16, 16);
	obmp = (HBITMAP)SelectObject(tempDC2, nMask);
	obmp2 = (HBITMAP)SelectObject(tempDC3, hbm);
	DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, NULL, DI_MASK);
	DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, NULL, DI_MASK);
	BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND);

	GdiFlush();

	SelectObject(tempDC2, obmp);
	DeleteDC(tempDC2);

	SelectObject(tempDC3, obmp2);
	DeleteDC(tempDC3);

	SelectObject(tempDC, oImage);
	DeleteDC(tempDC);

	DeleteObject(hbm);

	iNew.fIcon = TRUE;
	iNew.hbmColor = nImage;
	iNew.hbmMask = nMask;
	res = CreateIconIndirect(&iNew);

	DeleteObject(nImage);
	DeleteObject(nMask);

	return res;
}

HANDLE FASTCALL GetIconIndexFromFI(LPTSTR szMirVer)
{
	int i;
	DWORD val;
	HANDLE hFoundImage = INVALID_HANDLE_VALUE;
	short base, overlay, overlay2, overlay3;

	GetIconsIndexes(szMirVer, &base, &overlay, &overlay2, &overlay3);

	if(base == -1 || nFICount == 0xFF) return hFoundImage;

	// MAX: 1024 + 256 + 128 + 128
	val = (base << 22) | ((overlay & 0xFF) << 14) | ((overlay2 & 0x7F) << 7) | (overlay3 & 0x7F);

	for(i = 0; i < nFICount; i++)
	{
		if(fiList[i].dwArray == val)
		{
			hFoundImage = fiList[i].hRegisteredImage;
			break;
		}
	}

	if(hFoundImage == INVALID_HANDLE_VALUE && i == nFICount) //not found - then add
	{

		HICON hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3);

		fiList = (FOUNDINFO*)mir_realloc(fiList, sizeof(FOUNDINFO) * (nFICount + 1));
		fiList[nFICount].dwArray = val;

		if(hIcon != NULL)
		{
			fiList[nFICount].hRegisteredImage = (hIcon) ? (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0) : INVALID_HANDLE_VALUE;

			hFoundImage = fiList[nFICount].hRegisteredImage;

			DestroyIcon(hIcon);
		}
		else
		{
			fiList[nFICount].hRegisteredImage = INVALID_HANDLE_VALUE;
		}
		nFICount++;
	}

	return hFoundImage;
}

VOID FASTCALL ClearFI()
{
	if(fiList != NULL)
		mir_free(fiList);
	fiList = NULL;
	nFICount = 0;
	return;
}