#include "StdAfx.h"
#include "sametime.h"

CSametimeProto* getProtoFromMwConversation(mwConversation* conv)
{
	mwServiceIm* serviceIM = mwConversation_getService(conv);
	mwService* service = mwServiceIm_getService(serviceIM);
	mwSession* session = mwService_getSession(service);
	return (CSametimeProto*)mwSession_getProperty(session, "PROTO_STRUCT_PTR");
}

void mwIm_conversation_opened(mwConversation* conv)
{
	CSametimeProto* proto = getProtoFromMwConversation(conv);
	proto->debugLogW(L"mwIm_conversation_opened() start");

	mwIdBlock* idb = mwConversation_getTarget(conv);
	MCONTACT hContact = proto->FindContactByUserId(idb->user);

	if (!hContact) {
		proto->debugLogW(L"mwIm_conversation_opened() !hContact");
		mwSametimeList* user_list = mwSametimeList_new();
		mwSametimeGroup* stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_NORMAL, Translate("None"));
		mwSametimeUser* stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, idb);

		proto->AddContact(stuser, (proto->options.add_contacts ? false : true));
		proto->GetMoreDetails(idb->user);
	}

	ContactMessageQueue::iterator i;
	mir_cslock lck(proto->q_cs);
	if ((i = proto->contact_message_queue.find(hContact)) != proto->contact_message_queue.end()) {
		while (i->second.size()) {
			mwConversation_send(conv, mwImSend_PLAIN, (gconstpointer)i->second.front().c_str());
			i->second.pop();
		}
		proto->contact_message_queue.erase(i);
	}

	// gives linker error 'unresolved external symbol' :( So instead we will either add ciphers to the session or not (see session.cpp)
	//mwConversation_setEncrypted(conv, options.encrypt_session);
}

/** A conversation has been closed */
void mwIm_conversation_closed(mwConversation* conv, guint32 err)
{
	CSametimeProto* proto = getProtoFromMwConversation(conv);
	proto->debugLogW(L"mwIm_conversation_closed() start err=[%d]", err);

	if (err & ERR_FAILURE && err != CONNECTION_RESET) {
		proto->showPopup(err);
		if (err == ERR_NO_COMMON_ENCRYPT && !(proto->options.encrypt_session))
			proto->showPopup(TranslateT("No common encryption method. Try to enable encryption in protocol options."), SAMETIME_POPUP_INFO);
	}

	mwIdBlock* idb = mwConversation_getTarget(conv);
	MCONTACT hContact = proto->FindContactByUserId(idb->user);
	if (hContact) {
		mir_cslock lck(proto->q_cs);
		ContactMessageQueue::iterator i = proto->contact_message_queue.find(hContact);
		if (i != proto->contact_message_queue.end())
			proto->contact_message_queue.erase(i);
	}
}

/** A message has been received on a conversation */
void mwIm_conversation_recv(mwConversation* conv, mwImSendType type, gconstpointer msg)
{
	CSametimeProto* proto = getProtoFromMwConversation(conv);
	proto->debugLogW(L"mwIm_conversation_recv() start");

	mwIdBlock* idb = mwConversation_getTarget(conv);
	MCONTACT hContact = proto->FindContactByUserId(idb->user);
	proto->debugLogW(L"mwIm_conversation_recv() type=[%d] hContact=[%x]", type, hContact);

	if (type == mwImSend_TYPING) {
		CallService(MS_PROTO_CONTACTISTYPING, hContact, (GPOINTER_TO_UINT(msg) == 0 ? 0 : 2));
		return;
	}

	if (type != mwImSend_PLAIN)
		return;

	PROTORECVEVENT pre = { 0 };
	time_t t = time(NULL);
	pre.timestamp = t;
	pre.szMessage = (char*)msg;
	ProtoChainRecvMsg(hContact, &pre);
}

