// DWBS ------------------------------------------------------------------ DWBS
//
// PLATFORM		: ALL
// PRODUCT		: BIT_DEMON
// VISIBILITY	: PUBLIC
//
// DWBS ------------------------------------------------------------------ DWBS

#include <bdLobby/bdMessageStore/bdMessageStore.h>

#include <bdLobby/bdMessageStore/bdLobbyMessageFactory.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdLobbyMessageType.h>

#include <bdLobby/bdMessageStore/bdMessageTypes/bdChannelChatMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdChannelNotificationMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdFriendCustomMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdFriendSessionInvitationMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdFriendshipProposalMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdTeamCustomMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdTeamInvitationMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdTeamNotificationMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdTeamWideNotificationMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdTeamSessionInvitationMessage.h>
#include <bdLobby/bdMessageStore/bdMessageTypes/bdUserNotificationMessage.h>

#include <bdLobby/bdRemoteTaskManager/bdRemoteTaskManager.h>

#define BD_LOG_LEVEL	("lobby/messageStore")

bdMessageStore::bdMessageStore()
:	m_errorCode(BD_NO_ERROR),
	m_remoteTaskManager(BD_NULL)
{
	bdLobbyMessageFactoryImpl *const factory = bdLobbyMessageFactory::getInstance();
	// we only wish to create these classes once
	if(factory && !factory->containsClass(BD_FRIENDSHIP_PROPOSAL_MSG))
	{
		// register the message classes
#define BD_LOBBY_MESSAGE_TYPES_BEGIN
#define BD_LOBBY_MESSAGE_TYPE(MSG_TYPE_NAME, MSG_TYPE_VAL, MSG_IMPL, MSG_CLASSNAME)	BD_REGISTER_LOBBY_MESSAGE_TYPE(MSG_TYPE_VAL, MSG_IMPL)
#define BD_LOBBY_MESSAGE_TYPES_END(MSG_TYPE_NAME)

#include <bdLobby/bdMessageStore/bdMessageTypes/bdLobbyMessageTypes.h>

#undef BD_LOBBY_MESSAGE_TYPES_BEGIN
#undef BD_LOBBY_MESSAGE_TYPE
#undef BD_LOBBY_MESSAGE_TYPES_END
	}
}

void bdMessageStore::setRemoteTaskManager(bdRemoteTaskManager *const remoteTaskManager)
{
	m_remoteTaskManager = remoteTaskManager;
}

bdMessageStore::~bdMessageStore()
{
	m_remoteTaskManager = BD_NULL;
}

bdRemoteTask* bdMessageStore::getMessages(	bdUByte8 /*controllerIndex*/,
											bdUInt startIndex,
											bdUInt maxNumMessages,
											bdBool markAsRead,
											bdBool deleteMessages)
{
	if (!m_remoteTaskManager)
	{
		m_errorCode = BD_NOT_CONNECTED;
		return BD_NULL;
	}

	bdRemoteTask *remoteTask = BD_NULL;
	bdBitBuffer buffer (0,true);
	buffer.writeUByte8(0);
	buffer.writeUInt32(startIndex);
	buffer.writeUInt32(maxNumMessages);
	buffer.writeBool(markAsRead);
	buffer.writeBool(deleteMessages);

	m_errorCode = m_remoteTaskManager->startTask(	&remoteTask,
													BD_MESSAGING_SERVICE_ID,
													BD_LOBBY_MSG_OP_GET,
													buffer);

	return remoteTask;
}

bdRemoteTask* bdMessageStore::getMessagesByType(const bdLobbyMessageType *msgTypes,
												bdUInt numTypes,
												bdUInt startIndex,
												bdUInt maxNumMessages,
												bdBool getOnlyUnRead,
												bdBool markAsRead,
												bdBool deleteMessages)
{
	if (!m_remoteTaskManager)
	{
		m_errorCode = BD_NOT_CONNECTED;
		return BD_NULL;
	}

	bdRemoteTask *remoteTask = BD_NULL;
	bdBitBuffer buffer (0,true);
	buffer.writeUByte8(0);
	buffer.writeUInt32(startIndex);
	buffer.writeUInt32(maxNumMessages);
	buffer.writeBool(getOnlyUnRead);
	buffer.writeBool(markAsRead);
	buffer.writeBool(deleteMessages);
	
	for(bdUInt i = 0; i < numTypes; i++, msgTypes++)
	{
		buffer.writeUInt32(*msgTypes);
	}

	m_errorCode = m_remoteTaskManager->startTask(&remoteTask,
												 BD_MESSAGING_SERVICE_ID,
												 BD_LOBBY_MSG_OP_GET_TYPE,
												 buffer);
	return remoteTask;
}

