// Copyright (C) 2017 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:
//  akrzemi1@gmail.com

#include "boost/optional/optional.hpp"

#ifdef BOOST_BORLANDC
#pragma hdrstop
#endif

#include "boost/core/lightweight_test.hpp"
#include "boost/core/lightweight_test_trait.hpp"
#include "boost/type_traits/is_base_of.hpp"
#include "boost/optional/detail/experimental_traits.hpp"

#ifndef BOOST_OPTIONAL_DETAIL_NO_DEFAULTED_MOVE_FUNCTIONS

struct PrivDefault
{
  private: PrivDefault() {}
};

struct CustDefault
{
  CustDefault() {}
};

struct CustomizedTrivial
{
  CustomizedTrivial() {}
};

struct DeletedDefault
{
  BOOST_DELETED_FUNCTION(DeletedDefault())
};

namespace boost { namespace optional_config {
  
template <> struct optional_uses_direct_storage_for<CustomizedTrivial> : boost::true_type {};
  
}}

struct CustDtor
{
  ~CustDtor() {}
};

struct NoDefault
{
  explicit NoDefault(int) {}
};

struct Empty {};

template <typename T, typename U>
struct Aggregate { T t; U u; };

struct CustAssign
{
  CustAssign& operator=(CustAssign const&) { return *this; }
};

struct CustMove
{
  CustMove(CustMove &&) {}
};

void test_type_traits()
{
  // this only tests if type traits are implemented correctly
  BOOST_TEST_TRAIT_TRUE(( boost::optional_config::optional_uses_direct_storage_for<int> ));
  BOOST_TEST_TRAIT_TRUE(( boost::optional_config::optional_uses_direct_storage_for<double> ));
  
  BOOST_TEST_TRAIT_TRUE(( boost::optional_config::optional_uses_direct_storage_for<CustomizedTrivial> ));
  
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<PrivDefault> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<NoDefault> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<CustDefault> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<Aggregate<int, CustDefault> > ));
  
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<CustDtor> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<CustAssign> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<CustMove> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<Aggregate<int, CustMove> > ));
  
  BOOST_TEST_TRAIT_TRUE(( boost::optional_detail::is_type_trivially_copyable<int> ));
  BOOST_TEST_TRAIT_TRUE(( boost::optional_detail::is_type_trivially_copyable<double> ));

  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<Empty> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<Aggregate<int, double> > ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<Aggregate<Aggregate<Empty, int>, double> > ));
#ifndef BOOST_OPTIONAL_DETAIL_NO_SPEC_FOR_TRIVIAL_TYPES
  
  BOOST_TEST_TRAIT_TRUE(( boost::optional_detail::is_type_trivially_copyable<Empty> ));
  BOOST_TEST_TRAIT_TRUE(( boost::optional_detail::is_type_trivially_copyable<Aggregate<int, double> > ));
  BOOST_TEST_TRAIT_TRUE(( boost::optional_detail::is_type_trivially_copyable<Aggregate<Aggregate<Empty, int>, double> > ));
  
#endif  

  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<DeletedDefault> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_config::optional_uses_direct_storage_for<Aggregate<int, DeletedDefault> > ));
  
  BOOST_TEST_TRAIT_FALSE(( boost::optional_detail::is_type_trivially_copyable<CustDtor> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_detail::is_type_trivially_copyable<CustAssign> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_detail::is_type_trivially_copyable<CustMove> ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_detail::is_type_trivially_copyable<Aggregate<int, CustMove> > ));
}

void test_trivial_copyability()
{
  BOOST_TEST_TRAIT_TRUE((boost::is_base_of<boost::optional_detail::tc_optional_base<int>, boost::optional<int> > ));
  BOOST_TEST_TRAIT_TRUE((boost::is_base_of<boost::optional_detail::tc_optional_base<double>, boost::optional<double> > ));
  BOOST_TEST_TRAIT_TRUE((boost::is_base_of<boost::optional_detail::tc_optional_base<CustomizedTrivial>, boost::optional<CustomizedTrivial> > ));
  BOOST_TEST_TRAIT_FALSE((boost::is_base_of<boost::optional_detail::tc_optional_base<DeletedDefault>, boost::optional<DeletedDefault> > ));
  
#ifndef BOOST_OPTIONAL_DETAIL_NO_SPEC_FOR_TRIVIAL_TYPES
  BOOST_TEST_TRAIT_TRUE(( boost::optional_detail::is_type_trivially_copyable<boost::optional<int> > ));
  BOOST_TEST_TRAIT_TRUE(( boost::optional_detail::is_type_trivially_copyable<boost::optional<double> > ));
  BOOST_TEST_TRAIT_TRUE(( boost::optional_detail::is_type_trivially_copyable<boost::optional<CustomizedTrivial> > ));
  
  BOOST_TEST_TRAIT_FALSE(( boost::optional_detail::is_type_trivially_copyable<boost::optional<DeletedDefault> > ));
#endif

  BOOST_TEST_TRAIT_FALSE(( boost::optional_detail::is_type_trivially_copyable<boost::optional<Empty> > ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_detail::is_type_trivially_copyable<boost::optional<Aggregate<int, double> > > ));
  BOOST_TEST_TRAIT_FALSE(( boost::optional_detail::is_type_trivially_copyable<boost::optional<Aggregate<Aggregate<Empty, int>, double> > > ));
}

#endif

int main()
{
#ifndef BOOST_OPTIONAL_DETAIL_NO_DEFAULTED_MOVE_FUNCTIONS
  test_type_traits();
  test_trivial_copyability();
#endif
  return boost::report_errors();
}
