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



void CloseMyConference(CSametimeProto* proto) {
	mwConference_destroy(proto->my_conference, 0, Translate("I'm outa here."));
	proto->my_conference = 0;
}


CSametimeProto* getProtoFromMwConference(mwConference* conf)
{
	mwServiceConference* servConference = mwConference_getServiceConference(conf);
	mwService* service = mwServiceConference_getService(servConference);
	mwSession* session = mwService_getSession(service);
	return (CSametimeProto*)mwSession_getProperty(session, "PROTO_STRUCT_PTR");
}


/** triggered when we receive a conference invitation. Call
  mwConference_accept to accept the invitation and join the
  conference, or mwConference_close to reject the invitation.

  @param conf     the newly created conference
  @param inviter  the indentity of the user who sent the invitation
  @param invite   the invitation text
*/
void mwServiceConf_on_invited(mwConference* conf, mwLoginInfo* inviter, const char* invite) {

	GList *members, *mem;
	CSametimeProto* proto = getProtoFromMwConference(conf);
	proto->debugLog(_T("mwServiceConf_on_invited() start"));

	members = mem = mwConference_getMembers(conf);
	for (;mem;mem=mem->next) {
		if (proto->my_login_info && strcmp(proto->my_login_info->login_id, ((mwLoginInfo*)mem->data)->login_id) == 0) {
			proto->debugLog(_T("mwServiceConf_on_invited() already present"));
			char* utfs = mir_utf8encodeT(TranslateT("Invitation rejected - already present."));
			mwConference_reject(conf, 0, utfs);
			mir_free(utfs);
			return;
		}
	}
	g_list_free(members);

	wchar_t ws_username[128];
	MultiByteToWideChar(CP_UTF8, 0, (const char*)inviter->user_name, -1, ws_username, 128);

	wchar_t ws_invite[512];
	MultiByteToWideChar(CP_UTF8, 0, (const char*)invite, -1, ws_invite, 128);

	if (MessageBoxW(0, ws_invite, ws_username, MB_OKCANCEL) == IDOK) {
		proto->debugLog(_T("mwServiceConf_on_invited() mwConference_accept"));
		mwConference_accept(conf);
	} else {
		proto->debugLog(_T("mwServiceConf_on_invited() mwConference_reject"));
		char* temp = mir_utf8encodeT(TranslateT("Your invitation has been rejected."));
		mwConference_reject(conf, 0, temp);
		mir_free(temp);
	}

}

void CSametimeProto::ClearInviteQueue() {
	
	debugLog(_T("CSametimeProto::ClearInviteQueue() start"));
	if (!my_conference) return;

	mwIdBlock idb;
	idb.community = 0;

	while(invite_queue.size()) {
		idb.user = (char *)invite_queue.front().c_str();

		MCONTACT hContact = FindContactByUserId(idb.user);
		if (!hContact) {
			mwSametimeList* user_list = mwSametimeList_new();
			char* utfs = mir_utf8encodeT(TranslateT("None"));
			mwSametimeGroup* stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_NORMAL, utfs);
			mwSametimeUser* stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, &idb);

			hContact = AddContact(stuser, (options.add_contacts ? false : true));
			mwSametimeList_free(user_list);
			mir_free(utfs);

		}

		bool found = false;
		GList *members, *mem;
		members = mem = mwConference_getMembers(my_conference);
		for (;mem;mem=mem->next) {
			if (my_login_info && strcmp(idb.user, ((mwLoginInfo *)mem->data)->user_id) == 0) {
				found = true;
				break;
			}
		}
		g_list_free(members);

		if (!found) {
			char* temp = mir_utf8encodeT(TranslateT("Please join this meeting."));
			mwConference_invite(my_conference, &idb, temp);
			mir_free(temp);
		}

		invite_queue.pop();
	}
}