bdRemoteTask* bdMessageStore::deleteMessage(bdUByte8 /*controllerIndex*/,
											bdUInt64 messageID)
{
	if (!m_remoteTaskManager)
	{
		m_errorCode = BD_NOT_CONNECTED;
		return BD_NULL;
	}
	bdRemoteTask *remoteTask = BD_NULL;
	bdBitBuffer buffer (0,true);
	buffer.writeUByte8(0);
	buffer.writeUInt64(messageID);

	m_errorCode = m_remoteTaskManager->startTask(&remoteTask,
												 BD_MESSAGING_SERVICE_ID,
												 BD_LOBBY_MSG_OP_DELETE,
												 buffer);
	return remoteTask;
}

bdRemoteTask* bdMessageStore::deleteMessages(const bdUInt64 *messageIDs,
											 bdUInt numMessageIDs)
{
	if (!m_remoteTaskManager)
	{
		m_errorCode = BD_NOT_CONNECTED;
		return BD_NULL;
	}
	bdRemoteTask *remoteTask = BD_NULL;
	bdBitBuffer buffer (0,true);
	buffer.writeUByte8(0);
	for (bdUInt i = 0; i < numMessageIDs; i++, messageIDs++)
	{
		buffer.writeUInt64(*messageIDs);
	}

	m_errorCode = m_remoteTaskManager->startTask(&remoteTask,
												 BD_MESSAGING_SERVICE_ID,
												 BD_LOBBY_MSG_OP_DELETE_MESSAGES,
												 buffer);

	return remoteTask;
}

bdRemoteTask* bdMessageStore::sendMessageToUsers(const bdOnlineUserID *userIDs,
												 bdUInt numUserIDs,
												 bdByteBuffer& payload,
												 bdMessageDeliveryMethod deliveryMethod)
{
	bdRemoteTask *remoteTask = BD_NULL;

	remoteTask = sendMessage(userIDs, numUserIDs, payload, deliveryMethod, false);

	return remoteTask;
}

bdRemoteTask* bdMessageStore::sendMessageToTeam(bdTeamID teamID,
												bdByteBuffer& payload,
												bdMessageDeliveryMethod deliveryMethod)
{
	bdRemoteTask *remoteTask = BD_NULL;

	remoteTask = sendMessage(&teamID, 1, payload, deliveryMethod, true);

	return remoteTask;
}

bdRemoteTask* bdMessageStore::sendMessage(const bdEntityID *entityIDs,
										  bdUInt numUserIDs,
										  bdByteBuffer& payload,
										  bdMessageDeliveryMethod deliveryMethod,
										  bdBool isTeam)
{
	bdRemoteTask *remoteTask = BD_NULL;
	bdBitBuffer buffer(0, true);
	buffer.writeUByte8(0);
	/*
	bdByteBuffer *buf = new bdByteBuffer(BD_MAXIMUM_CUSTOM_MSG_SIZE+sizeof(bdUInt));
	bdUInt bufSize = payload.getSize()+sizeof(bdUInt);
	bdUInt payloadSize = payload.getSize();
	buf->write(&payloadSize, sizeof(bdUInt));
	buf->write(payload.getData());
	buffer.writeBlob(buf, bufSize);
	*/
	buffer.writeBlob(payload.getData(),payload.getSize());
	buffer.writeUInt32(static_cast<bdUInt32>(deliveryMethod));
	buffer.writeBool(isTeam);

	if(isTeam)
	{
		buffer.writeUInt64(entityIDs[0]);
	}
	else
	{
		for (bdUInt i = 0; i < numUserIDs; i++, entityIDs++)
		{
			buffer.writeUInt64(*entityIDs);
		}
	}

	m_errorCode = m_remoteTaskManager->startTask(&remoteTask,
												 BD_MESSAGING_SERVICE_ID,
												 BD_LOBBY_MSG_OP_SEND_MESSAGE,
												 buffer);
	return remoteTask;
}

