// Copyright (C) 2003, Fernando Luis Cacciola Carballal.
// Copyright (C) 2014, Andrzej Krzemienski.
//
// 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)
//
// See http://www.boost.org/lib/optional for documentation.
//
// You are welcome to contact the author at:
//  fernando_cacciola@hotmail.com
//
#include <boost/core/ignore_unused.hpp>

#ifdef ENABLE_TRACE
#define TRACE(msg) std::cout << msg << std::endl ;
#else
#define TRACE(msg)
#endif

namespace boost {

void assertion_failed (char const * expr, char const * func, char const * file, long )
{
  using std::string ;
  string msg =  string("Boost assertion failure for \"")
               + string(expr)
               + string("\" at file \"")
               + string(file)
               + string("\" function \"")
               + string(func)
               + string("\"") ;

  TRACE(msg);

  throw std::logic_error(msg);
}

}

using boost::optional ;


#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
using boost::swap ;
using boost::get ;
using boost::get_pointer ;
#endif

// MSVC6.0 does not support comparisons of optional against a literal null pointer value (0)
// via the safe_bool operator.
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1300) ) // 1300 == VC++ 7.1
#define BOOST_OPTIONAL_NO_NULL_COMPARE
#else
#define BOOST_OPTIONAL_NO_NULL_COMPARE  // Andrzej: I also disable 0 comparison everywhere
#endif

#define ARG(T) (static_cast< T const* >(0))

//
// Helper class used to verify the lifetime management of the values held by optional
//
class X
{
  public :

    X ( int av ) : v(av)
    {
      ++ count ;

      TRACE ( "X::X(" << av << "). this=" << this ) ;
    }