void mwIm_place_invite(struct mwConversation* conv, const char* message, const char* title, const char* name)
{
	CSametimeProto* proto = getProtoFromMwConversation(conv);
	proto->debugLogW(L"mwIm_place_invite() start");

	///TODO unimplemented

	wchar_t* tszMessage = mir_utf8decodeW(message);

	wchar_t msg[512];
	mir_snwprintf(msg, TranslateT("SERVICE NOT IMPLEMENTED. %s"), tszMessage);
	proto->showPopup(msg, SAMETIME_POPUP_INFO);

	mir_free(tszMessage);
}

mwImHandler mwIm_handler = {
	mwIm_conversation_opened,
	mwIm_conversation_closed,
	mwIm_conversation_recv,
	mwIm_place_invite,
	NULL
};

HANDLE CSametimeProto::SendMessageToUser(MCONTACT hContact, const char *szMsg)
{
	debugLogW(L"CSametimeProto::SendMessageToUser()  hContact=[%x]", hContact);

	mwAwareIdBlock id_block;
	if (GetAwareIdFromContact(hContact, &id_block)) {
		mwIdBlock idb;
		idb.user = id_block.user;
		idb.community = id_block.community;

		mwConversation* conv = mwServiceIm_getConversation(service_im, &idb);
		if (conv) {
			if (!mwConversation_isOpen(conv)) {
				debugLogW(L"CSametimeProto::SendMessageToUser()  mwConversation_isOpen");
				mir_cslock lck(q_cs);
				contact_message_queue[hContact].push(szMsg);
				mwConversation_open(conv);
			}
			else {
				debugLogW(L"CSametimeProto::SendMessageToUser()  !mwConversation_isOpen");
				mwConversation_send(conv, mwImSend_PLAIN, szMsg);
			}

			free(id_block.user);
			return (HANDLE)conv;
		}

		free(id_block.user);
	}

	return 0;
}

void CSametimeProto::SendTyping(MCONTACT hContact, bool typing)
{
	debugLogW(L"CSametimeProto::SendTyping() hContact=[%x] type=[%d]", hContact, typing);

	mwAwareIdBlock id_block;
	if (GetAwareIdFromContact(hContact, &id_block)) {
		mwIdBlock idb;
		idb.user = id_block.user;
		idb.community = id_block.community;

		mwConversation* conv = mwServiceIm_getConversation(service_im, &idb);
		if (conv) {
			if (mwConversation_isOpen(conv)) {
				debugLogW(L"CSametimeProto::SendTyping() send");
				mwConversation_send(conv, mwImSend_TYPING, (gconstpointer)GUINT_TO_POINTER(typing ? 1 : 0));
			}
		}

		free(id_block.user);
	}
}

void CSametimeProto::CloseIm(MCONTACT hContact)
{
	debugLogW(L"CSametimeProto::CloseIm() hContact=[%x]", hContact);

	mwAwareIdBlock id_block;
	if (GetAwareIdFromContact(hContact, &id_block)) {
		mwIdBlock idb;
		idb.user = id_block.user;
		idb.community = id_block.community;

		mwConversation* conv = mwServiceIm_getConversation(service_im, &idb);
		if (conv) {
			if (mwConversation_isOpen(conv)) {
				debugLogW(L"CSametimeProto::CloseIm() mwConversation_close");
				mwConversation_close(conv, 0);
			}
		}
		free(id_block.user);
	}
}

void CSametimeProto::InitMessaging()
{
	debugLogW(L"CSametimeProto::InitMessaging()");
	mwSession_addService(session, (mwService*)(service_im = mwServiceIm_new(session, &mwIm_handler)));
	mwServiceIm_setClientType(service_im, mwImClient_PLAIN);
}

void CSametimeProto::DeinitMessaging()
{
	debugLogW(L"CSametimeProto::DeinitMessaging()");
	mwSession_removeService(session, mwService_IM);
	mwService_free((mwService*)service_im);
	service_im = 0;
}