/*
 * Copyright 2009 Funambol, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */

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

const char* const c_LogName = "MessageFactory";

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)
{
    if(m_pMessageBody == NULL) GDLWARN("m_pMessageBody is 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() == NULL)
    {
        GDLWARN("ptrCommand is NULL");
        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() != NULL)
    {
        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);
    }
    else
    {
        GDLWARN("ptrResult is NULL");
    }

    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);
}


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);
        if(m_pSyncMLBuilder == (Funambol::SyncMLBuilder*)NULL)
        {
            GDLWARN("new Funambol::SyncMLBuilder");
        }

        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];
                    memset(sid5, '\0', 6);
                    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();
    if(pMessage == NULL) GDLWARN("new FStringBuffer");

    pMessage->append(SYNCML_HEADER);
    pMessage->append(SYNCML_SYNCML_OPEN);
    pMessage->append(pHeader);
    pMessage->append(SYNCML_SYNC_BODY_OPEN).append(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);
    Funambol::AccessConfig &ac = m_connInfo.acconfig;

    // m_pCred is created for BASIC and MD5 authentication schemes
    // in case of HMAC, cred is not required
    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();
    }
}