bdRemoteTask* bdMessageStore::getUnreadMessages(bdUByte8 /*controllerIndex*/,
												bdUInt startIndex,
												bdUInt maxNumMessages,
												bdBool markAsRead,
												bdBool deleteMessages)
{
	if (!m_remoteTaskManager)
	{
		m_errorCode = BD_NOT_CONNECTED;
		return BD_NULL;
	}
	bdRemoteTask *remoteTask = BD_NULL;
	bdBitBuffer buffer (0,true);
	buffer.writeUByte8(0);
	buffer.writeUInt32(startIndex);
	buffer.writeUInt32(maxNumMessages);
	buffer.writeBool(markAsRead);
	buffer.writeBool(deleteMessages);

	m_errorCode = m_remoteTaskManager->startTask(	&remoteTask,
													BD_MESSAGING_SERVICE_ID,
													BD_LOBBY_MSG_OP_GET_UNREAD,
													buffer);

	return remoteTask;
}

bdRemoteTask* bdMessageStore::setMessageStatus(bdUByte8 /*controllerIndex*/,
											   bdUInt64 messageID,
											   bdBool markMessageAsRead)
{
	if (!m_remoteTaskManager)
	{
		m_errorCode = BD_NOT_CONNECTED;
		return BD_NULL;
	}
	bdRemoteTask *remoteTask = BD_NULL;
	bdBitBuffer buffer (0,true);
	buffer.writeUByte8(0);
	buffer.writeUInt64(messageID);
	buffer.writeBool(markMessageAsRead);

	m_errorCode = m_remoteTaskManager->startTask(	&remoteTask,
													BD_MESSAGING_SERVICE_ID,
													BD_LOBBY_MSG_OP_SET_READSTATUS,
													buffer);

	return remoteTask;
}

bdUInt bdMessageStore::getNumMessages() const
{
	return m_messages.getSize();
}

bdLobbyMessageRef bdMessageStore::getAt(bdUInt index)
{
	return m_messages[index];
}

void bdMessageStore::removeAt(bdUInt index)
{
	m_messages.removeAt(index);
}

bdBool bdMessageStore::isUniqueMsgID(bdUInt64 msgID)
{
	bdUInt size = m_messages.getSize();

	bdUInt i;
	for (i=0; i<size; i++)
	{
		if (m_messages[i]->m_msgID == msgID)
		{
			return false;
		}
	}

	return true;
}

bdBool bdMessageStore::handleTask(const bdRemoteTask *remoteTask)
{
	bdBitBufferRef buffer = remoteTask->getResults();
	if (buffer.isNull())
	{
		return false;
	}
	bdBool ok = false;
	bdUInt32 taskErrorCode = BD_MAX_ERROR_CODE;
	ok = buffer->readUInt32(taskErrorCode);
	if (ok)
	{
		m_errorCode = static_cast<bdLobbyErrorCode>(taskErrorCode);
		if (m_errorCode == BD_NO_ERROR)
		{
			bdUByte8 operation = BD_LOBBY_MSG_OP_GET+1;
			ok = buffer->readUByte8(operation);

			if(ok && 
			   (operation == BD_LOBBY_MSG_OP_GET || 
			    operation == BD_LOBBY_MSG_OP_GET_UNREAD) || 
				operation == BD_LOBBY_MSG_OP_GET_TYPE)
			{
				bdUInt numMessages = 0;
				ok = buffer->readUInt32(numMessages);

				for(bdUInt i=0; ok && (i<numMessages); i++)
				{
					ok = createMessage(buffer);
				}
			}
		}
#if defined BD_DEBUG
		else
		{
			bdUInt64 transactionID;
			bdUInt64 connectionID;

			connectionID = m_remoteTaskManager->getConnectionID();
			transactionID = remoteTask->getTransactionID();

			// here we originally called bdRemoteTaskManager::obtainTransactionID
			BD_WARN(BD_LOG_LEVEL, "Transaction ID " BD_UINT_64_FORMAT ":" BD_UINT_64_FORMAT " failed.",
				connectionID, transactionID);
		}
#endif // BD_DEBUG
	}
	return (ok && (m_errorCode == BD_NO_ERROR));
}

bdLobbyErrorCode bdMessageStore::getErrorCode() const
{
	return m_errorCode;
}

bdBool bdMessageStore::createMessage(bdBitBufferRef buffer)
{
	bdBool ok = true;

	bdLobbyMessageRef newMessage;
	bdUInt msgType = 0;
	ok = ok && buffer->readUInt32(msgType);
	newMessage = bdLobbyMessageFactory::getInstance()->create(msgType);
	if (newMessage != BD_NULL)
	{
		ok = ok && newMessage->deserialize(buffer);

		if (isUniqueMsgID(newMessage->m_msgID) || newMessage->m_msgID == 0)
		{
			m_messages.pushBack(newMessage);
		}
	}
	return ok;
}

void bdMessageStore::clearMessages()
{
	m_messages.clear();
}
