/*
Plugin of Miranda IM for communicating with users of the AIM protocol.
Copyright (c) 2008-2012 Boris Krasnovskiy
Copyright (C) 2005-2006 Aaron Myles Landwehr

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, see <http://www.gnu.org/licenses/>.
*/

#include "stdafx.h"

void __cdecl CAimProto::avatar_request_thread(void* param)
{
	MCONTACT hContact = (UINT_PTR)param;

	char *sn = getStringA(hContact, AIM_KEY_SN);
	debugLogA("Starting avatar request thread for %s)", sn);

	if (wait_conn(m_hAvatarConn, m_hAvatarEvent, 0x10)) {
		char *hash_str = getStringA(hContact, AIM_KEY_AH);
		if (!hash_str) {
			mir_free(sn);
			return;
		}
		char type = getByte(hContact, AIM_KEY_AHT, 1);

		size_t len = (mir_strlen(hash_str) + 1) / 2;
		char *hash = (char*)alloca(len);
		string_to_bytes(hash_str, hash);
		debugLogA("Requesting an Avatar: %s (Hash: %s)", sn, hash_str);
		aim_request_avatar(m_hAvatarConn, m_avatar_seqno, sn, type, hash, (unsigned short)len);

		mir_free(hash_str);
	}

	mir_free(sn);
}

void __cdecl CAimProto::avatar_upload_thread(void* param)
{
	avatar_up_req* req = (avatar_up_req*)param;

	if (wait_conn(m_hAvatarConn, m_hAvatarEvent, 0x10)) {
		if (req->size2) {
			aim_upload_avatar(m_hAvatarConn, m_avatar_seqno, 1, req->data2, req->size2);
			aim_upload_avatar(m_hAvatarConn, m_avatar_seqno, 12, req->data1, req->size1);
		}
		else aim_upload_avatar(m_hAvatarConn, m_avatar_seqno, 1, req->data1, req->size1);
	}
	delete req;
}

void CAimProto::avatar_request_handler(MCONTACT hContact, char* hash, unsigned char type)//checks to see if the avatar needs requested
{
	if (hContact == NULL) {
		hash = m_hash_lg ? m_hash_lg : m_hash_sm;
		type = m_hash_lg ? 12 : 1;
	}

	char *saved_hash = getStringA(hContact, AIM_KEY_AH);
	if (hash && _stricmp(hash, "0201d20472") && _stricmp(hash, "2b00003341")) //gaim default icon fix- we don't want their blank icon displaying.
	{
		if (mir_strcmp(saved_hash, hash)) {
			setByte(hContact, AIM_KEY_AHT, type);
			setString(hContact, AIM_KEY_AH, hash);

			ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0);
		}
	}
	else {
		if (saved_hash) {
			delSetting(hContact, AIM_KEY_AHT);
			delSetting(hContact, AIM_KEY_AH);

			ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0);
		}
	}
	mir_free(saved_hash);
}

void CAimProto::avatar_retrieval_handler(const char* sn, const char* /*hash*/, const char* data, int data_len)
{
	bool res = false;
	PROTO_AVATAR_INFORMATION ai = { 0 };
	ai.hContact = contact_from_sn(sn);

	if (data_len > 0) {
		const wchar_t *type;
		ai.format = ProtoGetBufferFormat(data, &type);
		get_avatar_filename(ai.hContact, ai.filename, _countof(ai.filename), type);

		int fileId = _wopen(ai.filename, _O_CREAT | _O_TRUNC | _O_WRONLY | O_BINARY, _S_IREAD | _S_IWRITE);
		if (fileId >= 0) {
			_write(fileId, data, data_len);
			_close(fileId);
			res = true;

			char *my_sn = getStringA(AIM_KEY_SN);
			if (!mir_strcmp(sn, my_sn))
				CallService(MS_AV_REPORTMYAVATARCHANGED, (WPARAM)m_szModuleName, 0);
			mir_free(my_sn);
		}
	}
	else debugLogA("AIM sent avatar of zero length for %s.(Usually caused by repeated request for the same icon)", sn);

	ProtoBroadcastAck(ai.hContact, ACKTYPE_AVATAR, res ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, &ai, 0);
}

