// Three-state boolean logic library



// Copyright Douglas Gregor 2002-2004. Use, modification and

// distribution is subject to 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 BOOST_LOGIC_TRIBOOL_IO_HPP

#define BOOST_LOGIC_TRIBOOL_IO_HPP



#include <boost/logic/tribool.hpp>

#include <boost/detail/workaround.hpp>

#include <boost/noncopyable.hpp>



#if BOOST_WORKAROUND(_MSC_VER, >= 1200)

#  pragma once

#endif



#ifndef BOOST_NO_STD_LOCALE

#  include <locale>

#endif



#include <string>

#include <iostream>



namespace boost { namespace logic {



#ifdef BOOST_NO_STD_LOCALE



/**

 * \brief Returns a string containing the default name for the \c

 * false value of a tribool with the given character type T.

 *

 * This function only exists when the C++ standard library

 * implementation does not support locales.

 */

template<typename T> std::basic_string<T> default_false_name();



/**

 * \brief Returns the character string "false".

 *

 * This function only exists when the C++ standard library

 * implementation does not support locales.

 */

template<>

inline std::basic_string<char> default_false_name<char>()

{ return "false"; }



#  ifndef BOOST_NO_WCHAR_T

/**

 * \brief Returns the wide character string L"false".

 *

 * This function only exists when the C++ standard library

 * implementation does not support locales.

 */

template<>

inline std::basic_string<wchar_t> default_false_name<wchar_t>()

{ return L"false"; }

#  endif



/**

 * \brief Returns a string containing the default name for the \c true

 * value of a tribool with the given character type T.

 *

 * This function only exists when the C++ standard library

 * implementation does not support locales.

 */

template<typename T> std::basic_string<T> default_true_name();



/**

 * \brief Returns the character string "true".

 *

 * This function only exists when the C++ standard library

 * implementation does not support locales.

 */

template<>

inline std::basic_string<char> default_true_name<char>()

{ return "true"; }



#  ifndef BOOST_NO_WCHAR_T

/**

 * \brief Returns the wide character string L"true".

 *

 *  This function only exists * when the C++ standard library

 *  implementation does not support * locales.

 */

template<>

inline std::basic_string<wchar_t> default_true_name<wchar_t>()

{ return L"true"; }

#  endif

#endif



/**

 * \brief Returns a string containing the default name for the indeterminate

 * value of a tribool with the given character type T.

 *

 * This routine is used by the input and output streaming operators

 * for tribool when there is no locale support or the stream's locale

 * does not contain the indeterminate_name facet.

 */

template<typename T> std::basic_string<T> get_default_indeterminate_name();



/// Returns the character string "indeterminate".

template<>

inline std::basic_string<char> get_default_indeterminate_name<char>()

{ return "indeterminate"; }



#if BOOST_WORKAROUND(BOOST_MSVC, < 1300)

// VC++ 6.0 chokes on the specialization below, so we're stuck without 

// wchar_t support. What a pain. TODO: it might just need a the template 

// parameter as function parameter...

#else

#  ifndef BOOST_NO_WCHAR_T

/// Returns the wide character string L"indeterminate".

template<>

inline std::basic_string<wchar_t> get_default_indeterminate_name<wchar_t>()

{ return L"indeterminate"; }

#  endif

#endif



// http://www.cantrip.org/locale.html



#ifndef BOOST_NO_STD_LOCALE

/**

 * \brief A locale facet specifying the name of the indeterminate

 * value of a tribool.

 *

 * The facet is used to perform I/O on tribool values when \c

 * std::boolalpha has been specified. This class template is only

 * available if the C++ standard library implementation supports

 * locales.

 */

template<typename CharT>

class indeterminate_name : public std::locale::facet, private boost::noncopyable

{

public:

  typedef CharT char_type;

  typedef std::basic_string<CharT> string_type;



  /// Construct the facet with the default name

  indeterminate_name() : name_(get_default_indeterminate_name<CharT>()) {}



  /// Construct the facet with the given name for the indeterminate value

  explicit indeterminate_name(const string_type& name) : name_(name) {}



  /// Returns the name for the indeterminate value

  string_type name() const { return name_; }



  /// Uniquily identifies this facet with the locale.

  static std::locale::id id;



private:

