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

#include "commontypes.h"
#include "executionqueue/IExecutionQueue.h"
#include "Logger/LoggerMacroses.h"

#include "treemanager/IMOTreeManager.h"
#include "treemanager/ActionCommand.h"
#include "treemanager/MOTreeAddCommand.h"
#include "treemanager/MOTreeCopyCommand.h"
#include "treemanager/MOTreeDeleteCommand.h"
#include "treemanager/MOTreeGetCommand.h"
#include "treemanager/MOTreeReplaceCommand.h"
#include "treemanager/MOTreeExecCommand.h"
#include "treemanager/MOTreeSequenceCommand.h"
#include "serverexchange/wrappers/SCommandFactory.h"
#include "serverexchange/IServerExchangeManager.h"


using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_SyncMLCommand;

const char* const c_LogName = "MOTreeSequenceCommand";

MOTreeSequenceCommand::MOTreeSequenceCommand(ProfileComponentsHolder* prholder,
                                             SequencePtr cmd,
                                             const String& msgID,
                                             const char* serverId) :
    ActionCommand(prholder, cmd, msgID, serverId), m_SequenceCommand(cmd), m_commandItems(0)
{
    if(cmd.get() == NULL)
    {
        GDLWARN("cmd is NULL");
    }
}

MOTreeSequenceCommand::~MOTreeSequenceCommand()
{
    if (m_commandItems)
    {
        delete m_commandItems;
    }
}


bool MOTreeSequenceCommand::Execute()
{
    if (!m_pProfile || !m_SequenceCommand.get())
    {
        GDLERROR("component holder or command instanse not valid");
        return false;
    }

    Funambol::Sequence *seq = m_SequenceCommand.get();
    Funambol::ArrayList *cmds = seq->getCommands();
    if (!cmds || cmds->isEmpty())
    {
        m_resCode = e_Ok;
    }
    else
    {
        if (!notifyProvisionUpdate(e_Sequence, e_sessionStart, e_Ok))
        {
            GDLWARN("failed to notify provision update at start");
        }

        m_resCode = processCommands(*cmds);

        if (!notifyProvisionUpdate(e_Sequence, e_sessionEnd, m_resCode))
        {
            GDLWARN("failed to notify provision update at end");
        }
    }

    InvokeResult();

    return true;
}


ActionCommand * MOTreeSequenceCommand::createProcessingCommand(Funambol::AbstractCommand &cmd)
{
    const char *name = cmd.getName();

    if (name && !strcmp(name, ADD_COMMAND_NAME))
    {
        AddPtr ptrAdd((Funambol::Add*)cmd.clone());
        if(ptrAdd.get() == NULL)
        {
            GDLWARN("ptrAdd is NULL");
        }

        return new(std::nothrow) MOTreeAddCommand(m_pProfile, ptrAdd, m_messageID, m_serverID);
    }
    else if (name && !strcmp(name, COPY_COMMAND_NAME))
    {
        CopyPtr ptrCopy((Funambol::Copy*)cmd.clone());
        if(ptrCopy.get() == NULL)
        {
            GDLWARN("ptrCopy is NULL");
        }

        return new(std::nothrow) MOTreeCopyCommand(m_pProfile, ptrCopy, m_messageID, m_serverID);
    }
    else if (name && !strcmp(name, DELETE_COMMAND_NAME))
    {
        DeletePtr ptrDelete((Funambol::Delete*)cmd.clone());
        if(ptrDelete.get() == NULL)
        {
            GDLWARN("ptrDelete is NULL");
        }

        return new(std::nothrow) MOTreeDeleteCommand(m_pProfile, ptrDelete, m_messageID, m_serverID);
    }
    else if (name && !strcmp(name, GET_COMMAND_NAME))
    {
        GetPtr ptrGet((Funambol::Get*)cmd.clone());
        if(ptrGet.get() == NULL)
        {
            GDLWARN("ptrGet is NULL");
        }

        return new(std::nothrow) MOTreeGetCommand(m_pProfile, ptrGet, m_messageID, m_serverID);
    }
    else if (name && !strcmp(name, REPLACE_COMMAND_NAME))
    {
        ReplacePtr ptrReplace((Funambol::Replace*)cmd.clone());
        if(ptrReplace.get() == NULL)
        {
            GDLWARN("ptrReplace is NULL");
        }

        return new(std::nothrow) MOTreeReplaceCommand(m_pProfile, ptrReplace, m_messageID, m_serverID);
    }
    else if (name && !strcmp(name, EXEC_COMMAND_NAME))
    {
        ExecPtr ptrExec((Funambol::Exec*)cmd.clone());
        if(ptrExec.get() == NULL)
        {
            GDLWARN("ptrExec is NULL");
        }

        return new(std::nothrow) MOTreeExecCommand(m_pProfile, ptrExec, m_messageID, m_serverID);
    }
    return NULL;
}