/** triggered when we enter the conference. Provides the initial
  conference membership list as a GList of mwLoginInfo structures

  @param conf     the conference just joined
  @param members  mwLoginInfo list of existing conference members
*/
void mwServiceConf_conf_opened(mwConference* conf, GList* members) 
{	
	CSametimeProto* proto = getProtoFromMwConference(conf);
	proto->debugLog(_T("mwServiceConf_conf_opened() start"));

	TCHAR* tszConfId = mir_utf8decodeT(mwConference_getName(conf));
	TCHAR* tszConfTitle = mir_utf8decodeT(mwConference_getTitle(conf));

	// create new chat session
	GCSESSION gcs = { sizeof(gcs) };
	gcs.dwFlags = 0;
	gcs.iType = GCW_CHATROOM;
	gcs.pszModule = proto->m_szModuleName;
	gcs.ptszID = tszConfId;
	gcs.ptszName = tszConfTitle;
	gcs.dwItemData = 0;

	CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcs);
	mir_free(tszConfTitle);

	//add a group
	GCDEST gcd = { proto->m_szModuleName, 0 };
	gcd.iType = GC_EVENT_ADDGROUP;
	gcd.ptszID = tszConfId;

	GCEVENT gce = { sizeof(gce), &gcd };
	gce.dwFlags = GCEF_ADDTOLOG;
	gce.ptszStatus = TranslateT("Normal");

	CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce);

	// add users
	gcd.iType = GC_EVENT_JOIN;

	GList *user = members;
	for (;user; user=user->next) {
		proto->debugLog(_T("mwServiceConf_conf_opened() add user"));

		TCHAR* tszUserName = mir_utf8decodeT(((mwLoginInfo*)user->data)->user_name);
		TCHAR* tszUserId = mir_utf8decodeT(((mwLoginInfo*)user->data)->login_id);
		gce.ptszNick = tszUserName;
		gce.ptszUID = tszUserId;
		gce.bIsMe = (strcmp(((mwLoginInfo*)user->data)->login_id, proto->my_login_info->login_id) == 0);

		CallServiceSync(MS_GC_EVENT, 0, (LPARAM) &gce);

		mir_free(tszUserName);
		mir_free(tszUserId);
	}

	// finalize setup (show window)
	gcd.iType = GC_EVENT_CONTROL;
	CallServiceSync(MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gce);

	gcd.iType = GC_EVENT_CONTROL;
	CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce);

	if (conf == proto->my_conference) {
		proto->ClearInviteQueue();
	}

	mir_free(tszConfId);
}

/** triggered when a conference is closed. This is typically when
  we've left it */
void mwServiceConf_conf_closed(mwConference* conf, guint32 reason)
{
	CSametimeProto* proto = getProtoFromMwConference(conf);
	proto->debugLog(_T("mwServiceConf_conf_closed() start"));

	TCHAR* tszConfId = mir_utf8decodeT(mwConference_getName(conf));

	GCDEST gcd = { proto->m_szModuleName };
	gcd.ptszID = tszConfId;
	gcd.iType = GC_EVENT_CONTROL;

	GCEVENT gce = { sizeof(gce), &gcd };
	gce.dwFlags = GCEF_ADDTOLOG;

	CallService(MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gce);
	CallService(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce);
	mir_free(tszConfId);
}

/** triggered when someone joins the conference */
void mwServiceConf_on_peer_joined(mwConference* conf, mwLoginInfo* user)
{
	CSametimeProto* proto = getProtoFromMwConference(conf);
	proto->debugLog(_T("mwServiceConf_on_peer_joined() start"));

	TCHAR* tszConfId = mir_utf8decodeT(mwConference_getName(conf));

	MCONTACT hContact = proto->FindContactByUserId(((mwLoginInfo*)user)->user_id);
	if (!hContact) {
		mwIdBlock idb;
		idb.user = ((mwLoginInfo *)user)->user_id;
		idb.community = 0;

		mwSametimeList* user_list = mwSametimeList_new();
		char* utfs = mir_utf8encodeT(TranslateT("None"));
		mwSametimeGroup* stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_NORMAL, utfs);
		mwSametimeUser* stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, &idb);

		hContact = proto->AddContact(stuser, (proto->options.add_contacts ? false : true));

		mwSametimeList_free(user_list);
		mir_free(utfs);
	}

	// add user
	GCDEST gcd = { proto->m_szModuleName };
	gcd.ptszID = tszConfId;
	gcd.iType = GC_EVENT_JOIN;

	GCEVENT gce = { sizeof(gce), &gcd };
	gce.dwFlags = GCEF_ADDTOLOG;
	TCHAR* tszUserName = mir_utf8decodeT(((mwLoginInfo*)user)->user_name);
	TCHAR* tszUserId = mir_utf8decodeT(((mwLoginInfo*)user)->login_id);
	gce.ptszNick = tszUserName;
	gce.ptszUID = tszUserId;
	gce.ptszStatus = _T("Normal");
	gce.time = (DWORD)time(0);

	CallServiceSync(MS_GC_EVENT, 0, (LPARAM) &gce);

	mir_free(tszUserName);
	mir_free(tszUserId);
	mir_free(tszConfId);
}

