/*

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

Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org)
Copyright (c) 2000-04 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.
*/

#include "commonheaders.h"

/////////////////////////////////////////////////////////////////////////////////////////

INT_PTR GetAvatarBitmap(WPARAM wParam, LPARAM lParam)
{
	if (wParam == 0 || g_shutDown || fei == NULL)
		return 0;

	HCONTACT hContact = (HCONTACT)wParam;
	hContact = GetContactThatHaveTheAvatar(hContact);

	// Get the node
	CacheNode *node = FindAvatarInCache(hContact, TRUE);
	if (node == NULL || !node->loaded)
		return (INT_PTR)GetProtoDefaultAvatar(hContact);
	else
		return (INT_PTR)&node->ace;
}

/////////////////////////////////////////////////////////////////////////////////////////

INT_PTR ProtectAvatar(WPARAM wParam, LPARAM lParam)
{
	HCONTACT hContact = (HCONTACT)wParam;
	BYTE was_locked = db_get_b(hContact, "ContactPhoto", "Locked", 0);

	if (fei == NULL || was_locked == (BYTE)lParam)      // no need for redundant lockings...
		return 0;

	if (hContact) {
		if (!was_locked)
			MakePathRelative(hContact);
		db_set_b(hContact, "ContactPhoto", "Locked", lParam ? 1 : 0);
		if (lParam == 0)
			MakePathRelative(hContact);
		ChangeAvatar(hContact, TRUE);
	}
	return 0;
}

/*
 * set an avatar (service function)
 * if lParam == NULL, a open file dialog will be opened, otherwise, lParam is taken as a FULL
 * image filename (will be checked for existance, though)
 */

struct OpenFileSubclassData {
	BYTE *locking_request;
	BYTE setView;
};

UINT_PTR CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	OpenFileSubclassData *data= (OpenFileSubclassData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);

	switch(msg) {
	case WM_INITDIALOG:
		{
			OPENFILENAME *ofn = (OPENFILENAME *)lParam;

			data = (OpenFileSubclassData *) malloc(sizeof(OpenFileSubclassData));
			SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)data);
			data->locking_request = (BYTE *)ofn->lCustData;
			data->setView = TRUE;

			TranslateDialogDefault(hwnd);
			CheckDlgButton(hwnd, IDC_PROTECTAVATAR, *(data->locking_request));
		}
		break;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDC_PROTECTAVATAR)
			*(data->locking_request) = IsDlgButtonChecked(hwnd, IDC_PROTECTAVATAR) ? TRUE : FALSE;
		break;

	case WM_NOTIFY:
		if (data->setView) {
			HWND hwndParent = GetParent(hwnd);
			HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ;
			if (hwndLv != NULL) {
				SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0);
				data->setView = FALSE;
			}
		}
		break;

	case WM_NCDESTROY:
		free((OpenFileSubclassData *)data);
		SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)0);
		break;
	}

	return FALSE;
}

