/*
Facebook plugin for Miranda Instant Messenger
_____________________________________________
Copyright © 2009-11 Michal Zelinka, 2011-13 Robert Pösel
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 .
*/
#include "common.h"
void FacebookProto::ChangeStatus(void*)
{
	ScopedLock s(signon_lock_);
	ScopedLock b(facy.buddies_lock_);
	
	int new_status = m_iDesiredStatus;
	int old_status = m_iStatus;
	if (new_status == ID_STATUS_OFFLINE)
	{ // Logout	
		LOG("##### Beginning SignOff process");
		m_iStatus = facy.self_.status_id = ID_STATUS_OFFLINE;
		SetEvent(update_loop_lock_);
		Netlib_Shutdown(facy.hMsgCon);
		if (getByte(FACEBOOK_KEY_DISCONNECT_CHAT, DEFAULT_DISCONNECT_CHAT))
			facy.chat_state(false);
		facy.logout();
		deleteSetting("LogonTS");
		facy.clear_cookies();
		facy.buddies.clear();
		facy.messages_ignore.clear();
		ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
		OnLeaveChat(NULL, NULL);
		SetAllContactStatuses(ID_STATUS_OFFLINE, true);
		ToggleStatusMenuItems(false);
		if (facy.hMsgCon)
			Netlib_CloseHandle(facy.hMsgCon);
		facy.hMsgCon = NULL;
		LOG("##### SignOff complete");
		return;
	}
	else if (old_status == ID_STATUS_OFFLINE)
	{ // Login
		SYSTEMTIME t;
		GetLocalTime(&t);
		Log("[%d.%d.%d] Using Facebook Protocol RM %s", t.wDay, t.wMonth, t.wYear, __VERSION_STRING);
		
		LOG("***** Beginning SignOn process");
		m_iStatus = facy.self_.status_id = ID_STATUS_CONNECTING;
		ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
		ResetEvent(update_loop_lock_);
		if (NegotiateConnection() && facy.home())
		{		
			facy.reconnect();
			facy.load_friends();
			// Process Friends requests
			ForkThread(&FacebookProto::ProcessFriendRequests, this, NULL);
			// Get unread messages
			ForkThread(&FacebookProto::ProcessUnreadMessages, this);
			// Get notifications
			ForkThread(&FacebookProto::ProcessNotifications, this);
			setDword("LogonTS", (DWORD)time(NULL));
			ForkThread(&FacebookProto::UpdateLoop,  this);
			ForkThread(&FacebookProto::MessageLoop, this);
			if (getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, DEFAULT_SET_MIRANDA_STATUS))
			{
				ForkThread(&FacebookProto::SetAwayMsgWorker, this, NULL);
			}
		} else {
			ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)old_status, m_iStatus);
			if (facy.hFcbCon)
				Netlib_CloseHandle(facy.hFcbCon);
			facy.hFcbCon = NULL;
			facy.clear_cookies();
			// Set to offline
			m_iStatus = m_iDesiredStatus = facy.self_.status_id = ID_STATUS_OFFLINE;
			ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
			LOG("***** SignOn failed");
			return;
		}
		ToggleStatusMenuItems(true);
		LOG("***** SignOn complete");
	}
	else if (new_status == ID_STATUS_INVISIBLE)
	{
		facy.buddies.clear();
		this->SetAllContactStatuses(ID_STATUS_OFFLINE, true);
	}
	facy.chat_state(m_iDesiredStatus != ID_STATUS_INVISIBLE);	
	facy.buddy_list();
	m_iStatus = facy.self_.status_id = m_iDesiredStatus;
	ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
	LOG("***** ChangeStatus complete");
}
/** Return true on success, false on error. */
bool FacebookProto::NegotiateConnection()
{
	LOG("***** Negotiating connection with Facebook");
	bool error;
	std::string user, pass;
	DBVARIANT dbv = {0};
	error = true;
	if (!db_get_s(NULL,m_szModuleName,FACEBOOK_KEY_LOGIN,&dbv))
	{
		user = dbv.pszVal;
		db_free(&dbv);
		error = user.empty();
	}
	if (error)
	{
		NotifyEvent(m_tszUserName,TranslateT("Please enter a username."),NULL,FACEBOOK_EVENT_CLIENT);
		return false;
	}
	error = true;
	if (!db_get_s(NULL,m_szModuleName,FACEBOOK_KEY_PASS,&dbv))
	{
		CallService(MS_DB_CRYPT_DECODESTRING,strlen(dbv.pszVal)+1,
			reinterpret_cast(dbv.pszVal));
		pass = dbv.pszVal;
		db_free(&dbv);
		error = pass.empty();
	}
	if (error)
	{
		NotifyEvent(m_tszUserName,TranslateT("Please enter a password."),NULL,FACEBOOK_EVENT_CLIENT);
		return false;
	}
	// Load machine name
	if (!db_get_s(NULL,m_szModuleName,FACEBOOK_KEY_DEVICE_ID,&dbv))
	{
		facy.cookies["datr"] = dbv.pszVal;
		db_free(&dbv);
	}
	// Refresh last time of feeds update
	facy.last_feeds_update_ = ::time(NULL);
	// Get info about secured connection
	facy.https_ = db_get_b(NULL, m_szModuleName, FACEBOOK_KEY_FORCE_HTTPS, DEFAULT_FORCE_HTTPS) != 0;
	// Create default group for new contacts
	if (!db_get_ts(NULL, m_szModuleName, FACEBOOK_KEY_DEF_GROUP, &dbv) && lstrlen(dbv.ptszVal) > 0)
	{
		CallService(MS_CLIST_GROUPCREATE, 0, (LPARAM)dbv.ptszVal);
	}
	
	return facy.login(user, pass);
}
void FacebookProto::UpdateLoop(void *)
{
	time_t tim = ::time(NULL);
	LOG(">>>>> Entering Facebook::UpdateLoop[%d]", tim);
	for (int i = -1; !isOffline(); i = ++i % 50)
	{
		if (i != -1) {
			if (!facy.invisible_)
				if (!facy.buddy_list())
    				break;
		}
		if (i == 2 && getByte(FACEBOOK_KEY_EVENT_FEEDS_ENABLE, DEFAULT_EVENT_FEEDS_ENABLE))
			if (!facy.feeds())
				break;
		if (i == 49)
			ForkThread(&FacebookProto::ProcessFriendRequests, this, NULL);
		LOG("***** FacebookProto::UpdateLoop[%d] going to sleep...", tim);
		if (WaitForSingleObjectEx(update_loop_lock_, GetPollRate() * 1000, true) != WAIT_TIMEOUT)
			break;
		LOG("***** FacebookProto::UpdateLoop[%d] waking up...", tim);
	}
	ResetEvent(update_loop_lock_);
	LOG("<<<<< Exiting FacebookProto::UpdateLoop[%d]", tim);
}
void FacebookProto::MessageLoop(void *)
{
	time_t tim = ::time(NULL);
	LOG(">>>>> Entering Facebook::MessageLoop[%d]", tim);
	while (facy.channel())
	{
		if (isOffline())
			break;
		LOG("***** FacebookProto::MessageLoop[%d] refreshing...", tim);
	}
	LOG("<<<<< Exiting FacebookProto::MessageLoop[%d]", tim);
}
BYTE FacebookProto::GetPollRate()
{
	BYTE poll_rate = getByte(FACEBOOK_KEY_POLL_RATE, FACEBOOK_DEFAULT_POLL_RATE);
	return (
	    (poll_rate >= FACEBOOK_MINIMAL_POLL_RATE &&
	      poll_rate <= FACEBOOK_MAXIMAL_POLL_RATE)
	    ? poll_rate : FACEBOOK_DEFAULT_POLL_RATE);
}