/*
* 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 "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(std::nothrow) DeviceUpdater(*deviceAdapter);
            if (m_deviceUpdater == (DeviceUpdater*)NULL)
            {
                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();

#if !defined(PLATFORM_ANDROID)
    try
    {
#endif
        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();
        }
#if !defined(PLATFORM_ANDROID)
    }
    catch(...)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MOTreeManagerTransactionLog), "%s", "during calling tree lesteners exception was thrown");
    }
#endif
}
//-------------------------------------------------------------------------------------------
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;
}
//-------------------------------------------------------------------------------------------
}
