/*
* 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 "executionqueue/CommandsQueue.h"
#include "executionqueue/ICommand.h"

#include "lock.h"
#include "TimeLog.h"

#include "executionqueue/CommandInvokeThread.h"

using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_ExecutionQueue;
using namespace NS_DM_Client::NS_Common;

CommandsQueue::CommandsQueue(NS_Logging::Logger& log)
: m_CmdInvokeThread(0),
m_logger(log)
{
LOG_DEBUG_(m_logger,"Enter");
LOG_DEBUG_(m_logger,"Leave");
}

CommandsQueue::~CommandsQueue(void)
{
LOG_DEBUG_(m_logger,"Enter");

    if (m_CmdInvokeThread != (CommandInvokeThread*)NULL)
    {
        if (m_CmdInvokeThread->running())
        {
            LOG_WARNING_(m_logger, "CmdInvokeThread is still running. ");
            m_CmdInvokeThread->softTerminate();
        }
        delete m_CmdInvokeThread;
        m_CmdInvokeThread = (CommandInvokeThread*)NULL;
    }

    discardWaitingCommands();

LOG_DEBUG_(m_logger,"Leave");
}


bool CommandsQueue::Init()
{
LOG_DEBUG_(m_logger,"Enter");

    m_CmdInvokeThread = new(std::nothrow) CommandInvokeThread(m_resEvent, *this, m_logger);
    if (m_CmdInvokeThread != (CommandInvokeThread*)NULL)
    {
        m_CmdInvokeThread->start();
        LOG_DEBUG_(m_logger, "CmdInvokeThread (%p) of CmdsQ (%p) is running=%d. ", m_CmdInvokeThread, this, m_CmdInvokeThread->running());
    }

LOG_DEBUG_(m_logger,"Leave");

    return (m_CmdInvokeThread != (CommandInvokeThread*)NULL) && m_CmdInvokeThread->running();
}


bool CommandsQueue::Add(ICommand& cmd)
{
LOG_DEBUG_(m_logger,"Enter");

    bool brc = false;
    if ((m_CmdInvokeThread != (CommandInvokeThread*)NULL) && m_CmdInvokeThread->running())
    {
        Lock lock(m_CmdsLocker);

        bool noCommands = m_Cmds.empty();
        m_Cmds.push_back(&cmd);

        LOG_DEBUG_(m_logger, "CmdsQ (%p) is empty=%d. ", this, noCommands);

        if (noCommands)
        {
            LOG_DEBUG_(m_logger, "issue NEWCOMMAND signal. ");
            m_resEvent.signal(e_NewCommand);
        }

        brc = true;
    }
    else
    {
        LOG_WARNING_(m_logger, "CmdInvokeThread is not running. ");
    }

LOG_DEBUG_(m_logger,"Leave");

    return brc;
}


bool CommandsQueue::StopCommandExecution()
{
LOG_DEBUG_(m_logger,"Enter");

    m_CmdInvokeThread->softTerminate();

    const unsigned long waitThreadStopMilisec = 500;
    TimeLog timeLog;

    if (timeLog.Snap() != 0)
    {
        LOG_DEBUG_(m_logger, "clock_gettime failed. ");
    }

    bool waitResult = m_CmdInvokeThread->wait(waitThreadStopMilisec);
    if (timeLog.Snap() != 0)
    {
        LOG_DEBUG_(m_logger, "clock_gettime failed. ");
    }
    else
    {
        LOG_DEBUG_(m_logger, "Thread ending wait time: %d nsec. ", timeLog.NanoSeconds());
    }

    if (!waitResult)
    {
        LOG_WARNING_(m_logger, "CmdInvokeThread did not stop. ");
    }
    else
    {
        discardWaitingCommands();
    }

LOG_DEBUG_(m_logger,"Leave");

    return m_CmdInvokeThread->finished();
}


ICommand* CommandsQueue::getCommand()
{
LOG_DEBUG_(m_logger,"Enter");

    Lock lock(m_CmdsLocker);
    ICommand* cmd = (ICommand*)NULL;
    if (!m_Cmds.empty())
    {
        cmd = m_Cmds.front();
    }
    else
    {
        LOG_INFO_(m_logger, "CmdsQ is empty - can not get a command. ");
    }

LOG_DEBUG_(m_logger,"Leave");

    return cmd;
}


ICommand* CommandsQueue::popCommand()
{
LOG_DEBUG_(m_logger,"Enter");

    Lock lock(m_CmdsLocker);
    ICommand* cmd = (ICommand*)NULL;
    if (!m_Cmds.empty())
    {
        cmd = m_Cmds.front();
        m_Cmds.pop_front();
    }
    else
    {
        LOG_INFO_(m_logger, "CmdsQ is empty - can not pop a command. ");
    }

LOG_DEBUG_(m_logger,"Leave");
    return cmd;
}


void CommandsQueue::discardWaitingCommands()
{
LOG_DEBUG_(m_logger,"Enter");

    Lock lock(m_CmdsLocker);
    for (Commands::iterator i = m_Cmds.begin(), end = m_Cmds.end(); i != end; ++i)
    {
        LOG_DEBUG_(m_logger, "Command %p. ", *i);
        delete *i;
    }

    m_Cmds.clear();

LOG_DEBUG_(m_logger,"Leave");
}