static INT_PTR avSetAvatar(HCONTACT hContact, TCHAR *tszPath)
{
	BYTE is_locked = 0;
	TCHAR FileName[MAX_PATH], szBackupName[MAX_PATH];
	TCHAR *szFinalName = NULL;
	HANDLE hFile = 0;
	BYTE locking_request;

	if (hContact == NULL || fei == NULL)
		return 0;

	is_locked = db_get_b(hContact, "ContactPhoto", "Locked", 0);

	if ( tszPath == NULL ) {
		OPENFILENAME ofn = {0};
		TCHAR filter[256];

		filter[0] = '\0';
		CallService(MS_UTILS_GETBITMAPFILTERSTRINGST, SIZEOF(filter), ( LPARAM )filter);

		ofn.lStructSize = sizeof(ofn);
		ofn.hwndOwner = 0;
		ofn.lpstrFile = FileName;
		ofn.lpstrFilter = filter;
		ofn.nMaxFile = MAX_PATH;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.Flags = OFN_FILEMUSTEXIST | OFN_ENABLETEMPLATE | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
		ofn.lpstrInitialDir = _T(".");
		*FileName = '\0';
		ofn.lpstrDefExt = _T("");
		ofn.hInstance = g_hInst;
		ofn.lpTemplateName = MAKEINTRESOURCE(IDD_OPENSUBCLASS);
		ofn.lpfnHook = OpenFileSubclass;
		locking_request = is_locked;
		ofn.lCustData = (LPARAM)&locking_request;
		if (GetOpenFileName(&ofn)) {
			szFinalName = FileName;
			is_locked = locking_request ? 1 : is_locked;
		}
		else
			return 0;
	}
	else
		szFinalName = tszPath;

	/*
	* filename is now set, check it and perform all needed action
	*/

	if ((hFile = CreateFile(szFinalName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
		return 0;

	// file exists...

	CloseHandle(hFile);

	AVS_pathToRelative(szFinalName, szBackupName);
	db_set_ts(hContact, "ContactPhoto", "Backup", szBackupName);

	db_set_b(hContact, "ContactPhoto", "Locked", is_locked);
	db_set_ts(hContact, "ContactPhoto", "File", szFinalName);
	MakePathRelative(hContact, szFinalName);
	// Fix cache
	ChangeAvatar(hContact, TRUE);

	return 0;
}

INT_PTR SetAvatar(WPARAM wParam, LPARAM lParam)
{
	return avSetAvatar((HCONTACT)wParam, _A2T((const char*)lParam));
}

INT_PTR SetAvatarW(WPARAM wParam, LPARAM lParam)
{
	return avSetAvatar((HCONTACT)wParam, (TCHAR*)lParam);
}

/*
 * see if is possible to set the avatar for the expecified protocol
 */

static INT_PTR CanSetMyAvatar(WPARAM wParam, LPARAM lParam)
{
	char *protocol = (char *) wParam;
	if (protocol == NULL || fei == NULL)
		return 0;

	return ProtoServiceExists(protocol, PS_SETMYAVATAR);
}

/*
 * set an avatar for a protocol (service function)
 * if lParam == NULL, a open file dialog will be opened, otherwise, lParam is taken as a FULL
 * image filename (will be checked for existance, though)
 */

static int InternalRemoveMyAvatar(char *protocol)
{
	SetIgnoreNotify(protocol, TRUE);

	// Remove avatar
	int ret = 0;
	if (protocol != NULL)
	{
		if ( ProtoServiceExists(protocol, PS_SETMYAVATAR))
			ret = SaveAvatar(protocol, NULL);
		else
			ret = -3;

		if (ret == 0)
		{
			// Has global avatar?
			DBVARIANT dbv = {0};
			if ( !db_get_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv)) {
				db_free(&dbv);
				db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
				DeleteGlobalUserAvatar();
			}
		}
	}
	else
	{
		PROTOACCOUNT **accs;
		int i,count;

		ProtoEnumAccounts( &count, &accs );
		for (i = 0; i < count; i++)
		{
			if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR))
				continue;

			if (!Proto_IsAvatarsEnabled( accs[i]->szModuleName ))
				continue;

			// Found a protocol
			int retTmp = SaveAvatar( accs[i]->szModuleName, NULL);
			if (retTmp != 0)
				ret = retTmp;
		}

		DeleteGlobalUserAvatar();

		if (ret)
			db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
		else
			db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 0);
	}

	SetIgnoreNotify(protocol, FALSE);

	ReportMyAvatarChanged(WPARAM((protocol == NULL ) ? "" : protocol), 0);
	return ret;
}

