//  Copyright (c) 2001-2010 Hartmut Kaiser
//
//  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)

#if !defined(BOOST_SPIRIT_KARMA_CHAR_FEB_21_2007_0543PM)
#define BOOST_SPIRIT_KARMA_CHAR_FEB_21_2007_0543PM

#if defined(_MSC_VER)
#pragma once
#endif

#include <boost/spirit/home/support/common_terminals.hpp>
#include <boost/spirit/home/support/string_traits.hpp>
#include <boost/spirit/home/support/info.hpp>
#include <boost/spirit/home/support/char_class.hpp>
#include <boost/spirit/home/support/detail/get_encoding.hpp>
#include <boost/spirit/home/support/char_set/basic_chset.hpp>
#include <boost/spirit/home/karma/domain.hpp>
#include <boost/spirit/home/karma/meta_compiler.hpp>
#include <boost/spirit/home/karma/delimit_out.hpp>
#include <boost/spirit/home/karma/char/char_generator.hpp>
#include <boost/spirit/home/karma/auxiliary/lazy.hpp>
#include <boost/spirit/home/karma/detail/get_casetag.hpp>
#include <boost/spirit/home/karma/detail/generate_to.hpp>
#include <boost/fusion/include/at.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/cons.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/bool.hpp>
#include <string>

///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit
{
    ///////////////////////////////////////////////////////////////////////////
    // Enablers
    ///////////////////////////////////////////////////////////////////////////
    template <typename CharEncoding>
    struct use_terminal<karma::domain
      , tag::char_code<tag::char_, CharEncoding>        // enables char_
    > : mpl::true_ {};

    template <typename CharEncoding, typename A0>
    struct use_terminal<karma::domain
      , terminal_ex<
            tag::char_code<tag::char_, CharEncoding>    // enables char_('x'), char_("x")
          , fusion::vector1<A0>
        >
    > : mpl::true_ {};

    template <typename CharEncoding, typename A0, typename A1>
    struct use_terminal<karma::domain
      , terminal_ex<
            tag::char_code<tag::char_, CharEncoding>    // enables char_('a','z')
          , fusion::vector2<A0, A1>
        >
    > : mpl::true_ {};

    template <typename CharEncoding>                    // enables *lazy* char_('x'), char_("x")
    struct use_lazy_terminal<
        karma::domain
      , tag::char_code<tag::char_, CharEncoding>
      , 1 // arity
    > : mpl::true_ {};

    template <>
    struct use_terminal<karma::domain, char>            // enables 'x'
      : mpl::true_ {};

    template <>
    struct use_terminal<karma::domain, char[2]>         // enables "x"
      : mpl::true_ {};

    template <>
    struct use_terminal<karma::domain, wchar_t>         // enables L'x'
      : mpl::true_ {};

    template <>
    struct use_terminal<karma::domain, wchar_t[2]>      // enables L"x"
      : mpl::true_ {};

}}