  string_type name_;

};



template<typename CharT> std::locale::id indeterminate_name<CharT>::id;

#endif



/**

 * \brief Writes the value of a tribool to a stream.

 *

 * When the value of @p x is either \c true or \c false, this routine

 * is semantically equivalent to:

 * \code out << static_cast<bool>(x); \endcode

 *

 * When @p x has an indeterminate value, it outputs either the integer

 * value 2 (if <tt>(out.flags() & std::ios_base::boolalpha) == 0</tt>)

 * or the name of the indeterminate value. The name of the

 * indeterminate value comes from the indeterminate_name facet (if it

 * is defined in the output stream's locale), or from the

 * get_default_indeterminate_name function (if it is not defined in the

 * locale or if the C++ standard library implementation does not

 * support locales).

 *

 * \returns @p out

 */

template<typename CharT, typename Traits>

inline std::basic_ostream<CharT, Traits>&

operator<<(std::basic_ostream<CharT, Traits>& out, tribool x)

{

  if (!indeterminate(x)) {

    out << static_cast<bool>(x);

  } else {

    typename std::basic_ostream<CharT, Traits>::sentry cerberus(out);

    if (cerberus) {

      if (out.flags() & std::ios_base::boolalpha) {

#ifndef BOOST_NO_STD_LOCALE

        if (BOOST_HAS_FACET(indeterminate_name<CharT>, out.getloc())) {

          const indeterminate_name<CharT>& facet =

            BOOST_USE_FACET(indeterminate_name<CharT>, out.getloc());

          out << facet.name();

        } else {

          out << get_default_indeterminate_name<CharT>();

        }

#else

        out << get_default_indeterminate_name<CharT>();

#endif

      }

      else

        out << 2;

    }

  }

  return out;

}



/**

 * \brief Writes the indeterminate tribool value to a stream.

 *

 * This routine outputs either the integer

 * value 2 (if <tt>(out.flags() & std::ios_base::boolalpha) == 0</tt>)

 * or the name of the indeterminate value. The name of the

 * indeterminate value comes from the indeterminate_name facet (if it

 * is defined in the output stream's locale), or from the

 * get_default_indeterminate_name function (if it is not defined in the

 * locale or if the C++ standard library implementation does not

 * support locales).

 *

 * \returns @p out

 */

template<typename CharT, typename Traits>

inline std::basic_ostream<CharT, Traits>&

operator<<(std::basic_ostream<CharT, Traits>& out, 

           bool (*)(tribool, detail::indeterminate_t))

{ return out << tribool(indeterminate); } 



/**

 * \brief Reads a tribool value from a stream.

 *

 * When <tt>(out.flags() & std::ios_base::boolalpha) == 0</tt>, this

 * function reads a \c long value from the input stream @p in and

 * converts that value to a tribool. If that value is 0, @p x becomes

 * \c false; if it is 1, @p x becomes \c true; if it is 2, @p becomes

 * \c indetermine; otherwise, the operation fails (and the fail bit is

 * set on the input stream @p in).

 *

 * When <tt>(out.flags() & std::ios_base::boolalpha) != 0</tt>, this

 * function first determines the names of the false, true, and

 * indeterminate values. The false and true names are extracted from

 * the \c std::numpunct facet of the input stream's locale (if the C++

 * standard library implementation supports locales), or from the \c

 * default_false_name and \c default_true_name functions (if there is

 * no locale support). The indeterminate name is extracted from the

 * appropriate \c indeterminate_name facet (if it is available in the

 * input stream's locale), or from the \c get_default_indeterminate_name

 * function (if the C++ standard library implementation does not

 * support locales, or the \c indeterminate_name facet is not

 * specified for this locale object). The input is then matched to

 * each of these names, and the tribool @p x is assigned the value

 * corresponding to the longest name that matched. If no name is

 * matched or all names are empty, the operation fails (and the fail

 * bit is set on the input stream @p in).

 *

 * \returns @p in

 */

template<typename CharT, typename Traits>

inline std::basic_istream<CharT, Traits>&

operator>>(std::basic_istream<CharT, Traits>& in, tribool& x)

{

  if (in.flags() & std::ios_base::boolalpha) {

    typename std::basic_istream<CharT, Traits>::sentry cerberus(in);

    if (cerberus) {

      typedef std::basic_string<CharT> string_type;



#ifndef BOOST_NO_STD_LOCALE

      const std::numpunct<CharT>& numpunct_facet =

        BOOST_USE_FACET(std::numpunct<CharT>, in.getloc());



      string_type falsename = numpunct_facet.falsename();

      string_type truename = numpunct_facet.truename();



      string_type othername;

      if (BOOST_HAS_FACET(indeterminate_name<CharT>, in.getloc())) {

        othername =

          BOOST_USE_FACET(indeterminate_name<CharT>, in.getloc()).name();

      } else {

        othername = get_default_indeterminate_name<CharT>();

      }

#else

      string_type falsename = default_false_name<CharT>();

      string_type truename = default_true_name<CharT>();

      string_type othername = get_default_indeterminate_name<CharT>();

#endif



      typename string_type::size_type pos = 0;

      bool falsename_ok = true, truename_ok = true, othername_ok = true;



      // Modeled after the code from Library DR 17

      while (falsename_ok && pos < falsename.size()

             || truename_ok && pos < truename.size()

             || othername_ok && pos < othername.size()) {

        typename Traits::int_type c = in.get();

        if (c == Traits::eof())

          return in;



        bool matched = false;

        if (falsename_ok && pos < falsename.size()) {

          if (Traits::eq(Traits::to_char_type(c), falsename[pos]))

            matched = true;

          else

            falsename_ok = false;

        }



        if (truename_ok && pos < truename.size()) {

          if (Traits::eq(Traits::to_char_type(c), truename[pos]))

            matched = true;

          else

            truename_ok = false;

        }



        if (othername_ok && pos < othername.size()) {

          if (Traits::eq(Traits::to_char_type(c), othername[pos]))

            matched = true;

          else

            othername_ok = false;

        }



        if (matched) { ++pos; }

        if (pos > falsename.size()) falsename_ok = false;

        if (pos > truename.size())  truename_ok = false;

        if (pos > othername.size()) othername_ok = false;

      }



      if (pos == 0)

        in.setstate(std::ios_base::failbit);

      else {

        if (falsename_ok)      x = false;

        else if (truename_ok)  x = true;

        else if (othername_ok) x = indeterminate;

        else in.setstate(std::ios_base::failbit);

      }

    }

  } else {

    long value;

    if (in >> value) {

      switch (value) {

      case 0: x = false; break;

      case 1: x = true; break;

      case 2: x = indeterminate; break;

      default: in.setstate(std::ios_base::failbit); break;

      }

    }

  }



  return in;

}



} } // end namespace boost::logic



#endif // BOOST_LOGIC_TRIBOOL_IO_HPP