static void FilterGetStrings(TCHAR *filter, int bytesLeft, BOOL xml, BOOL swf)
{
	TCHAR *pfilter;
	int wParam = bytesLeft;

	lstrcpyn(filter, TranslateT("All Files"), bytesLeft); bytesLeft-=lstrlen(filter);
	_tcsncat(filter, _T(" (*.bmp;*.jpg;*.gif;*.png"), bytesLeft);
	if (swf) _tcscat(filter, _T(";*.swf"));
	if (xml) _tcscat(filter, _T(";*.xml"));
	_tcscat(filter, _T(")"));
	pfilter=filter+lstrlen(filter)+1; bytesLeft=wParam-(pfilter-filter);
	lstrcpyn(pfilter, _T("*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG"), bytesLeft);
	if (swf) _tcscat(pfilter, _T(";*.SWF"));
	if (xml) _tcscat(pfilter, _T(";*.XML"));
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);

	lstrcpyn(pfilter, TranslateT("Windows Bitmaps"), bytesLeft); bytesLeft-=lstrlen(pfilter);
	_tcsncat(pfilter, _T(" (*.bmp;*.rle)"), bytesLeft);
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
	lstrcpyn(pfilter, _T("*.BMP;*.RLE"), bytesLeft);
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);

	lstrcpyn(pfilter,TranslateT("JPEG Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter);
	_tcsncat(pfilter, _T(" (*.jpg;*.jpeg)"), bytesLeft);
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
	lstrcpyn(pfilter, _T("*.JPG;*.JPEG"), bytesLeft);
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);

	lstrcpyn(pfilter,TranslateT("GIF Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter);
	_tcsncat(pfilter, _T(" (*.gif)"), bytesLeft);
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
	lstrcpyn(pfilter, _T("*.GIF"), bytesLeft);
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);

	lstrcpyn(pfilter,TranslateT("PNG Bitmaps"), bytesLeft); bytesLeft-=lstrlen(pfilter);
	_tcsncat(pfilter, _T(" (*.png)"), bytesLeft);
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
	lstrcpyn(pfilter, _T("*.PNG"), bytesLeft);
	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);

	if (swf)
	{
		lstrcpyn(pfilter,TranslateT("Flash Animations"), bytesLeft); bytesLeft-=lstrlen(pfilter);
		_tcsncat(pfilter, _T(" (*.swf)"), bytesLeft);
		pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
		lstrcpyn(pfilter, _T("*.SWF"), bytesLeft);
		pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
	}

	if (xml)
	{
		lstrcpyn(pfilter, TranslateT("XML Files"), bytesLeft); bytesLeft-=lstrlen(pfilter);
		_tcsncat(pfilter, _T(" (*.xml)"), bytesLeft);
		pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
		lstrcpyn(pfilter, _T("*.XML"), bytesLeft);
		pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
	}

	if (bytesLeft) *pfilter='\0';
}

/*
 * Callback to set thumbnaill view to open dialog
 */
static UINT_PTR CALLBACK SetMyAvatarHookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg) {
	case WM_INITDIALOG:
		{
			hwndSetMyAvatar = hwnd;

			SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)lParam);
			OPENFILENAME *ofn = (OPENFILENAME *)lParam;
			SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData;
			data->thumbnail = TRUE;

			SetWindowText(GetDlgItem(hwnd, IDC_MAKE_SQUARE), TranslateT("Make the avatar square"));
			SetWindowText(GetDlgItem(hwnd, IDC_GROW), TranslateT("Grow avatar to fit max allowed protocol size"));

			CheckDlgButton(hwnd, IDC_MAKE_SQUARE, data->square ? BST_CHECKED : BST_UNCHECKED);
			CheckDlgButton(hwnd, IDC_GROW, data->grow ? BST_CHECKED : BST_UNCHECKED);

			if (data->protocol != NULL && (Proto_AvatarImageProportion(data->protocol) & PIP_SQUARE))
				EnableWindow(GetDlgItem(hwnd, IDC_MAKE_SQUARE), FALSE);
		}
		break;

	case WM_NOTIFY:
		{
			OPENFILENAME *ofn = (OPENFILENAME *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
			SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData;
			if (data->thumbnail)
			{
				HWND hwndParent = GetParent(hwnd);
				HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ;
				if (hwndLv != NULL)
				{
					SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0);
					data->thumbnail = FALSE;
				}
			}
			break;
		}
	case WM_DESTROY:
		{
			OPENFILENAME *ofn = (OPENFILENAME *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
			SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData;
			data->square = IsDlgButtonChecked(hwnd, IDC_MAKE_SQUARE);
			data->grow = IsDlgButtonChecked(hwnd, IDC_GROW);

			hwndSetMyAvatar = NULL;
			break;
		}
	}

	return 0;
}

