#include "shared_ptr.h"
#include "UnitTest++.h"
#include <stdexcept>
#include <vector>

class Foo {
private:
	int x;

public:
	Foo ( int x ) : x ( x ) { }

	int get_x() const {
		return x;
	}

	void set_x ( int nx ) {
		x = nx;
	}
};

typedef std::vector<bdm::shared_ptr<Foo> > TFooVector;

typedef std::vector<bdm::shared_ptr<const Foo> > TConstFooVector;

TEST ( test_shared_ptr ) {
	TFooVector v;

	bdm::shared_ptr<Foo> zero;
	CHECK_EQUAL ( 0l, zero.use_count() );
	bdm::shared_ptr<Foo> z2 ( zero );
	CHECK_EQUAL ( 0l, z2.use_count() );

	bdm::shared_ptr<Foo> one ( new Foo ( 1 ) );
	v.push_back ( one );
	CHECK ( !one.unique() );

	v.push_back ( one );
	v.push_back ( bdm::shared_ptr<Foo> ( new Foo ( 2 ) ) );
	CHECK_EQUAL ( static_cast<TFooVector::size_type> ( 3 ), v.size() );

	CHECK ( v[0] == v[1] );
	CHECK ( v[1] != v[2] );

	Foo c ( * ( v[0] ) );
	CHECK_EQUAL ( 1, c.get_x() );

	Foo *p = v[1].get();
	CHECK_EQUAL ( 1, p->get_x() );
	p->set_x ( 12 );
	CHECK_EQUAL ( 12, one->get_x() );

	CHECK ( v[2].unique() );
	CHECK_EQUAL ( 2, v[2]->get_x() );
}

TEST ( test_shared_ptr_const ) {
	TConstFooVector v;

	bdm::shared_ptr<const Foo> zero;
	CHECK_EQUAL ( 0l, zero.use_count() );
	bdm::shared_ptr<const Foo> z2 ( zero );
	CHECK_EQUAL ( 0l, z2.use_count() );

	bdm::shared_ptr<const Foo> one ( new Foo ( 1 ) );
	v.push_back ( one );
	CHECK ( !one.unique() );

	v.push_back ( one );
	v.push_back ( bdm::shared_ptr<const Foo> ( new Foo ( 2 ) ) );
	CHECK_EQUAL ( static_cast<TConstFooVector::size_type> ( 3 ), v.size() );

	CHECK ( v[0] == v[1] );
	CHECK ( v[1] != v[2] );

	Foo c ( * ( v[0] ) );
	CHECK_EQUAL ( 1, c.get_x() );

	const Foo *p = v[1].get();
	CHECK_EQUAL ( 1, p->get_x() );

	CHECK ( v[2].unique() );
	CHECK_EQUAL ( 2, v[2]->get_x() );

	bdm::shared_ptr<Foo> non_const;
	bdm::shared_ptr<const Foo> another ( non_const );
	CHECK ( !non_const.unique() );
}

// tested runtime check is only in debug version
#ifndef NDEBUG
TEST ( test_shared_ptr_error ) {
	bdm::shared_ptr<Foo> empty;
	try {
		Foo x(*empty);
		CHECK ( false );
	} catch ( std::runtime_error &exc ) {
		CHECK ( exc.what() );
	}
}
#endif
