/**
 * @file Chain.h
 * @author Bartosz Janiak
 * 
 * @brief Contains the definition of Chain 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 "Loggable.h"

#include <vector>

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_arithmetic.hpp>

namespace Nvidia {
namespace Logging {

/// @brief Empty structure. Used as a guardian element at the end of the Chain.
struct NullType {};

/**
 * @brief Expandable statically-typed container for values and references.
 * 
 * Serves as a container for values that user wants to display in a log message.
 * After being wrapped in a ChainLoggable or FormattedChainLoggable it can be
 * passed to the logging pipeline via ScopeLogger::Log().
 *
 * Chain works like a list from functional programming languages - each instance
 * contains the first value on the m_head attribute, and the rest of the chain -
 * which can be empty - on the m_tail attribute.
 * 
 * @tparam HeadType Type of the head. Can be a reference or a pointer.
 * @tparam TailType Type of the tail. Has to be a direct type, as m_tail is always stored via reference.
 */
template<typename HeadType, typename TailType>
class Chain
{
    template <typename TailType2, typename HeadType2>
    friend class Chain;

private:
    /// @brief First value of the chain.
    HeadType m_head;
    /// @brief Rest of the chain.
    const TailType& m_tail;

public:
    /// @brief Constructor. Sets the head and the tail of the new chain.
    Chain(HeadType head, const TailType& tail): m_head(head), m_tail(tail) {}

    /// @brief Copy constructor. Sets the head and the tail of the new chain.
    Chain(const Chain<HeadType, TailType>& aChain)
    {
        m_head = aChain.Head;
        m_tail = aChain.Tail;
    }

    /// @brief Copy assignment operator.
    Chain<HeadType, TailType>& operator=(const Chain<HeadType, TailType>& aChain)
    {
        m_head = aChain.Head;
        m_tail = aChain.Tail;
        return *this;
    }

    /// @brief Returns the m_head attribute.
    HeadType GetHead() const
    {
        return m_head;
    }

    /// @brief Returns the m_tail attribute.
    const TailType& GetTail() const
    {
        return m_tail;
    }

    /**
     * @brief Returns the new Chain with head argument as a new m_head and the original Chain as a m_tail.
     *  
     * This overloaded version of the operator+ is used to add arithmetic values to the chain. Copy of the
     * head argument will be stored in the chain.
     */
    template<typename T>
    typename boost::enable_if_c<boost::is_arithmetic<T>::value,
    Chain<T, Chain<HeadType, TailType> > >::type
    operator+(T head)
    {
        return Chain<T, Chain<HeadType, TailType> >(head, *this);
    }

    /**
     * @brief Returns the new Chain with head argument as a new m_head and the original Chain as a m_tail.
     *  
     * This overloaded version of the operator+ is used to add non-arithmetic values (strings, containers, loggables etc.)
     * to the chain. Reference to the head argument will be stored in the chain.
     */
    template<typename T>
    typename boost::disable_if_c<boost::is_arithmetic<T>::value,
    Chain<const T&, Chain<HeadType, TailType> > >::type
    operator+(const T& head)
    {
        return Chain<const T&, Chain<HeadType, TailType> >(head, *this);
    }
};

/// @brief typedef for an empty chain.
typedef Chain<NullType, NullType> EmptyChain;

/// @brief Function which creates an empty chain.
inline EmptyChain MakeEmptyChain()
{
    return Chain<NullType, NullType>(NullType(), NullType());
}

}
}