struct SaveProtocolData {
	DWORD max_size;
	TCHAR image_file_name[MAX_PATH];
	BOOL saved;
	BOOL need_smaller_size;
	int width;
	int height;
	TCHAR temp_file[MAX_PATH];
	HBITMAP hBmpProto;
};

void SaveImage(SaveProtocolData &d, char *protocol, int format)
{
	if ( !Proto_IsAvatarFormatSupported(protocol, format))
		return;

	mir_sntprintf(d.image_file_name, SIZEOF(d.image_file_name), _T("%s%s"), d.temp_file, ProtoGetAvatarExtension(format));
	if ( BmpFilterSaveBitmapT(d.hBmpProto, d.image_file_name, format == PA_FORMAT_JPEG ? JPEG_QUALITYSUPERB : 0))
		return;

	if (d.max_size != 0 && GetFileSize(d.image_file_name) > d.max_size) {
		DeleteFile(d.image_file_name);

		if (format == PA_FORMAT_JPEG) {
			// Try with lower quality
			if (!BmpFilterSaveBitmapT(d.hBmpProto, d.image_file_name, JPEG_QUALITYGOOD)) {
				if (GetFileSize(d.image_file_name) > d.max_size) {
					DeleteFile(d.image_file_name);
					d.need_smaller_size = TRUE;
				}
				else d.saved = TRUE;
			}
		}
		else d.need_smaller_size = TRUE;
	}
	else d.saved = TRUE;
}

static int SetProtoMyAvatar(char *protocol, HBITMAP hBmp, TCHAR *originalFilename, int originalFormat, BOOL square, BOOL grow)
{
	if (!ProtoServiceExists(protocol, PS_SETMYAVATAR))
		return -1;

	// If is swf or xml, just set it

	if (originalFormat == PA_FORMAT_SWF)
	{
		if (!Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_SWF))
			return -1;

		return SaveAvatar(protocol, originalFilename);
	}

	if (originalFormat == PA_FORMAT_XML)
	{
		if (!Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_XML))
			return -1;

		return SaveAvatar(protocol, originalFilename);
	}

	// Get protocol info
	SaveProtocolData d = {0};

	d.max_size = (DWORD) Proto_GetAvatarMaxFileSize(protocol);

	Proto_GetAvatarMaxSize(protocol, &d.width, &d.height);
	int orig_width = d.width;
	int orig_height = d.height;

	if (Proto_AvatarImageProportion(protocol) & PIP_SQUARE)
		square = TRUE;

	// Try to save until a valid image is found or we give up
	int num_tries = 0;
	do {
		// Lets do it
		ResizeBitmap rb;
		rb.size = sizeof(ResizeBitmap);
		rb.hBmp = hBmp;
		rb.max_height = d.height;
		rb.max_width = d.width;
		rb.fit = (grow ? 0 : RESIZEBITMAP_FLAG_DONT_GROW)
				| (square ? RESIZEBITMAP_MAKE_SQUARE : RESIZEBITMAP_KEEP_PROPORTIONS);

		d.hBmpProto = (HBITMAP) BmpFilterResizeBitmap((WPARAM)&rb, 0);

		if (d.hBmpProto == NULL)
		{
			if (d.temp_file[0] != '\0')
				DeleteFile(d.temp_file);
			return -1;
		}

		// Check if can use original image
		if (d.hBmpProto == hBmp
			&& Proto_IsAvatarFormatSupported(protocol, originalFormat)
			&& (d.max_size == 0 || GetFileSize(originalFilename) < d.max_size))
		{
			if (d.temp_file[0] != '\0')
				DeleteFile(d.temp_file);

			// Use original image
			return SaveAvatar(protocol, originalFilename);
		}

		// Create a temporary file (if was not created already)
		if (d.temp_file[0] == '\0')
		{
			d.temp_file[0] = '\0';
			if (GetTempPath(MAX_PATH, d.temp_file) == 0
				|| GetTempFileName(d.temp_file, _T("mir_av_"), 0, d.temp_file) == 0)
			{
				DeleteObject(d.hBmpProto);
				return -1;
			}
		}

		// Which format?

		// First try to use original format
		if (originalFormat != PA_FORMAT_BMP)
			SaveImage(d, protocol, originalFormat);

		if (!d.saved && originalFormat != PA_FORMAT_PNG)
			SaveImage(d, protocol, PA_FORMAT_PNG);

		if (!d.saved && originalFormat != PA_FORMAT_JPEG)
			SaveImage(d, protocol, PA_FORMAT_JPEG);

		if (!d.saved && originalFormat != PA_FORMAT_GIF)
			SaveImage(d, protocol, PA_FORMAT_GIF);

		if (!d.saved)
			SaveImage(d, protocol, PA_FORMAT_BMP);

		num_tries++;
		if (!d.saved && d.need_smaller_size && num_tries < 4)
		{
			// Cleanup
			if (d.hBmpProto != hBmp)
				DeleteObject(d.hBmpProto);

			// use a smaller size
			d.width = orig_width * (4 - num_tries) / 4;
			d.height = orig_height * (4 - num_tries) / 4;
		}

	} while(!d.saved && d.need_smaller_size && num_tries < 4);

	int ret;

	if (d.saved)
	{
		// Call proto service
		ret = SaveAvatar(protocol, d.image_file_name);
		DeleteFile(d.image_file_name);
	}
	else
	{
		ret = -1;
	}

	if (d.temp_file[0] != '\0')
		DeleteFile(d.temp_file);

	if (d.hBmpProto != hBmp)
		DeleteObject(d.hBmpProto);

	return ret;
}

