/*
 * 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 "Errors.h"
#include "serverexchange/LOCollector.h"
#include "Logger/LoggerMacroses.h"

using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_Communication;

static const char * c_LogName = "LOCollector";

LOCollector::LOCollector() :
    m_expected_size(0), m_targeturi(NULL), m_buffer(NULL), m_buffersize(0), m_bufferpos(0),
    m_pMessenger((Messenger*)NULL)
{
}


LOCollector::~LOCollector()
{
    SAFE_DELETE_ARR(m_targeturi);
    SAFE_DELETE_ARR(m_buffer);
    if (m_pMessenger != (Messenger*)NULL)
    {
        m_pMessenger->Release();
        m_pMessenger = (Messenger*)NULL;
    }
}


// returned value < 0 indicate that command should be furthely processed with common routines
int LOCollector::AddChunk(Funambol::ItemizedCommand &cmd)
{
    GLDEBUG("ResponseProcessor::LOCollector", "Add chunk");

    Funambol::ArrayList *pItems = cmd.getItems();
    Funambol::Meta      *pMeta  = NULL;
    if (!pItems || !(pItems->size() == 1))
    {
        GDLERROR("Commmand with LO should have only ne item");
        return e_CommandFailed;
    }

    Funambol::Item *pItem = (Funambol::Item *)pItems->get(0);
    if (pItem && pItem->getMeta())
    {
        pMeta = pItem->getMeta();
    }
    else
    {
        GDLERROR("Meta section in item of LO chunk is empty");
        return e_CommandFailed;
    }

    if (!m_command.get())
    {
        // first chunk added
        GDLDEBUG("process first chunk");
        if (!pItem->getMoreData())
        {
            GDLDEBUG("item does not contain item with MoreData tag");
            return -1;
        }

        // check TargetURI
        if (pItem->getTarget() && pItem->getTarget()->getLocURI() &&
            strlen(pItem->getTarget()->getLocURI()))
        {
            SAFE_DELETE_ARR(m_targeturi);
            m_targeturi = Funambol::stringdup(pItem->getTarget()->getLocURI());
        }
        else
        {
            GDLERROR("Target uri of LO is not specified");
            return e_CommandFailed;
        }

        m_expected_size = pMeta->getSize();
        GDLDEBUG("   LargeObject size is %d", m_expected_size);

        if (!m_expected_size)
        {
            // first chunk does not contain LO size
            GDLDEBUG("   command failed - LO size is 0");
            return e_CommandFailed;
        }
        Funambol::ItemizedCommand *newcmd = (Funambol::ItemizedCommand *)cmd.clone();
        m_command.reset(newcmd);
        int extrasize = (0 == m_expected_size%4) ? 4 : (4-m_expected_size%4); // 4 bytes bounds aligner, at least 1 byte for '\0'
        m_buffersize = m_expected_size + extrasize;
        GDLDEBUG("   allocate buffer for LO with size %d [= %d + %d]", m_buffersize, m_expected_size, extrasize);
        m_buffer = new char[m_buffersize];
        if(m_buffer == NULL)
        {
            GDLERROR("new char");
            return -1;
        }
        memset(m_buffer, 0, m_buffersize);

        m_pMessenger = CreateMessenger();
        if(m_pMessenger == (Messenger*)NULL)
        {
            GDLERROR("Failed to CreateMessenger");
            return -1;
        }
    }
    else
    {
        GDLWARN("m_command is NULL");
    }

    if (strcmp(m_command->getName(), cmd.getName()))
    {
        GDLERROR("new chunk has different name than initial LO chunk");
        return e_CommandFailed;
    }

    if (!pItem->getTarget() || !pItem->getTarget()->getLocURI())
    {
        GDLERROR("Target uri of LO chunk is not specified");
        return e_CommandFailed;
    }

    if (strcmp(m_targeturi, pItem->getTarget()->getLocURI()))
    {
        // first chunk received, we know the name and target
        // and received command with same name and different target
        GDLERROR("new chunk has different Traget URI than initial LO chunk");
        return e_CommandFailed;
    }

    if (pItem->getData())
    {
        const char *itemdata = pItem->getData()->getData();
        int itemdatalen = strlen(itemdata); // do not copy \0
        GDLDEBUG("   next chunk size is %d", itemdatalen);

        if (itemdatalen > m_expected_size-m_bufferpos)
        {
            GDLDEBUG("itemdatalen > m_expected_size-m_bufferpos, [%d > %d - %d]", itemdatalen, m_expected_size, m_bufferpos);
            return e_SizeMismatch; // new chunk size exceed declared size of whole bin obj
        }

        memcpy(m_buffer + m_bufferpos, itemdata, itemdatalen);
        m_bufferpos += itemdatalen;

        if (!notifyProgress())
        {
            GDLDEBUG("  notifyProgress cancelled the LO transmission");
            return e_NotExecuted;
        }
    }
    return e_ChunkedItemAcceptedAndBuffered;
}


ItemizedCommandPtr LOCollector::GetResultCmd()
{
    // copy data from buffer to ComplaexData instance
    if (m_command.get())
    {
        Funambol::ComplexData data(m_buffer);

        Funambol::ArrayList *items = m_command->getItems();
        Funambol::Item *item = (Funambol::Item *)items->get(0);
        item->setData(&data);
    }
    else
    {
        GDLWARN("m_command is NULL");
    }

    return m_command;
}


bool LOCollector::IsCompleted()
{
    return m_bufferpos == m_expected_size;
}


const char * LOCollector::GetLOCmdName()
{
    if (m_command.get())
    {
        return m_command->getName();
    }
    else
    {
        GDLWARN("m_command is NULL");
        return "";
    }
}


const char * LOCollector::GetLOTargetURI()
{
    return m_targeturi;
}


// return false if download should be stopped
// return true if download should be continued
bool LOCollector::notifyProgress()
{
    UIOptionalParameters parameters;
    if (m_pMessenger != (Messenger*)NULL)
        return (e_Ok == m_pMessenger->ProgressNotification("", parameters, 0));
    else
        return true;
}
