/**
 * @file GenericLogPrinter.h
 * @author Bartosz Janiak
 * 
 * @brief Contains the definition of GenericLogPrinter abstract class.
 *
 * Copyright © 2009 NVIDIA Corporation. All rights reserved.
 *
 * NVIDIA Corporation and its licensors retain all intellectual property and proprietary
 * rights in and to this software, related documentation and any modifications thereto. Any
 * use, reproduction, disclosure or distribution of this software and related documentation
 * without express license agreement from NVIDIA Corporation is strictly prohibited.
 */

#pragma once

#include "ILogPrinter.h"
#include "LogInfo.h"
#include "CompositeLoggable.h"
#include "IGenericLogManager.h"

#include <boost/foreach.hpp>
#include <boost/any.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <vector>

namespace Nvidia {
namespace Logging {

namespace Test {
    class LoggerTest;
    class LoggerConstructorTest;
}

/**
 * @brief GenericLogPrinter abstract class.
 *
 * Objects of this class are responsible for formatting messages provided by the Logger
 * into a custom format and type, suitable for transferring into the LogManager.
 * @tparam T Type of the result of message formatting.
 */
template <typename T>
class GenericLogPrinter : public ILogPrinter
{
    friend class Nvidia::Logging::Test::LoggerTest;
    friend class Nvidia::Logging::Test::LoggerConstructorTest;
private:
    /// @brief Contains all IGenericLogManager%s connected to this GenericLogPrinter.
    std::vector<IGenericLogManager<T>*> m_LogManagers;

    /// @brief Lock for PrintMessage to prevent concurrent use of DoFormatMessage
    boost::recursive_mutex m_printLock;
    
public:
    /**
     * @brief Adds the ILogManager instance to m_LogManagers vector.
     *
     * It's not the GenericLogPrinter, but the Logger that maintains the life cycle of the
     * ILogManager instances. This is because ILogManager instances can be shared
     * between different ILogPrinters.
     * @throws std::bad_cast If this GenericLogPrinter is incompatible with the provided
     * ILogManager instance (i.e. it's not a IGenericLogManager or the template parameters
     * don't match).
     */
    void AddLogManager(ILogManager* logManager)
    {
        IGenericLogManager<T>* manager = dynamic_cast<IGenericLogManager<T>*> (logManager);
        if ( manager == NULL )
            throw std::bad_cast();
        m_LogManagers.push_back(manager);
    }

    /**
     * @brief Prints the formatted message on all registered ILogManager%s.
     *
     * Implementation first calls DoFormatMessage() to format the message, and 
     * then passes the formatted message via ManageMessage method to every
     * IGenericLogManager from m_LogManagers vector .
     */
    void PrintMessage(const LogInfo& logInfo, const CompositeLoggable& message)
    {
        boost::recursive_mutex::scoped_lock lock(m_printLock);
        T formattedMessage = DoFormatMessage(logInfo, message);

        BOOST_FOREACH(IGenericLogManager<T>* logManager, m_LogManagers)
        {
            logManager->ManageMessage(formattedMessage);
        }
    }

private:
    /**
    * @brief Format the provided message into T.
    *
    * This method has been renamed from FormatMessage due to name clash with a macro defined
    * in windows.h.
    */
    virtual T DoFormatMessage(const LogInfo& logInfo, const CompositeLoggable& message) = 0;
};

}
}