#include "ParamPassTest.h"
#include <exception>
#include <utility>
#include <memory>
#include <type_traits>

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION ( ParamPassTest, "bits" );
CPPUNIT_TEST_SUITE_REGISTRATION ( ParamPassTest );

void ParamPassTest::setUp ( ) {
}

void ParamPassTest::tearDown ( ) {
}

int instances = 0;

class FooCloneable {
	int data;

	FooCloneable ( const FooCloneable & foo ) : data ( foo.data ) {
		std::cout << "Copy" << std::endl;

		instances++;
	}

	FooCloneable ( FooCloneable && foo ) : data ( foo.data ) {
		std::cout << "Move" << std::endl;

		instances++;
	}

public:
	FooCloneable ( int data ) : data ( data ) {
		std::cout << "Constructor" << std::endl;

		instances++;
	}

	~FooCloneable ( ) {
		std::cout << "Destructor" << std::endl;

		instances--;
	}

	FooCloneable * clone ( ) const {
		return new FooCloneable ( * this );
	}

	int getData ( ) const {
		return data;
	}

};

class FooCopyable {
	int data;

public:
	FooCopyable ( int data ) : data ( data ) {
		std::cout << "Constructor" << std::endl;

		instances++;
	}

	FooCopyable ( const FooCopyable & foo ) : data ( foo.data ) {
		std::cout << "Copy" << std::endl;

		instances++;
	}

	FooCopyable ( FooCopyable && foo ) : data ( foo.data ) {
		std::cout << "Move" << std::endl;

		instances++;
	}

	~FooCopyable ( ) {
		std::cout << "Destructor" << std::endl;

		instances--;
	}

	FooCopyable * clone ( ) const {
		return new FooCopyable ( * this );
	}

	int getData ( ) const {
		return data;
	}

};

int dest ( FooCloneable && foo ) {
	std::cout << "Destination called" << std::endl;

	if ( instances < 2 )
		throw std::exception ( );

	return foo.getData ( );
}

int dest ( FooCopyable && foo ) {
	std::cout << "Destination called" << std::endl;

	if ( instances < 2 )
		throw std::exception ( );

	return foo.getData ( );
}

int test ( FooCloneable && foo ) {
	return dest ( std::move ( foo ) );
}

int test ( const FooCloneable & foo ) {
	return test ( std::move_copy ( foo ) );
}

int test ( FooCopyable && foo ) {
	return dest ( std::move ( foo ) );
}

int test ( const FooCopyable & foo ) {
	return test ( std::move_copy ( foo ) );
}

void ParamPassTest::testParameterPassing ( ) {
	{
		const FooCloneable foo ( 1 );
		int res = 0;
		CPPUNIT_ASSERT_NO_THROW ( res = test ( foo ) );
		CPPUNIT_ASSERT ( res == 1 && instances == 1 );
	}
	{
		const FooCopyable foo ( 1 );
		int res = 0;
		CPPUNIT_ASSERT_NO_THROW ( res = test ( foo ) );
		CPPUNIT_ASSERT ( res == 1 && instances == 1 );
	}
}