/*
Copyright © 2009 Jim Porter
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"
#include "proto.h"
void TwitterProto::AddToListWorker(void *p)
{
	// TODO: what happens if there is an error?
	if (p == 0)
		return;
	TCHAR *name = static_cast(p);
	try
	{
		ScopedLock s(twitter_lock_);
		twitter_user user = twit_.add_friend(name);
		s.Unlock();
		HANDLE hContact = UsernameToHContact(name);
		UpdateAvatar(hContact, user.profile_image_url);
	}
	catch(const std::exception &e)
	{
		ShowPopup((std::string("While adding a friend, an error occurred: ")
			+e.what()).c_str());
		LOG("***** Error adding friend: %s", e.what());
	}
	mir_free(name);
}
HANDLE TwitterProto::AddToList(int flags, PROTOSEARCHRESULT *result)
{
	if (m_iStatus != ID_STATUS_ONLINE)
		return 0;
	ForkThread(&TwitterProto::AddToListWorker, this, mir_tstrdup(result->nick));
	return AddToClientList(result->nick, _T(""));
}
// *************************
void TwitterProto::UpdateInfoWorker(void *hContact)
{
	twitter_user user;
	std::tstring username;
	
	DBVARIANT dbv;
	if ( !DBGetContactSettingTString(hContact, m_szModuleName, TWITTER_KEY_UN, &dbv))
	{
		username = dbv.ptszVal;
		DBFreeVariant(&dbv);
	}
	else
		return;
	{
		ScopedLock s(twitter_lock_);
		twit_.get_info(username, &user);
	}
	UpdateAvatar(hContact, user.profile_image_url, true);
	ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, 0, 0);
}
int TwitterProto::GetInfo(HANDLE hContact, int info_type)
{
	if (m_iStatus != ID_STATUS_ONLINE)
		return 1;
	if (!IsMyContact(hContact)) // Do nothing for chat rooms
		return 1;
	if (info_type == 0) // From clicking "Update" in the Userinfo dialog
	{
		ForkThread(&TwitterProto::UpdateInfoWorker, this, hContact);
		return 0;
	}
	return 1;
}
// *************************
struct search_query
{
	search_query(const std::tstring &query, bool by_email) : query(query), by_email(by_email)
	{}
	std::tstring query;
	bool by_email;
};
void TwitterProto::DoSearch(void *p)
{
	if (p == 0)
		return;
	search_query *query = static_cast(p);
	twitter_user info;
	PROTOSEARCHRESULT psr = {sizeof(psr)};
	bool found;
	try
	{
		ScopedLock s(twitter_lock_);
		if (query->by_email)
			found = twit_.get_info_by_email(query->query, &info);
		else
			found = twit_.get_info(query->query, &info);
	}
	catch(const std::exception &e)
	{
		ShowPopup( (std::string("While searching for contacts, an error occurred: ")
			+e.what()).c_str());
		LOG("***** Error searching for contacts: %s", e.what());
	}
	if (found)
	{
		psr.nick      = ( TCHAR* )info.username. c_str();
		psr.firstName = ( TCHAR* )info.real_name.c_str();
		ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)1, 
			(LPARAM)&psr);
		ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)1, 0);
	}
	else
	{
		ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)1, 0);
	}
	delete query;
}
HANDLE TwitterProto::SearchBasic(const TCHAR *username)
{
	ForkThread(&TwitterProto::DoSearch, this, new search_query(username, false));
	return (HANDLE)1;
}
HANDLE TwitterProto::SearchByEmail(const TCHAR *email)
{
	ForkThread(&TwitterProto::DoSearch, this, new search_query(email, true));
	return (HANDLE)1;
}
// *************************
void TwitterProto::GetAwayMsgWorker(void *hContact)
{
	if (hContact == 0)
		return;
	DBVARIANT dbv;
	if ( !DBGetContactSettingTString(hContact, "CList", "StatusMsg", &dbv)) {
		ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)dbv.ptszVal);
		DBFreeVariant(&dbv);
	}
	else ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AWAYMSG, ACKRESULT_FAILED, (HANDLE)1, 0);
}
HANDLE TwitterProto::GetAwayMsg(HANDLE hContact)
{
	ForkThread(&TwitterProto::GetAwayMsgWorker, this, hContact);
	return (HANDLE)1;
}
int TwitterProto::OnContactDeleted(WPARAM wParam, LPARAM lParam)
{
	if (m_iStatus != ID_STATUS_ONLINE)
		return 0;
	const HANDLE hContact = reinterpret_cast(wParam);
	if (!IsMyContact(hContact))
		return 0;
	DBVARIANT dbv;
	if ( !DBGetContactSettingTString(hContact, m_szModuleName, TWITTER_KEY_UN, &dbv)) {
		if ( in_chat_ )
			DeleteChatContact(dbv.ptszVal);
		ScopedLock s(twitter_lock_);
		twit_.remove_friend(dbv.ptszVal); // Be careful about this until Miranda is fixed
		DBFreeVariant(&dbv);
	}
	return 0;
}
// *************************
bool TwitterProto::IsMyContact(HANDLE hContact, bool include_chat)
{
	const char *proto = reinterpret_cast( CallService(MS_PROTO_GETCONTACTBASEPROTO, 
		reinterpret_cast(hContact), 0));
	if (proto && strcmp(m_szModuleName, proto) == 0)
	{
		if (include_chat)
			return true;
		else
			return DBGetContactSettingByte(hContact, m_szModuleName, "ChatRoom", 0) == 0;
	}
	else
		return false;
}
HANDLE TwitterProto::UsernameToHContact(const TCHAR *name)
{
	for(HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
		hContact;
		hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
	{
		if (!IsMyContact(hContact))
			continue;
		DBVARIANT dbv;
		if ( !DBGetContactSettingTString(hContact, m_szModuleName, TWITTER_KEY_UN, &dbv)) {
			if ( lstrcmp(name, dbv.ptszVal) == 0) {
				DBFreeVariant(&dbv);
				return hContact;
			}
			DBFreeVariant(&dbv);
		}
	}
	return 0;
}
HANDLE TwitterProto::AddToClientList(const TCHAR *name, const TCHAR *status)
{
	// First, check if this contact exists
	HANDLE hContact = UsernameToHContact(name);
	if (hContact)
		return hContact;
	if (in_chat_)
		AddChatContact(name);
	// If not, make a new contact!
	hContact = (HANDLE)CallService(MS_DB_CONTACT_ADD, 0, 0);
	if (hContact ) {
		if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)m_szModuleName) == 0) {
			DBWriteContactSettingTString(hContact, m_szModuleName, TWITTER_KEY_UN, name);
			DBWriteContactSettingWord(hContact, m_szModuleName, "Status", ID_STATUS_ONLINE);
			DBWriteContactSettingTString(hContact, "CList", "StatusMsg", status);
			std::string url = profile_base_url(twit_.get_base_url()) + http::url_encode((char*)_T2A(name));
			DBWriteContactSettingString(hContact, m_szModuleName, "Homepage", url.c_str());
			return hContact;
		}
		CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0);
	}
	return 0;
}
void TwitterProto::SetAllContactStatuses(int status)
{
	for(HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
		hContact;
		hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
	{
		if (!IsMyContact(hContact))
			continue;
		DBWriteContactSettingWord(hContact, m_szModuleName, "Status", status);
	}
	SetChatStatus(status);
}