/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// test_reset_object_address.cpp

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// 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)

#include <sstream>
#include <cassert>
#include <cstdlib> // for rand()
#include <cstddef> // size_t

#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
    using ::rand;
    using ::size_t;
}
#endif

#include "test_tools.hpp"

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/polymorphic_text_oarchive.hpp>
#include <boost/archive/polymorphic_text_iarchive.hpp>

#include <boost/serialization/list.hpp>
#include <boost/serialization/access.hpp>

// Someday, maybe all tests will be converted to the unit test framework.
// but for now use the text execution monitor to be consistent with all
// the other tests.

// simple test of untracked value
#include "A.hpp"
#include "A.ipp"

void test1(){
    std::stringstream ss;
    const A a;
    {
        boost::archive::text_oarchive oa(ss);
        oa << a;
        ss.flush();
    }
    A a1;
    {
        boost::archive::text_iarchive ia(ss);
        // load to temporary
        A a2;
        ia >> a2;
        BOOST_CHECK_EQUAL(a, a2);
        // move to final destination
        a1 = a2;
        ia.reset_object_address(& a1, & a2);
    }
    BOOST_CHECK_EQUAL(a, a1);
}

// simple test of tracked value
class B {
    friend class boost::serialization::access;
    int m_i;
    template<class Archive>
    void serialize(Archive &ar, const unsigned int /*file_version*/){
        ar & m_i;
    }
public:
    bool operator==(const B & rhs) const {
        return m_i == rhs.m_i;
    }
    B() :
        m_i(std::rand())
    {}
};

//BOOST_TEST_DONT_PRINT_LOG_VALUE( B )

void test2(){
    std::stringstream ss;
    B const b;
    B const * const b_ptr = & b;
    BOOST_CHECK_EQUAL(& b, b_ptr);
    {
        boost::archive::text_oarchive oa(ss);
        oa << b;
        oa << b_ptr;
        ss.flush();
    }
    B b1;
    B * b1_ptr;
    {
        boost::archive::text_iarchive ia(ss);
        // load to temporary
        B b2;
        ia >> b2;
        BOOST_CHECK_EQUAL(b, b2);
        // move to final destination
        b1 = b2;
        ia.reset_object_address(& b1, & b2);
        ia >> b1_ptr;
    }
    BOOST_CHECK_EQUAL(b, b1);
    BOOST_CHECK_EQUAL(& b1, b1_ptr);
}

// check that nested member values are properly moved
class D {
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive &ar, const unsigned int /*file_version*/){
        ar & m_b;
    }
public:
    B m_b;
    bool operator==(const D & rhs) const {
        return m_b == rhs.m_b;
    }
    D(){}
};

//BOOST_TEST_DONT_PRINT_LOG_VALUE( D )

void test3(){
    std::stringstream ss;
    D const d;
    B const * const b_ptr = & d.m_b;
    {
        boost::archive::text_oarchive oa(ss);
        oa << d;
        oa << b_ptr;
        ss.flush();
    }
    D d1;
    B * b1_ptr;
    {
        boost::archive::text_iarchive ia(ss);
        D d2;
        ia >> d2;
        d1 = d2;
        ia.reset_object_address(& d1, & d2);
        ia >> b1_ptr;
    }
    BOOST_CHECK_EQUAL(d, d1);
    BOOST_CHECK_EQUAL(* b_ptr, * b1_ptr);
}

// check that data pointed to by pointer members is NOT moved
class E {
    int m_i;
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive &ar, const unsigned int /*file_version*/){
        ar & m_i;
    }
public:
    bool operator==(const E &rhs) const {
        return m_i == rhs.m_i;
    }
    E() :
        m_i(std::rand())
    {}
    E(const E & rhs) :
        m_i(rhs.m_i)
    {}
};
//BOOST_TEST_DONT_PRINT_LOG_VALUE( E )

// check that moves don't move stuff pointed to
class F {
    friend class boost::serialization::access;
    E * m_eptr;
    template<class Archive>
    void serialize(Archive &ar, const unsigned int /*file_version*/){
        ar & m_eptr;
    }
public:
    bool operator==(const F &rhs) const {
        return *m_eptr == *rhs.m_eptr;
    }
    F & operator=(const F & rhs) {
        * m_eptr = * rhs.m_eptr;
        return *this;
    }
    F(){
        m_eptr = new E;
    }
    F(const F & rhs){
        *this = rhs;
    }
    ~F(){
        delete m_eptr;
    }
};

//BOOST_TEST_DONT_PRINT_LOG_VALUE( F )

void test4(){
    std::stringstream ss;
    const F f;
    {
        boost::archive::text_oarchive oa(ss);
        oa << f;
        ss.flush();
    }
    F f1;
    {
        boost::archive::text_iarchive ia(ss);
        F f2;
        ia >> f2;
        f1 = f2;
        ia.reset_object_address(& f1, & f2);
    }
    BOOST_CHECK_EQUAL(f, f1);
}