/** triggered when someone leaves the conference */
void mwServiceConf_on_peer_parted(mwConference* conf, mwLoginInfo* user)
{
	CSametimeProto* proto = getProtoFromMwConference(conf);
	proto->debugLog(_T("mwServiceConf_on_peer_parted() start"));

	TCHAR* tszConfId = mir_utf8decodeT(mwConference_getName(conf));

	// remove user
	GCDEST gcd = { proto->m_szModuleName };
	gcd.ptszID = tszConfId;
	gcd.iType = GC_EVENT_PART;

	GCEVENT gce = { sizeof(gce), &gcd };
	gce.dwFlags = GCEF_ADDTOLOG;
	TCHAR* tszUserName = mir_utf8decodeT(((mwLoginInfo*)user)->user_name);
	TCHAR* tszUserId = mir_utf8decodeT(((mwLoginInfo*)user)->login_id);
	gce.ptszNick = tszUserName;
	gce.ptszUID = tszUserId;
	gce.ptszStatus = _T("Normal");
	gce.time = (DWORD)time(0);

	CallServiceSync(MS_GC_EVENT, 0, (LPARAM)(GCEVENT *) &gce);

	mir_free(tszUserName);
	mir_free(tszUserId);
	mir_free(tszConfId);
}

/** triggered when someone says something */
void mwServiceConf_on_text(mwConference* conf, mwLoginInfo* user, const char* what)
{
	CSametimeProto* proto = getProtoFromMwConference(conf);
	proto->debugLog(_T("mwServiceConf_on_text() start"));

	TCHAR* tszConfId = mir_utf8decodeT(mwConference_getName(conf));

	GCDEST gcd = { proto->m_szModuleName };
	gcd.ptszID = tszConfId;
	gcd.iType = GC_EVENT_MESSAGE;

	GCEVENT gce = { sizeof(gce), &gcd };
	gce.dwFlags = GCEF_ADDTOLOG;

	TCHAR* textT = mir_utf8decodeT(what);
	TCHAR* tszUserName = mir_utf8decodeT(((mwLoginInfo*)user)->user_name);
	TCHAR* tszUserId = mir_utf8decodeT(((mwLoginInfo*)user)->login_id);
	gce.ptszText = textT;
	gce.ptszNick = tszUserName;
	gce.ptszUID = tszUserId;
	gce.time = (DWORD)time(0);

	CallService(MS_GC_EVENT, 0, (LPARAM)(GCEVENT *) &gce);

	mir_free(textT);
	mir_free(tszUserName);
	mir_free(tszUserId);
	mir_free(tszConfId);
}

/** typing notification */
void mwServiceConf_on_typing(mwConference* conf, mwLoginInfo* who, gboolean typing)
{
	CSametimeProto* proto = getProtoFromMwConference(conf);
	proto->debugLog(_T("mwServiceConf_on_typing() start"));
	///TODO unimplemented
}

/** optional. called from mwService_free */
void mwServiceConf_clear(mwServiceConference* srvc)
{
}

mwConferenceHandler mwConference_handler = {
	mwServiceConf_on_invited,
	mwServiceConf_conf_opened,
	mwServiceConf_conf_closed,
	mwServiceConf_on_peer_joined,
	mwServiceConf_on_peer_parted,
	mwServiceConf_on_text,
	mwServiceConf_on_typing,
	mwServiceConf_clear
};

void CSametimeProto::TerminateConference(char* name)
{
	debugLog(_T("CSametimeProto::TerminateConference() start"));
		
	GList *conferences, *conf;
	conferences = conf = mwServiceConference_getConferences(service_conference);
	for (;conf;conf = conf->next) {
		if (strcmp(name, mwConference_getName((mwConference*)conf->data)) == 0) {

			TCHAR* idt = mir_utf8decodeT(name);
			GCDEST gcd = {m_szModuleName, idt, GC_EVENT_CONTROL};

			GCEVENT gce = { sizeof(gce), &gcd };
			gce.dwFlags = GCEF_ADDTOLOG;
			CallService(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce);
			
			mir_free(idt);
		}
	}
	g_list_free(conferences);
}