    X ( X const& rhs ) : v(rhs.v)
    {
       pending_copy = false ;

       TRACE ( "X::X( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;

       if ( throw_on_copy )
       {
         TRACE ( "throwing exception in X's copy ctor" ) ;
         throw 0 ;
       }

       ++ count ;
    }

    ~X()
    {
      pending_dtor = false ;

      -- count ;

      TRACE ( "X::~X(). v=" << v << "  this=" << this );
    }

    X& operator= ( X const& rhs )
      {
        pending_assign = false ;

        if ( throw_on_assign )
        {
          TRACE ( "throwing exception in X's assignment" ) ;

          v = -1 ;

          throw 0 ;
        }
        else
        {
          v = rhs.v ;

          TRACE ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;
        }
        return *this ;
      }

    friend bool operator == ( X const& a, X const& b )
      { return a.v == b.v ; }

    friend bool operator != ( X const& a, X const& b )
      { return a.v != b.v ; }

    friend bool operator < ( X const& a, X const& b )
      { return a.v < b.v ; }

    int  V() const { return v ; }
    int& V()       { return v ; }

    static int  count ;
    static bool pending_copy   ;
    static bool pending_dtor   ;
    static bool pending_assign ;
    static bool throw_on_copy ;
    static bool throw_on_assign ;

  private :

    int  v ;

  private :

    X() ;
} ;


int  X::count           = 0 ;
bool X::pending_copy    = false ;
bool X::pending_dtor    = false ;
bool X::pending_assign  = false ;
bool X::throw_on_copy   = false ;
bool X::throw_on_assign = false ;

inline void set_pending_copy           ( X const* ) { X::pending_copy  = true  ; }
inline void set_pending_dtor           ( X const* ) { X::pending_dtor  = true  ; }
inline void set_pending_assign         ( X const* ) { X::pending_assign = true  ; }
inline void set_throw_on_copy          ( X const* ) { X::throw_on_copy = true  ; }
inline void set_throw_on_assign        ( X const* ) { X::throw_on_assign = true  ; }
inline void reset_throw_on_copy        ( X const* ) { X::throw_on_copy = false ; }
inline void reset_throw_on_assign      ( X const* ) { X::throw_on_assign = false ; }
inline void check_is_pending_copy      ( X const* ) { BOOST_TEST( X::pending_copy ) ; }
inline void check_is_pending_dtor      ( X const* ) { BOOST_TEST( X::pending_dtor ) ; }
inline void check_is_pending_assign    ( X const* ) { BOOST_TEST( X::pending_assign ) ; }
inline void check_is_not_pending_copy  ( X const* ) { BOOST_TEST( !X::pending_copy ) ; }
inline void check_is_not_pending_dtor  ( X const* ) { BOOST_TEST( !X::pending_dtor ) ; }
inline void check_is_not_pending_assign( X const* ) { BOOST_TEST( !X::pending_assign ) ; }
inline void check_instance_count       ( int c, X const* ) { BOOST_TEST( X::count == c ) ; }
inline int  get_instance_count         ( X const* ) { return X::count ; }

inline void set_pending_copy           (...) {}
inline void set_pending_dtor           (...) {}
inline void set_pending_assign         (...) {}
inline void set_throw_on_copy          (...) {}
inline void set_throw_on_assign        (...) {}
inline void reset_throw_on_copy        (...) {}
inline void reset_throw_on_assign      (...) {}
inline void check_is_pending_copy      (...) {}
inline void check_is_pending_dtor      (...) {}
inline void check_is_pending_assign    (...) {}
inline void check_is_not_pending_copy  (...) {}
inline void check_is_not_pending_dtor  (...) {}
inline void check_is_not_pending_assign(...) {}
inline void check_instance_count       (...) {}
inline int  get_instance_count         (...) { return 0 ; }


template<class T>
inline void check_uninitialized_const ( optional<T> const& opt )
{
#ifndef BOOST_OPTIONAL_NO_NULL_COMPARE
  BOOST_TEST( opt == 0 ) ;
#endif
  BOOST_TEST( !opt ) ;
  BOOST_TEST( !get_pointer(opt) ) ;
  BOOST_TEST( !opt.get_ptr() ) ;
  BOOST_TEST( !opt.has_value() ) ;
  BOOST_TEST( !opt.is_initialized() ) ;
  BOOST_TEST( opt == boost::none ) ;
}
template<class T>
inline void check_uninitialized ( optional<T>& opt )
{
#ifndef BOOST_OPTIONAL_NO_NULL_COMPARE
  BOOST_TEST( opt == 0 ) ;
#endif
  BOOST_TEST( !opt ) ;
  BOOST_TEST( !get_pointer(opt) ) ;
  BOOST_TEST( !opt.get_ptr() ) ;
  BOOST_TEST( !opt.has_value() ) ;
  BOOST_TEST( !opt.is_initialized() ) ;
  BOOST_TEST( opt == boost::none ) ;

  check_uninitialized_const(opt);
}

template<class T>
inline void check_initialized_const ( optional<T> const& opt )
{
  BOOST_TEST( opt ) ;

#ifndef BOOST_OPTIONAL_NO_NULL_COMPARE
  BOOST_TEST( opt != 0 ) ;
#endif

  BOOST_TEST ( !!opt ) ;
  BOOST_TEST ( get_pointer(opt) ) ;
  BOOST_TEST ( opt.get_ptr() ) ;
  BOOST_TEST ( opt.has_value() ) ;
  BOOST_TEST ( opt.is_initialized() ) ;
  BOOST_TEST ( opt != boost::none ) ;
}

template<class T>
inline void check_initialized ( optional<T>& opt )
{
  BOOST_TEST( opt ) ;

#ifndef BOOST_OPTIONAL_NO_NULL_COMPARE
  BOOST_TEST( opt != 0 ) ;
#endif

  BOOST_TEST ( !!opt ) ;
  BOOST_TEST ( get_pointer(opt) ) ;
  BOOST_TEST ( opt.get_ptr() ) ;
  BOOST_TEST ( opt.has_value() ) ;
  BOOST_TEST ( opt.is_initialized() ) ;
  BOOST_TEST ( opt != boost::none ) ;

  check_initialized_const(opt);
}

template<class T>
inline void check_value_const ( optional<T> const& opt, T const& v, T const& z )
{
  BOOST_TEST( *opt == v ) ;
  BOOST_TEST( *opt != z ) ;
  BOOST_TEST( opt.get() == v ) ;
  BOOST_TEST( opt.get() != z ) ;
  BOOST_TEST( (*(opt.operator->()) == v) ) ;
  BOOST_TEST( *get_pointer(opt) == v ) ;
}

template<class T>
inline void check_value ( optional<T>& opt, T const& v, T const& z )
{
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1200) // 1200 == VC++ 6.0
  // For some reason, VC6.0 is creating a temporary while evaluating (*opt == v),
  // so we need to turn throw on copy off first.
  reset_throw_on_copy( ARG(T) ) ;
#endif

  BOOST_TEST( *opt == v ) ;
  BOOST_TEST( *opt != z ) ;
  BOOST_TEST( opt.get() == v ) ;
  BOOST_TEST( opt.get() != z ) ;
  BOOST_TEST( (*(opt.operator->()) == v) ) ;
  BOOST_TEST( *get_pointer(opt) == v ) ;

  check_value_const(opt,v,z);
}