static int InternalSetMyAvatar(char *protocol, TCHAR *szFinalName, SetMyAvatarHookData &data, BOOL allAcceptXML, BOOL allAcceptSWF)
{
	HANDLE hFile = 0;

	int format = ProtoGetAvatarFormat(szFinalName);
	if (format == PA_FORMAT_UNKNOWN || (hFile = CreateFile(szFinalName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
		return -3;

	CloseHandle(hFile);

	// file exists...

	HBITMAP hBmp = NULL;

	if (format == PA_FORMAT_SWF)
	{
		if (!allAcceptSWF)
			return -4;
	}
	else if (format == PA_FORMAT_XML)
	{
		if (!allAcceptXML)
			return -4;
	}
	else
	{
		// Try to open if is not a flash or XML
		hBmp = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) szFinalName, IMGL_TCHAR);
		if (hBmp == NULL)
			return -4;
	}

	SetIgnoreNotify(protocol, TRUE);

	int ret = 0;
	if (protocol != NULL)
	{
		ret = SetProtoMyAvatar(protocol, hBmp, szFinalName, format, data.square, data.grow);

		if (ret == 0)
		{
			DeleteGlobalUserAvatar();
			db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
		}
	}
	else
	{
		PROTOACCOUNT **accs;
		int i,count;

		ProtoEnumAccounts( &count, &accs );
		for (i = 0; i < count; i++)
		{
			if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR))
				continue;

			if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName ))
				continue;

			int retTmp = SetProtoMyAvatar( accs[i]->szModuleName, hBmp, szFinalName, format, data.square, data.grow);
			if (retTmp != 0)
				ret = retTmp;
		}

		DeleteGlobalUserAvatar();

		if (ret)
		{
			db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
		}
		else
		{
			// Copy avatar file to store as global one
			TCHAR globalFile[1024];
			BOOL saved = TRUE;
			if (FoldersGetCustomPathT(hGlobalAvatarFolder, globalFile, SIZEOF(globalFile), _T("")))
			{
				mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\%s"), g_szDataPath, _T("GlobalAvatar"));
				CreateDirectory(globalFile, NULL);
			}

			TCHAR *ext = _tcsrchr(szFinalName, _T('.')); // Can't be NULL here
			if (format == PA_FORMAT_XML || format == PA_FORMAT_SWF)
			{
				mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar%s"), globalFile, ext);
				CopyFile(szFinalName, globalFile, FALSE);
			}
			else
			{
				// Resize (to avoid too big avatars)
				ResizeBitmap rb = {0};
				rb.size = sizeof(ResizeBitmap);
				rb.hBmp = hBmp;
				rb.max_height = 300;
				rb.max_width = 300;
				rb.fit = (data.grow ? 0 : RESIZEBITMAP_FLAG_DONT_GROW)
						| (data.square ? RESIZEBITMAP_MAKE_SQUARE : RESIZEBITMAP_KEEP_PROPORTIONS);

				HBITMAP hBmpTmp = (HBITMAP) BmpFilterResizeBitmap((WPARAM)&rb, 0);

				// Check if need to resize
				if (hBmpTmp == hBmp || hBmpTmp == NULL)
				{
					// Use original image
					mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar%s"), globalFile, ext);
					CopyFile(szFinalName, globalFile, FALSE);
				}
				else
				{
					// Save as PNG
					mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar.png"), globalFile);
					if (BmpFilterSaveBitmap((WPARAM) hBmpTmp, (LPARAM) globalFile))
						saved = FALSE;

					DeleteObject(hBmpTmp);
				}
			}

			if (saved)
			{
				TCHAR relFile[1024];
				if (AVS_pathToRelative(globalFile, relFile))
					db_set_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", relFile);
				else
					db_set_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", globalFile);

				db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 0);
			}
			else
			{
				db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
			}
		}
	}

	DeleteObject(hBmp);

	SetIgnoreNotify(protocol, FALSE);

	ReportMyAvatarChanged(WPARAM((protocol == NULL) ? "" : protocol), 0);
	return ret;
}