int CSametimeProto::GcEventHook(WPARAM wParam, LPARAM lParam) {

	GCHOOK* gch = (GCHOOK*)lParam;

	if (strcmp(gch->pDest->pszModule, m_szModuleName) != 0) return 0;

	GList *conferences, *conf;
	conferences = conf = mwServiceConference_getConferences(service_conference);
	for (;conf;conf = conf->next) {
		TCHAR* tszConfId = mir_utf8decodeT(mwConference_getName((mwConference*)conf->data));
		if (_tcscmp(gch->pDest->ptszID, tszConfId) == 0) {
			
			switch(gch->pDest->iType) {
			case GC_USER_MESSAGE:
				{
					debugLog(_T("CSametimeProto::GcEventHook() GC_USER_MESSAGE"));
					char* utf_msg;
					utf_msg = mir_utf8encodeT(gch->ptszText);
					mwConference_sendText((mwConference*)conf->data, utf_msg);
					mir_free(utf_msg);
				}
				break;
			case GC_SESSION_TERMINATE:
				{
					if (my_conference == conf->data){
						debugLog(_T("CSametimeProto::GcEventHook() GC_SESSION_TERMINATE CloseMyConference"));
						CloseMyConference(this);
					} else {
						debugLog(_T("CSametimeProto::GcEventHook() GC_SESSION_TERMINATE mwConference_destroy"));
						char* utfs = mir_utf8encodeT(TranslateT("I'm outa here."));
						mwConference_destroy((mwConference*)conf->data, 0, utfs);
						mir_free(utfs);
					}
				}
				break;
			}

			break;
		}
		mir_free(tszConfId);
	}

	g_list_free(conferences);

	return 0;
}

int CSametimeProto::ChatDeleted(MCONTACT hContact) {
	
	if (db_get_b(hContact, m_szModuleName, "ChatRoom", 0) == 0)
		return 0;

	debugLog(_T("CSametimeProto::ChatDeleted() hContact=[%x]"), hContact);
	DBVARIANT dbv;
	if (!db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv)) {
		TerminateConference(dbv.pszVal);
		db_free(&dbv);
	}

	return 0;
}


INT_PTR CSametimeProto::onMenuLeaveChat(WPARAM wParam, LPARAM lParam)
{
	MCONTACT hContact = (MCONTACT)wParam;
	debugLog(_T("CSametimeProto::onMenuLeaveChat() hContact=[%x]"), hContact);
	ChatDeleted(hContact);
	return 0;
}


INT_PTR CSametimeProto::onMenuCreateChat(WPARAM wParam, LPARAM lParam)
{
	MCONTACT hContact = (MCONTACT)wParam;
	debugLog(_T("CSametimeProto::onMenuCreateChat() hContact=[%x]"), hContact);
	mwAwareIdBlock id_block;
	mwIdBlock idb;
	if (my_login_info && GetAwareIdFromContact(hContact, &id_block)) {
		TCHAR title[512];
		TCHAR* ts = mir_utf8decodeT(my_login_info->user_name);
		mir_sntprintf(title, SIZEOF(title), TranslateT("%s's conference"), ts);
		mir_free(ts);

		idb.user = id_block.user;
		idb.community = id_block.community;

		invite_queue.push(idb.user);

		if (!my_conference) {
			debugLog(_T("CSametimeProto::onMenuCreateChat() mwConference_open"));
			char* utfs;
			my_conference = mwConference_new(service_conference, utfs = mir_utf8encodeT(title));
			mwConference_open(my_conference);
			mir_free(utfs);
		} else {
			debugLog(_T("CSametimeProto::onMenuCreateChat() ClearInviteQueue"));
			ClearInviteQueue();
		}

		free(id_block.user);
	}

	return 0;
}

