/*
 * 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/AbstractCommand.h>
#include <syncml/core/Atomic.h>
#include <syncml/core/Item.h>
#include <syncml/core/Source.h>

#include "commontypes.h"
#include "executionqueue/IExecutionQueue.h"
#include "Logger/LoggerMacroses.h"
#include "treemanager/IMOTreeManager.h"
#include "treemanager/AlertCommand.h"
#include "treemanager/MOTreeAddCommand.h"
#include "treemanager/MOTreeAtomicCommand.h"
#include "treemanager/MOTreeCopyCommand.h"
#include "treemanager/MOTreeDeleteCommand.h"
#include "treemanager/MOTreeReplaceCommand.h"
#include "treemanager/MOTreeExecCommand.h"

#include "serverexchange/IServerExchangeManager.h"
#include "serverexchange/wrappers/SCommandFactory.h"

using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_SyncMLCommand;

const char* const c_LogName = "MOTreeAtomicCommand";

MOTreeAtomicCommand::MOTreeAtomicCommand(ProfileComponentsHolder* prholder,
										 AtomicPtr &cmd,
										 const String& msgID,
										 const char* serverId) :
	ActionCommand(prholder, cmd, msgID, serverId), m_AtomicCommand(cmd)
{
}


bool MOTreeAtomicCommand::Execute()
{
	if (!m_pProfile)
	{
		GDLERROR("component holder instanse not valid");
		return false;
	}

	if (!m_AtomicCommand.get())
	{
		GDLERROR("command instanse not valid");
		return false;
	}

	Funambol::ArrayList *cmds = m_AtomicCommand.get()->getCommands();
	if (!cmds || cmds->isEmpty())
	{
		m_resCode = e_Ok;
	}
	else
	{
		m_pProfile->GetMOTreeManager()->StartTransaction();
		m_resCode = processCommands(*cmds);
	}

	InvokeResult();
	postCommandsResults(*cmds);

	return true;
}


// implementation of IResultCollector interface
// MOTreeAtomicCommand collects execution results of its inner commands
void MOTreeAtomicCommand::PutResult(Funambol::AbstractCommand * pCommand, StatusCode result)
{
	if (!pCommand) return;

	GDLDEBUG("Put Result: %x %d", pCommand, result);

	AbstractCommandPtr pOrigCommand((Funambol::AbstractCommand *)pCommand->clone());
	m_commandsResults.push_back(SCommandFactory::CreateStatus(result, pOrigCommand));
}


ActionCommand * MOTreeAtomicCommand::createProcessingCommand(Funambol::AbstractCommand &cmd)
{
	const char *name = cmd.getName();
	if (name && !strcmp(name, ADD_COMMAND_NAME))
	{
		AddPtr ptrAdd((Funambol::Add*)cmd.clone());
		return new MOTreeAddCommand(m_pProfile, ptrAdd, m_messageID, m_serverID);
	}
	else if (name && !strcmp(name, ALERT_COMMAND_NAME))
	{
		AlertPtr ptrAlert((Funambol::Alert*)cmd.clone());
		return new AlertCommand(m_pProfile, ptrAlert, m_messageID, m_serverID);
	}
	else if (name && !strcmp(name, COPY_COMMAND_NAME))
	{
		CopyPtr ptrCopy((Funambol::Copy*)cmd.clone());
		return new MOTreeCopyCommand(m_pProfile, ptrCopy, m_messageID, m_serverID);
	}
	else if (name && !strcmp(name, DELETE_COMMAND_NAME))
	{
		DeletePtr ptrDelete((Funambol::Delete*)cmd.clone());
		return new MOTreeDeleteCommand(m_pProfile, ptrDelete, m_messageID, m_serverID);
	}
	else if (name && !strcmp(name, REPLACE_COMMAND_NAME))
	{
		ReplacePtr ptrReplace((Funambol::Replace*)cmd.clone());
		return new MOTreeReplaceCommand(m_pProfile, ptrReplace, m_messageID, m_serverID);
	}
	return NULL;
}


// MOTreeAtomicCommand executes inner commands within itself, without sending them 
// to the ExecutionQueue (it is expected that MOTreeAtomicCommand is already placed into the ExecutionQueue)
StatusCode MOTreeAtomicCommand::processCommands(Funambol::ArrayList &clist)
{
	StatusCode res = e_Ok;
	Funambol::AbstractCommand* cmd = NULL;
	uint count = clist.size();
	GDLDEBUG("Atomic command has %d nested commands", count);
	for (uint i=0; i<count; ++i)
	{
		if ((cmd = dynamic_cast<Funambol::AbstractCommand*>(clist[i])))
		{
			ActionCommand *pActionCmd = createProcessingCommand(*cmd);
			GDLDEBUG("Created ActionCommand %x for %s", pActionCmd, cmd->getName());

			if (pActionCmd)
			{
				pActionCmd->SetResultsCollector(this);
				pActionCmd->Execute();
				StatusCode cmdres = pActionCmd->GetResult();
				delete pActionCmd;

				GDLDEBUG("Command %s executed with result %d", cmd->getName(), cmdres);
				if (e_Ok != cmdres)
				{
					res = e_CommandFailed;
					break;
				}
			}
			else
			{
				GDLERROR("Command not created");
				res = e_CommandFailed;
				m_pProfile->GetMOTreeManager()->Rollback();
				break;
			}
		}
		else
		{
			GDLERROR("casting to Funambol::AbstractCommand failed");
			res = e_CommandFailed;//e_AtomicFailed;
			m_pProfile->GetMOTreeManager()->Rollback();
		}
	}

	if (e_Ok == res)
		m_pProfile->GetMOTreeManager()->Commit();

	return res;
}


// MOTreeAtomicCommand collects results of it inner commands and post them by its own
// this is made to provide possibility to correct successful results of first commands
// in case of failure of following commands
void MOTreeAtomicCommand::postCommandsResults(Funambol::ArrayList &atomicCommands)
{
	SCommandsArray results;

	if (e_Ok == m_resCode)
	{
		for (uint i=0; i<m_commandsResults.size(); ++i)
			results.push_back(m_commandsResults[i]);
	}
	else
	{
		for (int i=0; i<atomicCommands.size(); ++i)
		{
			StatusCode errCode = e_CommandFailed;
			Funambol::AbstractCommand *pCmd = (Funambol::AbstractCommand *)atomicCommands[i];
			if (pCmd && pCmd->getCmdID() && pCmd->getCmdID()->getCmdID())
			{
				if (findResultForCommand(pCmd->getCmdID()->getCmdID()))
				{
					errCode = e_AtomicRollbackOK;
				}
			}

			AbstractCommandPtr pOrigCommand((Funambol::AbstractCommand *)pCmd->clone());
			SCommandPtr status_msg = SCommandFactory::CreateStatus(errCode, pOrigCommand);
			results.push_back(status_msg);
		}
	}

	if (results.size())
		m_pProfile->GetServerExchangeManager()->AddCommands(results, m_serverID);
}


NS_SyncMLCommand::SCommandPtr MOTreeAtomicCommand::findResultForCommand(const char *cmdID)
{
	for (uint i=0; i<m_commandsResults.size(); ++i)
	{
		if (!strcmp(cmdID, m_commandsResults[i].get()->GetCmdRef()))
			return m_commandsResults[i];
	}
	return NS_SyncMLCommand::SCommandPtr();
}