INT_PTR avSetMyAvatar( char* protocol, TCHAR* tszPath )
{
	TCHAR FileName[MAX_PATH];
	TCHAR *szFinalName = NULL;
	BOOL allAcceptXML;
	BOOL allAcceptSWF;

	// Protocol allow seting of avatar?
	if (protocol != NULL && !CanSetMyAvatar((WPARAM)protocol, 0))
		return -1;

	if (tszPath == NULL && hwndSetMyAvatar != 0) {
		SetForegroundWindow(hwndSetMyAvatar);
		SetFocus(hwndSetMyAvatar);
		ShowWindow(hwndSetMyAvatar, SW_SHOW);
		return -2;
	}

	SetMyAvatarHookData data = { 0 };

	// Check for XML and SWF
	if (protocol == NULL)
	{
		allAcceptXML = TRUE;
		allAcceptSWF = TRUE;

		PROTOACCOUNT **accs;
		int i,count;

		ProtoEnumAccounts( &count, &accs );
		for (i = 0; i < count; i++)
		{
			if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR))
				continue;

			if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName ))
				continue;

			allAcceptXML = allAcceptXML && Proto_IsAvatarFormatSupported( accs[i]->szModuleName, PA_FORMAT_XML);
			allAcceptSWF = allAcceptSWF && Proto_IsAvatarFormatSupported( accs[i]->szModuleName, PA_FORMAT_SWF);
		}

		data.square = db_get_b(0, AVS_MODULE, "SetAllwaysMakeSquare", 0);
	}
	else
	{
		allAcceptXML = Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_XML);
		allAcceptSWF = Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_SWF);

		data.protocol = protocol;
		data.square = (Proto_AvatarImageProportion(protocol) & PIP_SQUARE)
						|| db_get_b(0, AVS_MODULE, "SetAllwaysMakeSquare", 0);
	}

	if (tszPath == NULL) {
		OPENFILENAME ofn = {0};
		TCHAR filter[512];
		TCHAR inipath[1024];

		data.protocol = protocol;

		filter[0] = '\0';
		FilterGetStrings(filter, SIZEOF(filter), allAcceptXML, allAcceptSWF);

		FoldersGetCustomPathT(hMyAvatarsFolder, inipath, SIZEOF(inipath), _T("."));

		ofn.lStructSize = sizeof(ofn);
		ofn.hwndOwner = 0;
		ofn.lpstrFile = FileName;
		ofn.lpstrFilter = filter;
		ofn.nMaxFile = MAX_PATH;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.Flags = OFN_FILEMUSTEXIST | OFN_ENABLETEMPLATE | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
		ofn.lpstrInitialDir = inipath;
		ofn.lpTemplateName = MAKEINTRESOURCE(IDD_SET_OWN_SUBCLASS);
		ofn.lpfnHook = SetMyAvatarHookProc;
		ofn.lCustData = (LPARAM) &data;

		*FileName = '\0';
		ofn.lpstrDefExt = _T("");
		ofn.hInstance = g_hInst;

		TCHAR title[256];
		if (protocol == NULL)
			mir_sntprintf(title, SIZEOF(title), TranslateT("Set My Avatar"));
		else
		{
			TCHAR* prototmp = mir_a2t(protocol);
			mir_sntprintf(title, SIZEOF(title), TranslateT("Set My Avatar for %s"), prototmp);
			mir_free(prototmp);
		}
		ofn.lpstrTitle = title;

		if (GetOpenFileName(&ofn))
			szFinalName = FileName;
		else
			return 1;
	}
	else
		szFinalName = (TCHAR *)tszPath;

	/*
	* filename is now set, check it and perform all needed action
	*/

	if (szFinalName[0] == '\0')
		return InternalRemoveMyAvatar(protocol);

	return InternalSetMyAvatar(protocol, szFinalName, data, allAcceptXML, allAcceptSWF);
}