///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit { namespace karma
{
    using spirit::lit;    // lit('x') is equivalent to 'x'

    ///////////////////////////////////////////////////////////////////////////
    //
    //  any_char
    //      generates a single character from the associated attribute
    //
    //      Note: this generator has to have an associated attribute
    //
    ///////////////////////////////////////////////////////////////////////////
    template <typename CharEncoding, typename Tag>
    struct any_char
      : char_generator<any_char<CharEncoding, Tag>, CharEncoding, Tag>
    {
        typedef typename CharEncoding::char_type char_type;
        typedef CharEncoding char_encoding;

        template <typename Context, typename Unused>
        struct attribute
        {
            typedef char_type type;
        };

        // any_char has an attached parameter
        template <typename Attribute, typename CharParam, typename Context>
        bool test(Attribute const& attr, CharParam& ch, Context&) const
        {
            ch = CharParam(attr);
            return true;
        }

        // any_char has no attribute attached, it needs to have been
        // initialized from a direct literal
        template <typename CharParam, typename Context>
        bool test(unused_type, CharParam&, Context&) const
        {
            // It is not possible (doesn't make sense) to use char_ without
            // providing any attribute, as the generator doesn't 'know' what
            // character to output. The following assertion fires if this
            // situation is detected in your code.
            BOOST_SPIRIT_ASSERT_MSG(false, char_not_usable_without_attribute, ());
            return false;
        }

        template <typename Context>
        static info what(Context const& /*context*/)
        {
            return info("any-char");
        }
    };

    ///////////////////////////////////////////////////////////////////////////
    //
    //  literal_char
    //      generates a single character given by a literal it was initialized
    //      from
    //
    ///////////////////////////////////////////////////////////////////////////
    template <typename CharEncoding, typename Tag, bool no_attribute>
    struct literal_char
      : char_generator<literal_char<CharEncoding, Tag, no_attribute>
          , CharEncoding, Tag>
    {
        typedef typename CharEncoding::char_type char_type;
        typedef CharEncoding char_encoding;

        literal_char(char_type ch)
          : ch (spirit::char_class::convert<char_encoding>::to(Tag(), ch)) 
        {}

        template <typename Context, typename Unused>
        struct attribute
          : mpl::if_c<no_attribute, unused_type, char_type>
        {};

        // A char_('x') which additionally has an associated attribute emits
        // its immediate literal only if it matches the attribute, otherwise
        // it fails.
        // any_char has an attached parameter
        template <typename Attribute, typename CharParam, typename Context>
        bool test(Attribute const& attr, CharParam& ch_, Context&) const
        {
            // fail if attribute isn't matched my immediate literal
            ch_ = attr;
            return attr == ch;
        }

        // A char_('x') without any associated attribute just emits its 
        // immediate literal
        template <typename CharParam, typename Context>
        bool test(unused_type, CharParam& ch_, Context&) const
        {
            ch_ = ch;
            return true;
        }

        template <typename Context>
        info what(Context const& /*context*/) const
        {
            return info("literal-char", char_encoding::toucs4(ch));
        }

        char_type ch;
    };

    ///////////////////////////////////////////////////////////////////////////
    // char range generator
    template <typename CharEncoding, typename Tag>
    struct char_range
      : char_generator<char_range<CharEncoding, Tag>, CharEncoding, Tag>
    {
        typedef typename CharEncoding::char_type char_type;
        typedef CharEncoding char_encoding;

        char_range(char_type from, char_type to)
          : from(spirit::char_class::convert<char_encoding>::to(Tag(), from))
          , to(spirit::char_class::convert<char_encoding>::to(Tag(), to)) 
        {}

        // A char_('a', 'z') which has an associated attribute emits it only if 
        // it matches the character range, otherwise it fails.
        template <typename Attribute, typename CharParam, typename Context>
        bool test(Attribute const& attr, CharParam& ch, Context&) const
        {
            // fail if attribute doesn't belong to character range
            ch = attr;
            return (from <= char_type(attr)) && (char_type(attr) <= to);
        }

        // A char_('a', 'z') without any associated attribute fails compiling
        template <typename CharParam, typename Context>
        bool test(unused_type, CharParam&, Context&) const
        {
            // It is not possible (doesn't make sense) to use char_ generators 
            // without providing any attribute, as the generator doesn't 'know' 
            // what to output. The following assertion fires if this situation
            // is detected in your code.
            BOOST_SPIRIT_ASSERT_MSG(false
              , char_range_not_usable_without_attribute, ());
            return false;
        }

        template <typename Context>
        info what(Context& /*context*/) const
        {
            info result("char-range", char_encoding::toucs4(from));
            boost::get<std::string>(result.value) += '-';
            boost::get<std::string>(result.value) += to_utf8(char_encoding::toucs4(to));
            return result;
        }

        char_type from, to;
    };

    ///////////////////////////////////////////////////////////////////////////
    // character set generator
    template <typename CharEncoding, typename Tag>
    struct char_set
      : char_generator<char_set<CharEncoding, Tag>, CharEncoding, Tag>
    {
        typedef typename CharEncoding::char_type char_type;
        typedef CharEncoding char_encoding;

        template <typename String>
        char_set(String const& str)
        {
            typedef typename traits::char_type_of<String>::type in_type;

            BOOST_SPIRIT_ASSERT_MSG((
                (sizeof(char_type) == sizeof(in_type))
            ), cannot_convert_string, (String));

            typedef spirit::char_class::convert<char_encoding> convert_type;

            char_type const* definition =
                (char_type const*)traits::get_c_string(str);
            char_type ch = convert_type::to(Tag(), *definition++);
            while (ch)
            {
                char_type next = convert_type::to(Tag(), *definition++);
                if (next == '-')
                {
                    next = convert_type::to(Tag(), *definition++);
                    if (next == 0)
                    {
                        chset.set(ch);
                        chset.set('-');
                        break;
                    }
                    chset.set(ch, next);
                }
                else
                {
                    chset.set(ch);
                }
                ch = next;
            }
        }

        // A char_("a-z") which has an associated attribute emits it only if 
        // it matches the character set, otherwise it fails.
        template <typename Attribute, typename CharParam, typename Context>
        bool test(Attribute const& attr, CharParam& ch, Context&) const
        {
            // fail if attribute doesn't belong to character set
            ch = attr;
            return chset.test(char_type(attr));
        }

        // A char_("a-z") without any associated attribute fails compiling
        template <typename CharParam, typename Context>
        bool test(unused_type, CharParam&, Context&) const
        {
            // It is not possible (doesn't make sense) to use char_ generators 
            // without providing any attribute, as the generator doesn't 'know' 
            // what to output. The following assertion fires if this situation
            // is detected in your code.
            BOOST_SPIRIT_ASSERT_MSG(false
               , char_set_not_usable_without_attribute, ());
            return false;
        }

        template <typename Context>
        info what(Context& /*context*/) const
        {
            return info("char-set");
        }

        support::detail::basic_chset<char_type> chset;
    };

    ///////////////////////////////////////////////////////////////////////////
    // Generator generators: make_xxx function (objects)
    ///////////////////////////////////////////////////////////////////////////
    namespace detail
    {
        template <typename Modifiers, typename Encoding>
        struct basic_literal
        {
            static bool const lower =
                has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value;
            static bool const upper =
                has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value;

            typedef literal_char<
                typename spirit::detail::get_encoding<
                    Modifiers, Encoding, lower || upper>::type
              , typename get_casetag<Modifiers, lower || upper>::type
              , true>
            result_type;

            template <typename Char>
            result_type operator()(Char ch, unused_type) const
            {
                return result_type(ch);
            }

            template <typename Char>
            result_type operator()(Char const* str, unused_type) const
            {
                return result_type(str[0]);
            }
        };
    }

    // literals: 'x', "x"
    template <typename Modifiers>
    struct make_primitive<char, Modifiers>
      : detail::basic_literal<Modifiers, char_encoding::standard> {};

    template <typename Modifiers>
    struct make_primitive<char const(&)[2], Modifiers>
      : detail::basic_literal<Modifiers, char_encoding::standard> {};

    // literals: L'x', L"x"
    template <typename Modifiers>
    struct make_primitive<wchar_t, Modifiers>
      : detail::basic_literal<Modifiers, char_encoding::standard_wide> {};

    template <typename Modifiers>
    struct make_primitive<wchar_t const(&)[2], Modifiers>
      : detail::basic_literal<Modifiers, char_encoding::standard_wide> {};

    // char_
    template <typename CharEncoding, typename Modifiers>
    struct make_primitive<tag::char_code<tag::char_, CharEncoding>, Modifiers>
    {
        static bool const lower =
            has_modifier<Modifiers, tag::char_code<tag::lower, CharEncoding> >::value;
        static bool const upper =
            has_modifier<Modifiers, tag::char_code<tag::upper, CharEncoding> >::value;

        typedef any_char<
            typename spirit::detail::get_encoding<
                Modifiers, CharEncoding, lower || upper>::type
          , typename detail::get_casetag<Modifiers, lower || upper>::type
        > result_type;

        result_type operator()(unused_type, unused_type) const
        {
            return result_type();
        }
    };

    // char_(...)
    template <typename CharEncoding, typename Modifiers, typename A0>
    struct make_primitive<
        terminal_ex<
            tag::char_code<tag::char_, CharEncoding>
          , fusion::vector1<A0>
        >
      , Modifiers>
    {
        static bool const lower =
            has_modifier<Modifiers, tag::char_code<tag::lower, CharEncoding> >::value;
        static bool const upper =
            has_modifier<Modifiers, tag::char_code<tag::upper, CharEncoding> >::value;

        typedef typename spirit::detail::get_encoding<
            Modifiers, CharEncoding, lower || upper>::type encoding;
        typedef typename detail::get_casetag<
            Modifiers, lower || upper>::type tag;

        typedef typename mpl::if_<
            traits::is_string<A0>
          , char_set<encoding, tag>
          , literal_char<encoding, tag, false>
        >::type result_type;

        template <typename Terminal>
        result_type operator()(Terminal const& term, unused_type) const
        {
            return result_type(fusion::at_c<0>(term.args));
        }
    };

    // char_("x")
    template <typename CharEncoding, typename Modifiers, typename Char>
    struct make_primitive<
        terminal_ex<
            tag::char_code<tag::char_, CharEncoding>
          , fusion::vector1<Char(&)[2]> // For single char strings
        >
      , Modifiers>
    {
        static bool const lower =
            has_modifier<Modifiers, tag::char_code<tag::lower, CharEncoding> >::value;
        static bool const upper =
            has_modifier<Modifiers, tag::char_code<tag::upper, CharEncoding> >::value;

        typedef literal_char<
            typename spirit::detail::get_encoding<
                Modifiers, CharEncoding, lower || upper>::type
          , typename detail::get_casetag<Modifiers, lower || upper>::type
          , false
        > result_type;

        template <typename Terminal>
        result_type operator()(Terminal const& term, unused_type) const
        {
            return result_type(fusion::at_c<0>(term.args)[0]);
        }
    };

    // char_('a', 'z')
    template <typename CharEncoding, typename Modifiers, typename A0, typename A1>
    struct make_primitive<
        terminal_ex<
            tag::char_code<tag::char_, CharEncoding>
          , fusion::vector2<A0, A1>
        >
      , Modifiers>
    {
        static bool const lower =
            has_modifier<Modifiers, tag::char_code<tag::lower, CharEncoding> >::value;
        static bool const upper =
            has_modifier<Modifiers, tag::char_code<tag::upper, CharEncoding> >::value;

        typedef char_range<
            typename spirit::detail::get_encoding<
                Modifiers, CharEncoding, lower || upper>::type
          , typename detail::get_casetag<Modifiers, lower || upper>::type
        > result_type;

        template <typename Terminal>
        result_type operator()(Terminal const& term, unused_type) const
        {
            return result_type(fusion::at_c<0>(term.args)
              , fusion::at_c<1>(term.args));
        }
    };

}}}   // namespace boost::spirit::karma

#endif
