/*
 * 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 "Logger/Logger.h"
#include "MOTreeManagerTransaction.h"
#include "treemanager/MOTreeManagerListenerCollection.h"
#include "treemanager/IMOTreeManager.h"
#include "treemanager/MOTreeManager.h"
#include "DeviceAdapter/DeviceUpdater.h"
#include "DeviceAdapter/IDeviceAdapter.h"

#include "daemon/ProfileComponentsHolder.h"

namespace NS_DM_Client
{

const char* const c_MOTreeManagerTransactionLog = "MOTreeManagerTransaction";

//-------------------------------------------------------------------------------------------
MOTreeManagerTransaction::MOTreeManagerTransaction()
    : m_deviceUpdater(0), m_manager(0), m_listeners(0), m_inTransaction(false)
{
}
//-------------------------------------------------------------------------------------------
MOTreeManagerTransaction::~MOTreeManagerTransaction()
{
}
//-------------------------------------------------------------------------------------------
bool MOTreeManagerTransaction::Init(MOTreeManagerListenerCollection* listeners, IDeviceAdapter* deviceAdapter,
									NS_DataStorage::IDataStorage* dataStorage, MOTreeManager* manager)
{
    m_listeners = listeners;
    if (!m_listeners)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "m_listeners is inited with NULL");
        return false;
    }
    m_data.m_dataStorage = dataStorage;
    m_manager = manager;

    if (deviceAdapter)
    {
		if (!m_deviceUpdater)
		{
	        m_deviceUpdater = new DeviceUpdater(*deviceAdapter);
			if (!m_deviceUpdater)
		    {
				LOG_ERROR_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "creation of device updater failed");
				return false;
			}
		}
        if (!m_deviceUpdater->Synchronize(*m_manager))
        {
            LOG_WARNING_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "synchronization with device failed");
        }
    }
	else
	{
		LOG_ERROR_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "failed to get IDeviceAdapter instance using profile component holder");
		return false;
	}
    return true;
}
//-------------------------------------------------------------------------------------------
bool MOTreeManagerTransaction::Release()
{
    if (m_deviceUpdater)
    {
        delete m_deviceUpdater;
    }
    m_deviceUpdater = 0; // so component is no Init
    delete this;
	return true;
}
//-------------------------------------------------------------------------------------------
bool MOTreeManagerTransaction::StartTransaction()
{
    if (m_inTransaction)
    {
        return false;
    }
    m_inTransaction = true;
    return true;
}
//-------------------------------------------------------------------------------------------
bool MOTreeManagerTransaction::Commit()
{
    bool res = executeDeviceUpdater();
    if (!res)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "device updater failed. Call rollbackFromCommit");
        if (!rollbackFromCommit())
        {
            LOG_ERROR_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "rollback from commit failed");
        }
    }
    else
    {
        callListeners();
    }

    m_data.clearData();
    m_inTransaction = false;
    return res;
}
//-------------------------------------------------------------------------------------------
bool MOTreeManagerTransaction::Rollback(bool delete_backup)
{
    bool res = true;

    TransactionDataElement* data = m_data.getFirstTransactionElement();

    if (!data)
    {
        LOG_INFO_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "no data was changed during transaction. No Need rollback");
    }

    while (data && res)
    {
        switch (data->m_type)
        {
        case e_transactionAdd:
        {
            res = res && m_data.rollbackAdd(data);
            break;
        }
        case e_transactionDelete:
        {
            res = res && m_data.rollbackDelete(data);
            break;
        }
        case e_transactionReplace:
        {
            res = res && m_data.rollbackReplace(data);
            break;
        }
        case e_transactionReplaceProperty:
        {
            res = res && m_data.rollbackReplaceProperty(data);
            break;
        }
        case e_transactionCopy:
        {
            res = res && m_data.rollbackCopy(data);
            break;
        }
        };
        data = m_data.getNextTransactionElement();
    }

    if (delete_backup)
    {
        m_data.clearData();
    }
    m_inTransaction = false;

	return res;
}
//-------------------------------------------------------------------------------------------

MOTreeManagerTransactionData& MOTreeManagerTransaction::Data()
{
    return m_data;
}

//-------------------------------------------------------------------------------------------

bool MOTreeManagerTransaction::rollbackFromCommit()
{
    bool res = Rollback(false);
    res = res && executeDeviceUpdater();

    m_data.clearData();
    return res;
}
//-------------------------------------------------------------------------------------------
void MOTreeManagerTransaction::SetDeviceUpdater(IDeviceUpdater* updater)
{
    if (m_deviceUpdater)
    {
        delete m_deviceUpdater;
        m_deviceUpdater = 0;
    }

    m_deviceUpdater = updater;
}
//-------------------------------------------------------------------------------------------
void MOTreeManagerTransaction::callListeners()
{   
	TransactionDataElement* data = m_data.getFirstTransactionElement();

    try
    {
        while (data)
        {
            if (data->m_type == e_transactionAdd)
            {
                m_listeners->ExecuteOnAdd(data->m_after.m_name);
            }
            else if (data->m_type == e_transactionReplace)
            {
                m_listeners->ExecuteOnReplace(data->m_after.m_name);
            }

            data = m_data.getNextTransactionElement();
        }
    }
    catch(...)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "during calling tree lesteners exception was thrown");
    }

}
//-------------------------------------------------------------------------------------------
bool MOTreeManagerTransaction::executeDeviceUpdater()
{
    bool res = true;

    TransactionDataElement* data = m_data.getFirstTransactionElement();
    String uri;
    while (data && res)
    {
        if ((data->m_type == e_transactionAdd) || (data->m_type == e_transactionReplace))
        {
            uri = (data->m_type == e_transactionAdd) ? data->m_after.m_name : data->m_before.m_name;
            if (data->m_needSerialize)
            {
                LOG_INFO_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s%s", "call device updater on next URI:", uri.c_str());
                res = m_deviceUpdater->Execute(uri, *m_manager);
            }
            else
            {
                LOG_INFO_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "call device updater on next URI: %s. No need update because needSerialize not set", uri.c_str());
            }
        }
        data = m_data.getNextTransactionElement();
    }

    return res;
}
//-------------------------------------------------------------------------------------------
}
