/**
 * @file FormattedChainLoggable.h
 * @author Bartosz Janiak
 * 
 * @brief Contains the definition of FormattedChainLoggable class and related utility functions.
 *
 * 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 "ChainLoggable.h"
#include "TBoostFormat.h"

#include <boost/lexical_cast.hpp>

namespace Nvidia {
namespace Logging {

/**
 * @brief ChainLoggable which allows arbitrary formatting of its string representation.
 *
 * @tparam ChainType Type of the wrapped Chain.
 */
template <typename ChainType>
class FormattedChainLoggable : public ChainLoggable<ChainType>
{
private:
    /// @brief Format string.
    std::tstring m_format;

public:
    /**
     * @brief Constructor.
     *
     * @param chain The chain which should be wrapped.
     * @param format Format string conforming to the boost::format specification
              (which is compatible with printf format specification, see
              http://www.boost.org/doc/libs/1_39_0/libs/format/doc/format.html#printf_directives 
              for details).
     */
    FormattedChainLoggable(const ChainType& chain, std::tstring& format) :
        ChainLoggable(chain),
        m_format(format) {}
    
    /**
     * @brief Returns all elements of the m_wrappedChain formatted into a string using boost::format.
     *
     * The m_wrappedChain is processed in reverse, i.e. from the tail to the head.
     */
    virtual std::tstring GetStringValue() const
    {
        try {
            boost::tformat formatter(m_format);
            AppendChain(m_wrappedChain, formatter);
            return formatter.str();
        } catch ( std::exception& exception ) {
            return boost::lexical_cast<std::tstring>(exception.what());
        }
    }

private:
    template<typename HeadType, typename TailType>
    void AppendChain(const Chain<HeadType, TailType>& chain, boost::tformat& formatter) const
    {
        AppendChain(chain.GetTail(), formatter);
        formatter % chain.GetHead();
    }

    //EmptyChain specialization
    template<>
    void AppendChain(const EmptyChain&, boost::tformat&) const
    {}
};

/// @brief Convenience function wrapping the FormattedChainLoggable constructor.
template <typename ChainType>
FormattedChainLoggable<ChainType> MakeFormattedChainLoggable(const ChainType& chain, std::tstring format)
{
    return FormattedChainLoggable<ChainType>(chain, format);
}

}
}