// MOTreeSequenceCommand executes inner commands within itself, without sending them
// to the ExecutionQueue (it is expected that MOTreeSequenceCommand is already placed into the ExecutionQueue)
StatusCode MOTreeSequenceCommand::processCommands(Funambol::ArrayList &clist)
{
    StatusCode res = e_Ok;
    Funambol::AbstractCommand* cmd = NULL;

    int count = clist.size();
    GDLDEBUG("Sequence has %d internal commands", count);
    for (int i=0; i<count; ++i)
    {
        if ((cmd = static_cast<Funambol::AbstractCommand*>(clist[i])))
        {
            ActionCommand *treecmd = createProcessingCommand(*cmd);

            if (treecmd)
            {
                GDLDEBUG("Received command %x", treecmd);
                if (m_pCommandsSink)
                    treecmd->SetCommandsSink(*m_pCommandsSink);

                treecmd->Execute();
                delete treecmd;
            }
            else
            {
                GDLERROR("Failed to create handling command");
            }
        }
        else
        {
            GDLERROR("casting to Funambol::AbstractCommand failed, command index = %d", i);
        }
    }

    GDLDEBUG("result StatusCode = %d", res);
    return res;
}

Funambol::ArrayList* MOTreeSequenceCommand::getListOfItems()
{
    if (m_commandItems == 0)
    {
        Funambol::Sequence* seq = m_SequenceCommand.get();
        if (seq)
        {
            Funambol::ArrayList* cmds = seq->getCommands();
            if (cmds)
            {
                Funambol::AbstractCommand* cmd = NULL;
                Funambol::ArrayList* conmmand_items;
                ActionCommand *tree_cmd = 0;
                size_t commands_count = cmds->size();

                bool res = true;

                for (size_t i = 0; i < commands_count, res; ++i)
                {
                    if ((cmd = static_cast<Funambol::AbstractCommand*>((*cmds)[i])))
                    {
                        if (tree_cmd = createProcessingCommand(*cmd))
                        {
                            if (conmmand_items = tree_cmd->getListOfItems())
                            {
                                if (m_commandItems == 0)
                                {
                                    m_commandItems = new Funambol::ArrayList;
                                }
                                if (m_commandItems)
                                {
                                    if (m_commandItems->add(conmmand_items) == -1)
                                    {
                                        GDLERROR("Failed to add items to items list");
                                        res = false;
                                    }
                                }
                            }
                            else
                            {
                                GDLERROR("Failed to get list of items from command");
                                res = false;
                            }

                            delete tree_cmd;
                        }
                        else
                        {
                            GDLERROR("Failed to create processing command");
                            res = false;
                        }
                    }
                    else
                    {
                        GDLERROR("Failed to cast to Funambol::AbstractCommand");
                        res = false;
                    }
                }

                if (!res)
                {
                    delete m_commandItems;
                    m_commandItems = 0;
                }
            }
            else
            {
                GDLERROR("Failed to get list of commands from sequence command");
            }
        }
        else
        {
            GDLERROR("Failed to get sequence command");
        }
    }

    return m_commandItems;
}
