/*
 * 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/core/CmdID.h>
#include <syncml/core/Item.h>
#include <syncml/core/Results.h>
#include <syncml/formatter/Formatter.h>
#include "Errors.h"
#include "Utils.h"
#include "Logger/LoggerMacroses.h"
#include "serverexchange/LOResults.h"


using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_SyncMLCommand;

static const char * c_LogName = "LOResults";

LOResults::LOResults(ResultsPtr results, unsigned int resultMaxSize, unsigned int maxObjSize) :
    m_firstChunk(true), m_curritemindex(-1),
    m_maxsize(resultMaxSize),
    m_maxobjsize(maxObjSize),
    m_buffersize(0), m_bufferpos(0), m_buffer(NULL), m_rescmd(results), m_items(NULL)
{
    m_items = results->getItems();
    GDLDEBUG("m_items  \t%x, size %d", m_items, m_items ? m_items->size() : 0);

    if (m_items && m_items->size())
    {
        GDLDEBUG("Results HAS DATA");
        Funambol::Item *item = (Funambol::Item *)m_items->get(0);
        GDLDEBUG("Item is %x", item);
        if (item)
            if (item->getData() && item->getData()->getData())
            {
                m_buffer = item->getData()->getData();
                m_buffersize = strlen(m_buffer);
                m_bufferpos = 0;
                m_curritemindex = 0;
                GDLDEBUG("Buffer size %d", m_buffersize);
            }
    }
}


LOResults::~LOResults()
{
    m_buffer = NULL;
    m_bufferpos = 0;
    m_curritemindex = 0;
}


bool LOResults::HasAllChunksCreated()
{
    return m_items ? (m_curritemindex < 0 || m_curritemindex >= m_items->size()) : true;
}


unsigned int LOResults::GetNextChunkEmptySize()
{
    Funambol::Results *pResults = NULL;
    pResults = new Funambol::Results(NULL,
                                     m_rescmd->getMsgRef(),
                                     m_rescmd->getCmdRef(),
                                     NULL,
                                     m_rescmd->getTargetRef(),
                                     m_rescmd->getSourceRef(),
                                     NULL);
    if(pResults == (Funambol::Results*)NULL)
    {
        GDLWARN("pResults is NULL");
    }

    Funambol::ArrayList arrItems;


    Funambol::Item *item = currentItem();
    Funambol::Meta *pMeta = getMetaFrom(*item);
    Funambol::Item newitem(item->getTarget(),
                            item->getSource(),
                            pMeta,
                            NULL,
                            !(m_buffersize == m_bufferpos));
    arrItems.add(newitem);

    Funambol::CmdID cmdID("1");
    pResults->setCmdID(&cmdID);
    pResults->setItems(&arrItems);

    if (pMeta != item->getMeta()) // check if pointer created with clone
    {
        SAFE_DELETE(pMeta);
    }

    Funambol::StringBuffer *results_syncml = Funambol::Formatter::getResults(pResults);

    int result = results_syncml ? results_syncml->length() : 0;

    SAFE_DELETE(pResults);
    SAFE_DELETE(results_syncml);

    return result;
}


ResultsPtr LOResults::GetNextChunk(const char * cmdid, int maxSize)
{
    Funambol::Results *pResults = NULL;

    do
    {
        if (HasAllChunksCreated() || !m_rescmd.get())
        {
            GDLDEBUG("Break");
            break;
        }

        Funambol::CmdID cmdId(cmdid);
        pResults = new Funambol::Results(&cmdId,
                                         m_rescmd->getMsgRef(),
                                         m_rescmd->getCmdRef(),
                                         NULL,
                                         m_rescmd->getTargetRef(),
                                         m_rescmd->getSourceRef(),
                                         NULL);
        if(pResults == (Funambol::Results*)NULL)
        {
            GDLWARN("pResults is NULL");
        }

        Funambol::Item *item = currentItem();
        GDLDEBUG("Get item -\tindex=%d item=%x count=%d", m_curritemindex, item, m_items->size());
        if (!item) break;

        pResults->setCmdID(&cmdId);

        Funambol::Meta *pMeta = getMetaFrom(*item);
        if (m_firstChunk)
            m_firstChunk = false;
        else if (pMeta)
            pMeta->setSize(0); // Size section should be present only in the Meta of the first chunk

        Funambol::ArrayList arrItems;
        Funambol::StringBuffer *resstr = Funambol::Formatter::getResults(pResults);
        int datasize = m_maxsize - resstr->length();
        if (datasize > __MIN(maxSize, m_maxobjsize))
            datasize = __MIN(maxSize, m_maxobjsize);
        SAFE_DELETE(resstr);

        // calculate number of bytes to copy into the Data section
        int   bytes_to_copy = ((m_buffersize - m_bufferpos)>datasize ? datasize : (m_buffersize - m_bufferpos));
        char *chunkdata     = new char[bytes_to_copy+1];
        if(chunkdata == (char*)NULL)
        {
            GDLWARN("new chunkdata");
        }
        memset(chunkdata, '\0', bytes_to_copy+1);

        // read data to buffer
        GDLDEBUG("bytes_to_copy into the Data: %d", bytes_to_copy);
        memcpy(chunkdata, m_buffer+m_bufferpos, bytes_to_copy);
        m_bufferpos += bytes_to_copy;

        Funambol::ComplexData cdata(chunkdata);
        SAFE_DELETE_ARR(chunkdata);
        
        Funambol::Item nitem(item->getTarget(),
                             item->getSource(),
                             pMeta,
                             &cdata,
                             !(m_buffersize == m_bufferpos));

        if (pMeta != item->getMeta()) // pointer created with clone
        {
            SAFE_DELETE(pMeta);
        }

        arrItems.clear();
        arrItems.add(nitem);
        pResults->setItems(&arrItems);

        if (m_buffersize == m_bufferpos)
        {
            // switch to next item
            m_curritemindex++;
            if (m_curritemindex < m_items->size())
            {
                Funambol::Item *item = currentItem();
                if (!item || !item->getData()) break;

                m_buffer = item->getData()->getData();
                m_buffersize = m_buffer ? strlen(m_buffer) : 0;
                m_bufferpos = 0;
            }
        }

    } while (0);

    return ResultsPtr(pResults);
}


Funambol::Item * LOResults::currentItem()
{
    if (m_curritemindex >= 0 && m_curritemindex < m_items->size())
        return static_cast<Funambol::Item*>(m_items->get(m_curritemindex));
    else
        return NULL;
}


Funambol::Meta * LOResults::getMetaFrom(Funambol::Item &item)
{
    Funambol::Meta *pMeta = item.getMeta();

    if (m_firstChunk)
    {
        if (item.getMeta())
            pMeta = item.getMeta()->clone();

        if (pMeta)
        {
            pMeta->setSize(m_buffersize);
        }
        else
        {
            pMeta = item.getMeta();
        }
    }
    else if (pMeta)
        pMeta->setSize(0);

    return pMeta;
}

