//
// Boost.Pointer Container
//
//  Copyright Thorsten Ottosen 2003-2005. 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)
//
// For more information, see http://www.boost.org/libs/ptr_container/
//
 
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
#include <boost/config.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/functional/hash.hpp>
#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <cstdlib>

using namespace std;
using namespace boost;

//////////////////////////////////////////////////////////////////////////////
// Test class 1: a class hierarchy
//////////////////////////////////////////////////////////////////////////////

namespace test
{
    class Base
    {
    protected:
        Base( const Base& r ) : data1(r.data1), data2(r.data2), 
            data3(r.data3), data(r.data) 
        { 
    #ifdef PTR_CONTAINER_DEBUG
            objects++;
            std::cout <<"+ " << objects << "\n"; 
    #endif
        }
        
        Base& operator=( const Base& );
    
    public: // for test reasons only
        int data1, data2, data3;
        string data;
        
    public:
        
        Base() : data1(1), data2(2), data3(rand()%256), 
                 data(lexical_cast<string>(rand())) 
        {
    #ifdef PTR_CONTAINER_DEBUG
            objects++;
            std::cout <<"+ " << objects << "\n"; 
    #endif
        }
        
        virtual ~Base()                       
        {
    #ifdef PTR_CONTAINER_DEBUG
            objects--;
            std::cout <<"- " << objects << "\n"; 
            if( objects < 0 )
                terminate();
    #endif
        }
        
        void     print( ostream& out ) const  { do_print( out); }
        Base*    clone() const                { return do_clone(); }
        void     foo()                        { do_foo(); }
        
        virtual bool less_than( const Base& b ) const
        {
            return data3 < b.data3;
        }
        
        virtual bool equal( const Base& b ) const
        {
            return data1 == b.data1 && 
                   data2 == b.data2 &&
                   data3 == b.data3 &&
                   data  == b.data;
        }
    
    #ifdef PTR_CONTAINER_DEBUG
        static int objects;
    #endif    
        
    private:
        virtual void  do_print( ostream& /*out*/ ) const   { };
        virtual Base* do_clone() const                     { return new Base( *this ); }; 
        virtual void  do_foo()                             { };
    };
    
    #ifdef PTR_CONTAINER_DEBUG
    int Base::objects = 0;
    #endif
    
    
    
    ostream& operator<<( ostream& out, const Base& b )
    {
        b.print( out );
        return out;
    }
    
    
    //
    //  We rely on argument dependent lookup
    //  for this to be found
    //
    inline Base* new_clone( const Base& b )
    {
        return b.clone();
    }
    
    
    
    inline bool operator<( const Base& l, const Base& r )
    {
        return l.less_than( r );
    }
    
    
    
    inline bool operator>( const Base& l, const Base& r )
    {
        return r < l;
    }
    
    
    
    inline bool operator==( const Base& l, const Base& r )
    {
        return l.equal( r );
    }
    
    
    
    inline bool operator!=( const Base& l, const Base& r )
    {
        return !l.equal( r );
    }
    
    
    
    inline std::size_t hash_value( const Base& b )
    {
        std::size_t seed = 0;
        boost::hash_combine( seed, b.data );
        boost::hash_combine( seed, b.data1 );
        boost::hash_combine( seed, b.data2 );
        boost::hash_combine( seed, b.data3 );
        return seed;
    }
    
    
    class Derived_class : public Base
    {   
    protected:
        Derived_class( const Derived_class& r ) : Base( r ), i_(r.i_)
        { } 
        
    public: // for test reasons only
        int i_;
    
    private:
            
        virtual void  do_print( ostream& out ) const
        {
            out << i_;
        }
        
        
        virtual Base* do_clone() const
        {
            return new Derived_class( *this );
        }
        
        virtual void do_foo() 
        {
            ++i_;
        }
        
    public:
        Derived_class() : i_( rand() )
        { }
    
        virtual bool less_than( const Base& b ) const
        {
            const Derived_class& d = dynamic_cast<const Derived_class&>( b );
            return i_ < d.i_;
        }
    };
    
    
    
    inline std::size_t hash_value( const Derived_class& b )
    {
        std::size_t seed = hash_value( static_cast<const Base&>( b ) );
        boost::hash_combine( seed, b.i_ );
        return seed;
    }
}

using test::Base;
using test::Derived_class;

//////////////////////////////////////////////////////////////////////////////
// Test class 2: a value class 
//////////////////////////////////////////////////////////////////////////////

class Value 
{   
public: // for test reasons only    
    string s_;
    
public:
    
    Value() : s_( boost::lexical_cast<string>( rand() ) ) 
    {}
    
    ~Value()      { /** debug code here */ }
    
    string name() const
    {
        return s_;
    }
};



inline bool operator<( const Value& l, const Value& r )
{
    return l.name() < r.name();
}



inline bool operator>( const Value& l, const Value& r )
{
    return l.name() > r.name();
}



inline bool operator==( const Value& l, const Value& r )
{
    return l.name() == r.name();
}



inline bool operator!=( const Value& l, const Value& r )
{
    return l.name() != r.name();
}



inline ostream& operator<<( ostream& out, const Value& v )
{
    return out << v.name() << " ";
}



inline std::size_t hash_value( const Value& v )
{
    return boost::hash_value( v.s_ );
}

//
// used to hide "unused variable" warnings
//
template< class T >
inline void hide_warning( T& /*r*/ )
{ }

//
// used to customize tests for circular_buffer
//
template< class Cont >
struct set_capacity
{
    void operator()( Cont& ) const
    { }
};

//
//  transfer() test
// 

template< class Cont1, class Cont2 >
void transfer_test( Cont1& from, Cont2& to )
{
    BOOST_TEST_MESSAGE( "starting container transfer test" );
    BOOST_CHECK( !from.empty() );
    to. BOOST_NESTED_TEMPLATE transfer<Cont1>( from );
    BOOST_CHECK( !to.empty() );
    BOOST_TEST_MESSAGE( "finishing container transfer test" );
}


//
// test of copy operations
//

template< class BaseContainer, class DerivedContainer, class Derived >
void container_assignment_test()
{
    BOOST_TEST_MESSAGE( "starting container assignment test" );

    DerivedContainer derived;
    set_capacity<DerivedContainer>()( derived );
    derived.insert( derived.begin(), new Derived );
    derived.insert( derived.begin(), new Derived );

    BaseContainer base( derived );
    BOOST_CHECK_EQUAL( derived.size(), base.size() );
    base.clear();
    base = derived;
    BOOST_CHECK_EQUAL( derived.size(), base.size() );
    BaseContainer base2( base );
    BOOST_CHECK_EQUAL( base2.size(), base.size() );
    base2 = base;
    BOOST_CHECK_EQUAL( base2.size(), base.size() );
    base = base;

    BOOST_TEST_MESSAGE( "finished container assignment test" );
}