int CSametimeProto::PrebuildContactMenu(WPARAM wParam, LPARAM lParam)
{
	MCONTACT hContact = (MCONTACT)wParam;
	debugLog(_T("CSametimeProto::PrebuildContactMenu() hContact=[%x]"), hContact);
	CLISTMENUITEM mi = {0};
	mi.cbSize = sizeof(mi);
	mi.flags = CMIM_FLAGS | (db_get_b(hContact, m_szModuleName, "ChatRoom", 0) == 1 ? 0 : CMIF_HIDDEN);
	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hLeaveChatMenuItem, (LPARAM)&mi);

	// if user is already in our meeting, 
	bool not_present = true;
	DBVARIANT dbv;
	if (my_conference && !db_get_utf(hContact, m_szModuleName, "stid", &dbv)) {
		char* user_id = dbv.pszVal;

		GList *members, *mem;
		members = mem = mwConference_getMembers(my_conference);
		for (;mem;mem=mem->next) {
			if (my_login_info && strcmp(user_id, ((mwLoginInfo *)mem->data)->user_id) == 0) {
				not_present = false;
				break;
			}
		}
		g_list_free(members);

		db_free(&dbv);
	}
	mi.flags = CMIM_FLAGS | CMIF_NOTOFFLINE | (db_get_b(hContact, m_szModuleName, "ChatRoom", 0) == 0 && not_present ? 0 : CMIF_HIDDEN);
	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hCreateChatMenuItem, (LPARAM)&mi);

	return 0;
}

void CSametimeProto::InitConference()
{
	debugLog(_T("CSametimeProto::InitConference()"));

	my_login_info = mwSession_getLoginInfo(session);

	service_conference = mwServiceConference_new(session, &mwConference_handler);
	mwSession_addService(session, (struct mwService*)service_conference);

	HookProtoEvent(ME_GC_EVENT, &CSametimeProto::GcEventHook);
}

void CSametimeProto::DeinitConference()
{
	GList *conferences, *conf;
	debugLog(_T("CSametimeProto::DeinitConference()"));

	if (service_conference){
		conferences = conf = mwServiceConference_getConferences(service_conference);
		for (;conf;conf = conf->next) {
			if (my_conference == conf->data) CloseMyConference(this);
			else {
				char* utfs = mir_utf8encodeT(TranslateT("I'm outa here."));
				mwConference_destroy((mwConference*)conf->data, 0, utfs);
				mir_free(utfs);
			}
		}
		g_list_free(conferences);
	}

	my_login_info = 0;

	mwSession_removeService(session, mwService_CONFERENCE);
	if (service_conference){
		mwService_free((mwService*)service_conference);
		service_conference = 0;
	}
}

void CSametimeProto::InitConferenceMenu()
{
	debugLog(_T("CSametimeProto::InitConferenceMenu()"));

	CreateProtoService(MS_SAMETIME_MENULEAVECHAT, &CSametimeProto::onMenuLeaveChat);
	CreateProtoService(MS_SAMETIME_MENUCREATECHAT, &CSametimeProto::onMenuCreateChat);

	char service[128];

	CLISTMENUITEM mi = { sizeof(mi) };
	mi.flags = CMIF_TCHAR | CMIF_NOTOFFLINE;
	mi.pszContactOwner = m_szModuleName;
	
	mi.ptszName = LPGENT("Leave conference");
	mir_snprintf(service, sizeof(service), "%s%s", m_szModuleName, MS_SAMETIME_MENULEAVECHAT);
	mi.pszService = service;
	mi.icolibItem = GetIconHandle(IDI_ICON_LEAVE);
	hLeaveChatMenuItem = Menu_AddContactMenuItem(&mi);

	mi.ptszName = LPGENT("Start conference");
	mir_snprintf(service, sizeof(service), "%s%s", m_szModuleName, MS_SAMETIME_MENUCREATECHAT);
	mi.pszService = service;
	mi.icolibItem = GetIconHandle(IDI_ICON_INVITE);
	hCreateChatMenuItem = Menu_AddContactMenuItem(&mi);

	HookProtoEvent(ME_CLIST_PREBUILDCONTACTMENU, &CSametimeProto::PrebuildContactMenu);
}

void CSametimeProto::DeinitConferenceMenu()
{
	debugLog(_T("CSametimeProto::DeinitConferenceMenu()"));
	CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hLeaveChatMenuItem, (LPARAM)0);
	CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hCreateChatMenuItem, (LPARAM)0);
}