// check that multiple moves keep track of the correct target
class G {
    friend class boost::serialization::access;
    A m_a1;
    A m_a2;
    A *m_pa2;
    template<class Archive>
    void save(Archive &ar, const unsigned int /*file_version*/) const {
        ar << m_a1;
        ar << m_a2;
        ar << m_pa2;
    }
    template<class Archive>
    void load(Archive &ar, const unsigned int /*file_version*/){
        A a; // temporary A
        ar >> a;
        m_a1 = a;
        ar.reset_object_address(& m_a1, & a);
        ar >> a;
        m_a2 = a;
        ar.reset_object_address(& m_a2, & a);
        ar & m_pa2;
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()
public:
    bool operator==(const G &rhs) const {
        return
            m_a1 == rhs.m_a1
            && m_a2 == rhs.m_a2
            && *m_pa2 == *rhs.m_pa2;
    }
    G & operator=(const G & rhs) {
        m_a1 = rhs.m_a1;
        m_a2 = rhs.m_a2;
        m_pa2 = & m_a2;
        return *this;
    }
    G(){
        m_pa2 = & m_a2;
    }
    G(const G & rhs){
        *this = rhs;
    }
    ~G(){}
};

//BOOST_TEST_DONT_PRINT_LOG_VALUE( G )

void test5(){
    std::stringstream ss;
    const G g;
    {
        boost::archive::text_oarchive oa(ss);
        oa << g;
        ss.flush();
    }
    G g1;
    {
        boost::archive::text_iarchive ia(ss);
        ia >> g1;
    }
    BOOST_CHECK_EQUAL(g, g1);
}

// joaquin's test - this tests the case where rest_object_address
// is applied to an item which in fact is not tracked so that
// the call is in fact superfluous.
struct foo
{
  int x;

private:
  friend class boost::serialization::access;

  template<class Archive>
  void serialize(Archive &,const unsigned int)
  {
  }
};

struct bar
{
  foo  f[2];
  foo* pf[2];

private:
  friend class boost::serialization::access;
  BOOST_SERIALIZATION_SPLIT_MEMBER()

  template<class Archive>
  void save(Archive& ar,const unsigned int)const
  {
    for(int i=0;i<2;++i){
      ar<<f[i].x;
      ar<<f[i];
    }
    for(int j=0;j<2;++j){
      ar<<pf[j];
    }
  }

  template<class Archive>
  void load(Archive& ar,const unsigned int)
  {
    for(int i=0;i<2;++i){
      int x;
      ar>>x;
      f[i].x=x;
      ar.reset_object_address(&f[i].x,&x);
      ar>>f[i];
    }
    for(int j=0;j<2;++j){
      ar>>pf[j];
    }
  }
};

int test6()
{
  bar b;
  b.f[0].x=0;
  b.f[1].x=1;
  b.pf[0]=&b.f[0];
  b.pf[1]=&b.f[1];

  std::ostringstream oss;
  {
    boost::archive::text_oarchive oa(oss);
    oa<<const_cast<const bar&>(b);
    oss.flush();
  }

  bar b1;
  b1.pf[0]=0;
  b1.pf[1]=0;

  std::istringstream iss(oss.str());
  boost::archive::text_iarchive ia(iss);
  ia>>b1;
  BOOST_CHECK(b1.pf[0]==&b1.f[0]&&b1.pf[1]==&b1.f[1]);

  return 0;
}

// test one of the collections
void test7(){
    std::stringstream ss;
    B const b;
    B const * const b_ptr = & b;
    BOOST_CHECK_EQUAL(& b, b_ptr);
    {
        std::list<const B *> l;
        l.push_back(b_ptr);
        boost::archive::text_oarchive oa(ss);
        oa << const_cast<const std::list<const B *> &>(l);
        ss.flush();
    }
    B b1;
    {
        std::list<B *> l;
        boost::archive::text_iarchive ia(ss);
        ia >> l;
        delete l.front(); // prevent memory leak
    }
}

// test one of the collections with polymorphic archive
void test8(){
    std::stringstream ss;
    B const b;
    B const * const b_ptr = & b;
    BOOST_CHECK_EQUAL(& b, b_ptr);
    {
        std::list<const B *> l;
        l.push_back(b_ptr);
        boost::archive::polymorphic_text_oarchive oa(ss);
        boost::archive::polymorphic_oarchive & poa = oa;
        poa << const_cast<const std::list<const B *> &>(l);
    }
    B b1;
    {
        std::list<B *> l;
        boost::archive::polymorphic_text_iarchive ia(ss);
        boost::archive::polymorphic_iarchive & pia = ia;
        pia >> l;
        delete l.front(); // prevent memory leak
    }
}

int test_main(int /* argc */, char * /* argv */[])
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    return EXIT_SUCCESS;
}
