/*
 * Copyright (C) 2009 - 2010 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * 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 Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

/* $Id$ */

#include <syncml/formatter/Formatter.h>
#include <syncml/core/SessionID.h>
#include "serverexchange/MessageFactory.h"

using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_Communication;
using namespace NS_DM_Client::NS_SyncMLCommand;


#define SYNCML_HEADER			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
#define SYNCML_SYNCML_OPEN		"<SyncML>\n"
#define SYNCML_SYNCML_CLOSE		"</SyncML>"
#define SYNCML_FINAL			"<Final/>\n"
#define SYNCML_SYNC_BODY_OPEN	"<SyncBody>\n"
#define SYNCML_SYNC_BODY_CLOSE	"</SyncBody>\n"


MessageFactory::MessageFactory(ConnectionInfo &info) :
	m_commandNumber(1), m_connInfo(info), m_pCred(NULL), m_pMessageBody(new FStringBuffer()),
	m_pSyncHdr(NULL), m_pSyncMLBuilder(NULL)
{
}


MessageFactory::~MessageFactory()
{
	SAFE_DELETE(m_pCred);
	SAFE_DELETE(m_pMessageBody);
	SAFE_DELETE(m_pSyncHdr);
	SAFE_DELETE(m_pSyncMLBuilder);
}


int MessageFactory::AddCommand(NS_SyncMLCommand::SCommandPtr ptrCommand)
{
	if (!ptrCommand.get()) return 0;

	Funambol::CmdID cmdID(m_commandNumber++);
	if (!ptrCommand->Internal())
	{
		ptrCommand->Prepare();
	}

	if (!ptrCommand->Internal()) return 0;

	ptrCommand->Internal()->setCmdID(&cmdID);

	FStringBuffer *cmd_syncml = ptrCommand->ToString();
	int cmdsize = cmd_syncml->length();
	m_pMessageBody->append(cmd_syncml);
	SAFE_DELETE(cmd_syncml);

	return cmdsize;
}


int MessageFactory::AddCommand(ResultsPtr ptrResult)
{
	int cmdsize = 0;

	if (ptrResult.get())
	{
		Funambol::CmdID cmdID(m_commandNumber++);
		ptrResult->setCmdID(&cmdID);

		FStringBuffer *cmd_syncml = Funambol::Formatter::getResults(ptrResult.get());
		cmdsize = cmd_syncml->length();
		m_pMessageBody->append(cmd_syncml);
		SAFE_DELETE(cmd_syncml);
	}

	return cmdsize;
}


FStringBuffer * MessageFactory::GetMessage(bool includeFinal)
{
	createHeader();

	FStringBuffer *p_syncmlHeader = Funambol::Formatter::getSyncHdr(m_pSyncHdr);
	FStringBuffer *p_syncmlMessage = createXML(p_syncmlHeader, m_pMessageBody, includeFinal);
	SAFE_DELETE(p_syncmlHeader);

	return p_syncmlMessage;
}


int MessageFactory::MessageSizeWithoutBody(bool includeFinal)
{
	createHeader();

	FStringBuffer *p_syncmlHeader = Funambol::Formatter::getSyncHdr(m_pSyncHdr);
	int hdr_size = p_syncmlHeader->length();
	SAFE_DELETE(p_syncmlHeader);

	return hdr_size + getMsgServiceInfoSize(includeFinal);
}


const char * MessageFactory::GetMessageID()
{
	createHeader();
	return m_pSyncHdr->getMsgID();
}


void MessageFactory::ResetCommands()
{
	m_pMessageBody->reset();
}


void MessageFactory::createHeader()
{
	if (!m_pSyncMLBuilder)
	{
		prepareCredentials();
		m_pSyncMLBuilder =
			new Funambol::SyncMLBuilder(const_cast<char*>(m_connInfo.GetSessionURL()),
										const_cast<char*>(m_connInfo.devinf->getDevID()),
										Funambol::SYNCML_DM_1_2);

		m_pSyncHdr = m_pSyncMLBuilder->prepareSyncHdr(m_pCred,
													  m_connInfo.settings.MaxMsgSize,
													  m_connInfo.settings.MaxObjSize);

		m_pSyncHdr->setMsgID(m_connInfo.GetNextMessageID());

		if (m_connInfo.GetSessionID())
		{
			Funambol::SessionID sessionID(m_connInfo.GetSessionID());
			m_pSyncHdr->setSessionID(&sessionID);
		}
		else
		{
			Funambol::SessionID *psid = m_pSyncHdr->getSessionID();
			if (psid && psid->getSessionID())
			{
				int len = strlen(psid->getSessionID());
				if (len > 5)
				{
					char sid5[6] = {0};
					memcpy(sid5, psid->getSessionID() + len-5, 5);
					psid->setSessionID(sid5);
					m_connInfo.SetSessionID(sid5);
				}
			}
		}
	}
}


Funambol::StringBuffer * MessageFactory::createXML(FStringBuffer *pHeader, FStringBuffer *pMessageBody, bool addFinal)
{
	FStringBuffer *pMessage = new FStringBuffer();
	pMessage->append(SYNCML_HEADER);
	pMessage->append(SYNCML_SYNCML_OPEN);
	pMessage->append(pHeader);
	pMessage->append(SYNCML_SYNC_BODY_OPEN).append(m_pMessageBody);
	if (addFinal)
	{
		pMessage->append(SYNCML_FINAL);
	}
	pMessage->append(SYNCML_SYNC_BODY_CLOSE);
	pMessage->append(SYNCML_SYNCML_CLOSE);

	return pMessage;
}


unsigned int MessageFactory::getMsgServiceInfoSize(bool includeFinal)
{
	return	strlen(SYNCML_HEADER) +
			strlen(SYNCML_SYNCML_OPEN) +
			strlen(SYNCML_SYNC_BODY_OPEN) +
			strlen(SYNCML_SYNC_BODY_CLOSE) +
			(includeFinal ? strlen(SYNCML_FINAL) : 0) +
			strlen(SYNCML_SYNCML_CLOSE);
}


void MessageFactory::prepareCredentials()
{
	SAFE_DELETE(m_pCred); // in case of HMAC cred will be empty
	
	Funambol::AccessConfig &ac = m_connInfo.acconfig;//.accessConfigGuard.GetAccessConfig();
	if (!strcmp(AUTH_TYPE_BASIC, ac.getClientAuthType()) || !strcmp(AUTH_TYPE_MD5, ac.getClientAuthType()))
	{
		m_credentialHandler.setUsername           (ac.getUsername());
		m_credentialHandler.setPassword           (ac.getPassword());
		m_credentialHandler.setClientNonce        (ac.getClientNonce());
		m_credentialHandler.setClientAuthType     (ac.getClientAuthType());
		
		m_credentialHandler.setServerID           (ac.getServerID());
		m_credentialHandler.setServerPWD          (ac.getServerPWD());
		m_credentialHandler.setServerNonce        (ac.getServerNonce());
		m_credentialHandler.setServerAuthType     (ac.getServerAuthType());
		m_credentialHandler.setServerAuthRequired (ac.getServerAuthRequired());
		
		m_pCred = m_credentialHandler.getClientCredential();		
	}
}
