//          Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef SNOWHOUSE_STRINGIZERS_H
#define SNOWHOUSE_STRINGIZERS_H

#include "stringize.h"

namespace snowhouse
{
    namespace detail
    {
        template <typename Container>
        struct SequentialContainerStringizer
        {
            static std::string ToString(const Container& cont)
            {
                std::ostringstream stm;
                using Iterator = typename Container::const_iterator;

                stm << "[ ";
                for (Iterator it = cont.begin(); it != cont.end();)
                {
                    stm << snowhouse::Stringize(*it);

                    if (++it != cont.end())
                    {
                        stm << ", ";
                    }
                }
                stm << " ]";
                return stm.str();
            }
        };
    }

    namespace typing
    {
        template <typename T>
        struct is_const_iterable
        {
            template <typename CLASS>
            static yes compile_time_check_const_iterator(
                typename CLASS::const_iterator*);

            template <typename>
            static no compile_time_check_const_iterator(...);

            static const bool value =
                sizeof(compile_time_check_const_iterator<T>(nullptr)) ==
                sizeof(yes);
        };

        template <typename T, bool = is_const_iterable<T>::value>
        struct is_container
        {
            using Iterator = typename T::const_iterator;

            struct FallbackBeginEnd
            {
                Iterator begin() const;
                Iterator end() const;
            };

            using fallback_method = Iterator (FallbackBeginEnd::*)() const;

            template <typename CLASS, CLASS>
            struct is_of_type;

            template <typename CLASS>
            static no compile_time_check_begin(
                is_of_type<fallback_method, &CLASS::begin>*);

            template <typename CLASS>
            static no compile_time_check_end(
                is_of_type<fallback_method, &CLASS::end>*);

            template <typename>
            static yes compile_time_check_begin(...);

            template <typename>
            static yes compile_time_check_end(...);

            struct FallbackContainer : T, FallbackBeginEnd
            {
            };

            static const bool has_begin =
                sizeof(compile_time_check_begin<FallbackContainer>(0)) ==
                sizeof(yes);
            static const bool has_end =
                sizeof(compile_time_check_end<FallbackContainer>(0)) ==
                sizeof(yes);

            static const bool value = has_begin && has_end;
        };

        template <typename T>
        struct is_container<T, false>
        {
            static const bool value = false;
        };

        template <>
        struct is_container<std::string>
        {
            static const bool value = false;
        };

        template <bool, typename = yes>
        struct enable_if
        {
        };

        template <typename TRUE_TYPE>
        struct enable_if<true, TRUE_TYPE>
        {
            using type = TRUE_TYPE;
        };
    }

    template <typename T>
    struct Stringizer<T,
        typename typing::enable_if<typing::is_container<T>::value>::type> :
        detail::SequentialContainerStringizer<T>
    {
    };
}

#endif