static INT_PTR SetMyAvatar( WPARAM wParam, LPARAM lParam )
{
	return avSetMyAvatar(( char* )wParam, _A2T(( const char* )lParam ));
}

static INT_PTR SetMyAvatarW( WPARAM wParam, LPARAM lParam )
{
	return avSetMyAvatar(( char* )wParam, ( TCHAR* )lParam );
}

/////////////////////////////////////////////////////////////////////////////////////////

INT_PTR CALLBACK DlgProcAvatarOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);

static INT_PTR ContactOptions(WPARAM wParam, LPARAM lParam)
{
	if (wParam)
		CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_AVATAROPTIONS), 0, DlgProcAvatarOptions, (LPARAM)wParam);
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////

INT_PTR DrawAvatarPicture(WPARAM wParam, LPARAM lParam)
{
	AVATARDRAWREQUEST *r = (AVATARDRAWREQUEST *)lParam;
	AVATARCACHEENTRY *ace = NULL;

	if (fei == NULL || r == NULL || IsBadReadPtr((void *)r, sizeof(AVATARDRAWREQUEST)))
		return 0;

	if (r->cbSize != sizeof(AVATARDRAWREQUEST))
		return 0;

	if (r->dwFlags & AVDRQ_PROTOPICT) {
		if (r->szProto == NULL)
			return 0;

		for (int i = 0; i < g_ProtoPictures.getCount(); i++) {
			protoPicCacheEntry& p = g_ProtoPictures[i];
			if ( !lstrcmpA(p.szProtoname, r->szProto) && lstrlenA(r->szProto) == lstrlenA(p.szProtoname) && p.hbmPic != 0) {
				ace = (AVATARCACHEENTRY *)&g_ProtoPictures[i];
				break;
			}
		}
	}
	else if (r->dwFlags & AVDRQ_OWNPIC) {
		if (r->szProto == NULL)
			return 0;

		if (r->szProto[0] == '\0' && db_get_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1))
			return -1;

		ace = (AVATARCACHEENTRY *)GetMyAvatar(0, (LPARAM)r->szProto);
	}
	else
		ace = (AVATARCACHEENTRY *)GetAvatarBitmap((WPARAM)r->hContact, 0);

	if (ace && (!(r->dwFlags & AVDRQ_RESPECTHIDDEN) || !(ace->dwFlags & AVS_HIDEONCLIST))) {
		ace->t_lastAccess = time(NULL);

		if (ace->bmHeight == 0 || ace->bmWidth == 0 || ace->hbmPic == 0)
			return 0;

		InternalDrawAvatar(r, ace->hbmPic, ace->bmWidth, ace->bmHeight, ace->dwFlags);
		return 1;
	}

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////

INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam)
{
	if (wParam || g_shutDown || fei == NULL)
		return 0;

	char *szProto = (char *)lParam;
	if (lParam == 0 || IsBadReadPtr(szProto, 4))
		return 0;

	for (int i = 0; i < g_MyAvatars.getCount(); i++)
		if (!lstrcmpA(szProto, g_MyAvatars[i].szProtoname) && g_MyAvatars[i].hbmPic != 0)
			return (INT_PTR)&g_MyAvatars[i];

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////

static void ReloadMyAvatar(LPVOID lpParam)
{
	char *szProto = (char *)lpParam;

	mir_sleep(500);
	for (int i = 0; !g_shutDown && i < g_MyAvatars.getCount(); i++) {
		char *myAvatarProto = g_MyAvatars[i].szProtoname;

		if (szProto[0] == 0) {
			// Notify to all possibles
			if (lstrcmpA(myAvatarProto, szProto)) {
				if (!ProtoServiceExists( myAvatarProto, PS_SETMYAVATAR))
					continue;
				if (!Proto_IsAvatarsEnabled( myAvatarProto ))
					continue;
			}

		}
		else if (lstrcmpA(myAvatarProto, szProto))
			continue;

		if (g_MyAvatars[i].hbmPic)
			DeleteObject(g_MyAvatars[i].hbmPic);

		if (CreateAvatarInCache((HCONTACT)-1, &g_MyAvatars[i], myAvatarProto) != -1)
			NotifyEventHooks(hMyAvatarChanged, (WPARAM)myAvatarProto, (LPARAM)&g_MyAvatars[i]);
		else
			NotifyEventHooks(hMyAvatarChanged, (WPARAM)myAvatarProto, 0);
	}

	free(lpParam);
}

INT_PTR ReportMyAvatarChanged(WPARAM wParam, LPARAM lParam)
{
	const char *proto = (const char*)wParam;
	if (proto == NULL)
		return -1;

	for (int i = 0; i < g_MyAvatars.getCount(); i++) {
		if (g_MyAvatars[i].dwFlags & AVS_IGNORENOTIFY)
			continue;

		if ( !lstrcmpA(g_MyAvatars[i].szProtoname, proto)) {
			LPVOID lpParam = (void *)malloc(lstrlenA(g_MyAvatars[i].szProtoname) + 2);
			strcpy((char *)lpParam, g_MyAvatars[i].szProtoname);
			mir_forkthread(ReloadMyAvatar, lpParam);
			return 0;
		}
	}

	return -2;
}

/////////////////////////////////////////////////////////////////////////////////////////

void InitServices()
{
	CreateServiceFunction(MS_AV_GETAVATARBITMAP, GetAvatarBitmap);
	CreateServiceFunction(MS_AV_PROTECTAVATAR, ProtectAvatar);
	CreateServiceFunction(MS_AV_SETAVATAR, SetAvatar);
	CreateServiceFunction(MS_AV_SETAVATARW, SetAvatarW);
	CreateServiceFunction(MS_AV_SETMYAVATAR, SetMyAvatar);
	CreateServiceFunction(MS_AV_SETMYAVATARW, SetMyAvatarW);
	CreateServiceFunction(MS_AV_CANSETMYAVATAR, CanSetMyAvatar);
	CreateServiceFunction(MS_AV_CONTACTOPTIONS, ContactOptions);
	CreateServiceFunction(MS_AV_DRAWAVATAR, DrawAvatarPicture);
	CreateServiceFunction(MS_AV_GETMYAVATAR, GetMyAvatar);
	CreateServiceFunction(MS_AV_REPORTMYAVATARCHANGED, ReportMyAvatarChanged);

	CreateServiceFunction(MS_AV_LOADBITMAP32, BmpFilterLoadBitmap32);
	CreateServiceFunction(MS_AV_SAVEBITMAP, BmpFilterSaveBitmap);
	CreateServiceFunction(MS_AV_CANSAVEBITMAP, BmpFilterCanSaveBitmap);
	CreateServiceFunction(MS_AV_RESIZEBITMAP, BmpFilterResizeBitmap);
}