/*
 * 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/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);
	Funambol::ArrayList arrItems;


	Funambol::Item *item = (Funambol::Item*) m_items->get(m_curritemindex);
	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);

		Funambol::Item *item = (Funambol::Item*) m_items->get(m_curritemindex);
		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
			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];

		// 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;
		chunkdata[bytes_to_copy] = 0;

		Funambol::ComplexData cdata(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 = (Funambol::Item*) m_items->get(m_curritemindex);
				if (!item || item->getData()) break;

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

		SAFE_DELETE_ARR(chunkdata);
	} while (0);

	return ResultsPtr(pResults);
}


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
		pMeta->setSize(0);

	return pMeta;
}
