/*
 * 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 "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(NULL)
{
}


LOCollector::~LOCollector()
{
	SAFE_DELETE_ARR(m_targeturi);
	SAFE_DELETE_ARR(m_buffer);
	SAFE_DELETE(m_pMessenger);
}


// 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];
		memset(m_buffer, 0, m_buffersize);

		m_pMessenger = CreateMessenger();
	}

	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);
	}
	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
	{
		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)
		return (e_Ok == m_pMessenger->ProgressNotification("", parameters, 0));
	else
		return true;
}
