/*****************************************************************************
 * $Id: Ptr.cc,v 1.5 2006/08/10 03:18:58 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Test of Ptr<> template class.
 *//*
 * LEGAL:	COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// This test is also about TESTING COMPILE-ERRORS will happen.
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#ifndef TEST_COMPILE_ERRORS
#define TEST_COMPILE_ERRORS  0
#endif

#define TESTING 1
#include "test.hh"
#include "gfx_types.hh"

////////////////////////////////////////////////////////////////////////////////
// Test class.
class Object
{
public:
    Object( void ) : mVal(0)      { ++mInstanceCnt; }
    Object( int val ) : mVal(val) { ++mInstanceCnt; }
	virtual ~Object()             { --mInstanceCnt; }
    virtual void Cnt( void ) { ++mObjectCnt; }
	virtual string GetName( void ) { return string("Object"); }
	int Get( void ) const { return mVal; }
    int mVal;
	static int mInstanceCnt; // inc/dec by ctor/dtor
    static int mObjectCnt;   // incremented by Cnt() (not instance cnt)
};

class Derived : public Object
{
public:
    Derived( void ) : Object(0)      { ++mInstanceCnt; }
    Derived( int val ) : Object(val) { ++mInstanceCnt; }
	virtual ~Derived()               { --mInstanceCnt; }
    virtual void Cnt( void ) { ++mDerivedCnt; }
	virtual string GetName( void ) { return string("Derived"); }
	static int mInstanceCnt; // inc/dec by ctor/dtor
    static int mDerivedCnt;   // incremented by Cnt() (not instance cnt)
};

int Object::mInstanceCnt;
int Object::mObjectCnt;
int Derived::mInstanceCnt;
int Derived::mDerivedCnt;

/*****************************************************************************
 * 
 *****************************************************************************/
Object* ReturnRawPointer( void )
{
	return new Object;
}

/*****************************************************************************
 * 
 *****************************************************************************/
Ptr<Object> ReturnPtrExplicit( void )
{
	return Ptr<Object>( new Object );
}
Ptr<Object> ReturnPtrImplicit( void )
{
	return new Object;
}

/*****************************************************************************
 * 
 *****************************************************************************/
int GetInt( Ptr<Object> obj )
{
//	delete obj;  // the beauty of the Ptr<> class is that such ownership bugs won't compile!

	return obj->mVal;
}

/*****************************************************************************
 * Test
 *****************************************************************************/