int CAimProto::get_avatar_filename(MCONTACT hContact, wchar_t* pszDest, size_t cbLen, const wchar_t *ext)
{
	int tPathLen = mir_snwprintf(pszDest, cbLen, L"%s\\%S", VARSW(L"%miranda_avatarcache%"), m_szModuleName);

	if (ext && _waccess(pszDest, 0))
		CreateDirectoryTreeW(pszDest);

	size_t tPathLen2 = tPathLen;

	DBVARIANT dbv;
	if (getWString(hContact, AIM_KEY_AH, &dbv)) return GAIR_NOAVATAR;
	tPathLen += mir_snwprintf(pszDest + tPathLen, cbLen - tPathLen, L"\\%s", dbv.ptszVal);
	db_free(&dbv);

	bool found = false;
	if (ext == NULL) {
		mir_snwprintf(pszDest + tPathLen, cbLen - tPathLen, L".*");

		_tfinddata_t c_file;
		long hFile = _tfindfirst(pszDest, &c_file);
		if (hFile > -1L) {
			do {
				if (wcsrchr(c_file.name, '.')) {
					mir_snwprintf(pszDest + tPathLen2, cbLen - tPathLen2, L"\\%s", c_file.name);
					found = true;
				}
			} while (_tfindnext(hFile, &c_file) == 0);
			_findclose(hFile);
		}

		if (!found) pszDest[0] = 0;
	}
	else {
		mir_snwprintf(pszDest + tPathLen, cbLen - tPathLen, ext);
		found = _waccess(pszDest, 0) == 0;
	}

	return found ? GAIR_SUCCESS : GAIR_WAITFOR;
}

bool get_avatar_hash(const wchar_t* file, char* hash, char** data, unsigned short &size)
{
	int fileId = _wopen(file, _O_RDONLY | _O_BINARY, _S_IREAD);
	if (fileId == -1) return false;

	long  lAvatar = _filelength(fileId);
	if (lAvatar <= 0) {
		_close(fileId);
		return false;
	}

	char* pResult = (char*)mir_alloc(lAvatar);
	int res = _read(fileId, pResult, lAvatar);
	_close(fileId);

	if (res <= 0) {
		mir_free(pResult);
		return false;
	}

	mir_md5_state_t state;
	mir_md5_init(&state);
	mir_md5_append(&state, (unsigned char*)pResult, lAvatar);
	mir_md5_finish(&state, (unsigned char*)hash);

	if (data) {
		*data = pResult;
		size = (unsigned short)lAvatar;
	}
	else
		mir_free(pResult);

	return true;
}

void rescale_image(char *data, unsigned short size, char *&data1, unsigned short &size1)
{
	FI_INTERFACE *fei = NULL;
	CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM)&fei);
	if (fei == NULL) return;

	FIMEMORY *hmem = fei->FI_OpenMemory((BYTE *)data, size);
	FREE_IMAGE_FORMAT fif = fei->FI_GetFileTypeFromMemory(hmem, 0);
	FIBITMAP *dib = fei->FI_LoadFromMemory(fif, hmem, 0);
	fei->FI_CloseMemory(hmem);

	if (fei->FI_GetWidth(dib) > 64) {
		FIBITMAP *dib1 = fei->FI_Rescale(dib, 64, 64, FILTER_BSPLINE);

		FIMEMORY *hmem2 = fei->FI_OpenMemory(NULL, 0);
		fei->FI_SaveToMemory(fif, dib1, hmem2, 0);

		BYTE *data2; DWORD size2;
		fei->FI_AcquireMemory(hmem2, &data2, &size2);
		data1 = (char*)mir_alloc(size2);
		memcpy(data1, data2, size2);
		size1 = size2;

		fei->FI_CloseMemory(hmem2);
		fei->FI_Unload(dib1);
	}
	fei->FI_Unload(dib);
}