namespace gfx {
bool
TestPtr( void )
{
 //-----------------------------------------------------------------------------
 // Test if code compiles.
 {
    Ptr<Object> ptr( new Object );
    ptr = NULL;
    ptr = new Object;
    GetInt( ptr );
    GetInt( new Object );  // implicit type conversion from Object* to Ptr<Object>

	Ptr<Object> ptr1 = ReturnPtrExplicit();
	Ptr<Object> ptr2 = ReturnPtrImplicit();

    Ptr<Object> ptrnull;
    if ( ptrnull.mObj == NULL  // both sides
      && NULL == ptrnull.mObj
      && GET_PTR(ptrnull) == reinterpret_cast<Object*>(NULL)
      && reinterpret_cast<Object*>(NULL) == GET_PTR(ptrnull) )
    {
        cerr << "ok: default Ptr nullified" << endl;
    }
    else
    {
        cerr << "ERROR: default Ptr nullfied" << endl;
		Fail();
    }

	// Ptr to const object
	const Object* objc = new Object;
    Ptr<const Object> ptrc;  ptrc = objc;
 }

 //-----------------------------------------------------------------------------
 // Test accessing object.
 {
    Ptr<Object> ptr( new Object(12345) );
	if ( ptr->mVal == 12345
      && ptr->Get() == 12345 )
    {
        cerr << "ok: operator->()" << endl;
    }
    else
    {
        cerr << "ERROR: operator->()" << endl;
		Fail();
    }

	Ptr<Derived> ptrDerived = new Derived( 888 );
	if (  ptrDerived->mVal == 888
       && ptrDerived->GetName() == string("Derived") )
	{
        cerr << "ok: operator->(Derived)" << endl;
	}
	 else
	{
        cerr << "ERROR: operator->(Derived)" << endl;
		Fail();
	}
 }

 //-----------------------------------------------------------------------------
 // Test operator*() (dereference)
 {
    Ptr<Object> ptr( new Object(999) );

	Object& obj = *ptr; // dereference

	if ( obj.mVal == 999 )
    {
        cerr << "ok: operator*() dereference" << endl;
    }
    else
    {
        cerr << "ERROR: operator*() dereference" << endl;
		Fail();
    }

 }

 //-----------------------------------------------------------------------------
 // Ensure Ptr<> doesn't delete actual object.
 {
	Object::mInstanceCnt = 0;
    Ptr<Object> ptr = new Object;
 }
 // Out-of-scope, Ptr<> destroyed.
 {
	if ( Object::mInstanceCnt == 1 )
    {
        cerr << "ok: Ptr<> didn't destroy object" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr<> didn't destroy object " << Object::mInstanceCnt << endl;
		Fail();
    }
 }

 //-----------------------------------------------------------------------------
 // Test Ptr::operator==()
 {
    Object* obj = new Object;
    Ptr<Object> ptr = obj;
    if ( ptr == ptr )
    {
        cerr << "ok: Ptr::operator==(ptr,ptr)" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr::operator==(ptr,ptr)" << endl;
		Fail();
    }

    if ( ptr == obj && obj == ptr ) // both sides
    {
        cerr << "ok: Ptr::operator==(ptr,obj)" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr::operator==(ptr,obj)" << endl;
		Fail();
    }

    Ptr<Object> ptr2 = ptr;  // copy constructor
    if ( ptr2 == obj && obj == ptr2 )  // both sides
    {
        cerr << "ok: Ptr::operator==(ptr2,obj)" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr::operator==(ptr2,obj)" << endl;
		Fail();
    }

    if ( ptr2 == ptr && ptr2 == ptr ) // both sides
    {
        cerr << "ok: Ptr::operator==(ptr2,ptr)" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr::operator==(ptr2,ptr1)" << endl;
		Fail();
    }
 }

 //-----------------------------------------------------------------------------
 // Test Ptr::operator!=()
 {
	Object* obj1 = new Object;
    Object* obj2 = new Object;
	Ptr<Object> ptr1 = obj1;
	Ptr<Object> ptr2( obj2 );
    if ( ptr1 != ptr2 && ptr2 != ptr1 )  // both sides
    {
        cerr << "ok: Ptr::operator!=(ptr1,ptr2)" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr::operator!=(ptr1,ptr2)" << endl;
		Fail();
    }

    if ( ptr2 != obj1 && obj1 != ptr2 )  // both sides
    {
        cerr << "ok: Ptr::operator!=(obj1,ptr2)" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr::operator!=(obj1,ptr2)" << endl;
		Fail();
    }
 }

 //-----------------------------------------------------------------------------
 // Test Ptr<> default ctor.
 {
    Ptr<Object> ptrDefault;
	Ptr<Object> ptrDefault2 = ptrDefault;
    if ( ptrDefault == NULL && ptrDefault2 == NULL && ptrDefault == ptrDefault2 )
    {
        cerr << "ok: Ptr<> default" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr<> default" << endl;
		Fail();
    }
 }

 //-----------------------------------------------------------------------------
 // Test Ptr::operator=(T* obj)
 {
    Object* obj1 = new Object;
    Object* obj2 = new Object;

    Ptr<Object> ptr1;
	Ptr<Object> ptr2;

	ptr1 = obj1;
	ptr2 = obj2;

    if ( ptr1 == obj1 && ptr2 == obj2
      && obj1 == ptr1 && obj2 == ptr2 ) // swap for both sides 
    {
        cerr << "ok: Ptr::operator=(T* obj)" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr::operator=(T* obj)" << endl;
		Fail();
    }
 }

 //-----------------------------------------------------------------------------
 // Test GET_PTR.
 {
    Object* obj1a = new Object;
    Ptr<Object> ptr( obj1a );
    Object* obj1b = GET_PTR( ptr );
    if ( obj1a == obj1b )
    {
        cerr << "ok: GET_PTR()" << endl;
    }
    else
    {
        cerr << "ERROR: GET_PTR()" << endl;
		Fail();
    }
 }

 //-----------------------------------------------------------------------------
 // Test assigning Ptrs of different types.
 // Try to test the assignment by inferring which object the Ptr really points to.
 //
 // Test: Ptr<Base> = pBase;
 {
//  Object* base = new Object;
    Derived* derived = new Derived;
    Derived* derived2 = new Derived;

    Object::mObjectCnt = Derived::mDerivedCnt = 0;
    Ptr<Object> ptrBase = derived;             // this calls ctor, not operator=()
    Ptr<Object> ptrBase2; ptrBase2 = derived;  // this calls operator=()
    ptrBase->Cnt();  // this is confusing: really calls Derived::Cnt()
    if ( Object::mObjectCnt == 0
      && Derived::mDerivedCnt == 1
      && ptrBase == derived  && derived == ptrBase      // test Ptr<>::operator==()
      && ptrBase != derived2 && derived2 != ptrBase     // test Ptr<>::operator!=()
      && ptrBase2 == derived && derived == ptrBase2     // test Ptr<>::operator==()
      && ptrBase2 != derived2 && derived2 != ptrBase2 )  // test Ptr<>::operator!=()
    {
        cerr << "ok: Ptr<Base> = pDerived;" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr<Base> = pDerived;" << endl;
		Fail();
    }
 }

 //-----------------------------------------------------------------------------
 // Test: Ptr<Base> = Ptr<Derived>;
 {
    Ptr<Object>  ptrBase    = new Object;
    Ptr<Object>  ptrBase2   = new Object;
    Ptr<Derived> ptrDerived = new Derived;
    Ptr<Derived> ptrDerived2 = new Derived;

    Object::mObjectCnt = Derived::mDerivedCnt = 0;

    ptrBase = ptrDerived;  // Ptr<Base> = Ptr<Derived>

    ptrBase->Cnt();  // this is confusing: really calls Derived::Cnt()

    if ( Object::mObjectCnt == 0
      && Derived::mDerivedCnt == 1
      && ptrBase == ptrDerived && ptrDerived == ptrBase  // test Ptr<>::operator==(), address comparison of same object but different related type
      && ptrDerived != ptrDerived2 && ptrDerived2 != ptrDerived  // test Ptr<>::operator!=()
      && ptrBase2 != ptrDerived2 && ptrDerived2 != ptrBase2
      && ptrDerived2 == ptrDerived2 )
    {
        cerr << "ok: Ptr<Base> = Ptr<Derived>;" << endl;
    }
    else
    {
        cerr << "ERROR: Ptr<Base> = Ptr<Derived>;" << endl;
		Fail();
    }
 }

//####################################################################
#if TEST_COMPILE_ERRORS
 {
    // THESE SHOULD NOT COMPILE!
    // If these do, the Ptr<> class is FLAWED!
    // A pointer to a derived object can't point to a base object (slice pitfall).

    Ptr<Object>  ptrBase    = new Object;
    Ptr<Derived> ptrDerived = new Derived;

    ptrDerived = ptrBase;				// !!! COMPILE ERROR !!!
	Ptr<Derived> badDerived( ptrBase );	// !!! COMPILE ERROR !!! Ptr<> ctor shouldn't compile!
    ptrBase = ptrDerived;  // ok

	Object* base = ptrBase;				// !!! COMPILE ERROR !!! no automatic conversion from Ptr<*> to T*
 }
 {
    Object* base = new Object;
    Ptr<Derived> ptrDerived = base;		// !!! COMPILE ERROR !!!
    Ptr<Object>  ptrBase = new Derived; // ok
 }
 {
    const Ptr<Object> ptrc = new Object;
    ptrc = new Object;					// !!! COMPILE ERROR !!!  const Ptr can't be reassigned
 }
#endif
//####################################################################

	return true;
}

} // namespace gfx

/*****************************************************************************
 * 
 *****************************************************************************/
int main( int argc, char** argv )
{
	GFX_INIT( argv[0] );

	int res = 1;
	if ( gfx::TestPtr() )
	{
		cerr << endl << "Test PASSED. \n";
		res = 0;
	}
	else
	{
		cerr << endl << "Test FAILED! \n";
		res = 1;
	}
	GFX_CLEANUP();

    cerr << endl;
    cerr << "---------------------------------------------------" << endl;
    cerr << "NOTE: This test is also for TESTING COMPILE ERRORS." << endl;
    cerr << "Test compiling with '#define TEST_COMPILE_ERRORS 1'" << endl;
    cerr << "to catch if the C++ compiler allows bad Ptr<> code." << endl;
    cerr << "make TEST_COMPILE_ERRORS=1"                          << endl;
    cerr << "---------------------------------------------------" << endl;
    cerr << endl;

	exit( res );
}
