/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Test of SafePtr<> template class.
 *//*
 * LEGAL:   COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// 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 "base/module.hh"
#include "base/types.hh"
using namespace base;

////////////////////////////////////////////////////////////////////////////////
// Test class.
class Object
{
public:
    Object( void ) : mVal(0)      { ++msInstanceCount; }
    Object( int val ) : mVal(val) { ++msInstanceCount; }
    virtual ~Object()             { --msInstanceCount; }
    virtual void Count( void ) { ++msObjectCount; }
    virtual string GetName( void ) { return string("Object"); }
    int Get( void ) const { return mVal; }
    int           mVal;
    CLASS_VAR int msInstanceCount; // inc/dec by ctor/dtor
    CLASS_VAR int msObjectCount;   // incremented by Count() (not instance count)
};

class Derived : public Object
{
public:
    Derived( void ) : Object(0)      { ++msInstanceCount; }
    Derived( int val ) : Object(val) { ++msInstanceCount; }
    virtual ~Derived()               { --msInstanceCount; }
    virtual void Count( void ) { ++msDerivedCount; }
    virtual string GetName( void ) { return string("Derived"); }
    CLASS_VAR int msInstanceCount; // inc/dec by ctor/dtor
    CLASS_VAR int msDerivedCount;  // incremented by Count() (not instance count)
};

int Object::msInstanceCount;
int Object::msObjectCount;
int Derived::msInstanceCount;
int Derived::msDerivedCount;

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

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

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

    return obj->mVal;
}

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

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

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

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

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

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

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

    Object& obj = *ptr; // dereference

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

 }

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

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

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

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

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

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

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

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

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

    SafePtr<Object> ptr1( NULL );
    SafePtr<Object> ptr2( NULL );

    ptr1 = obj1;
    ptr2 = obj2;

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

 //-----------------------------------------------------------------------------
 // Test SafePtr = SafePtr
 {
    CDEBUG << "Testing SafePtr = SafePtr assignment." << std::endl;
    {
    Object* obj1 = new Object;
    SafePtr<Object> ptr1 = obj1;
    SafePtr<Object> ptr2 = ptr1;
    if ( ptr1 != ptr2 ) return Fail();
    if ( ptr1.PTR() != ptr2.PTR() ) return Fail();
    }

    {
    SafePtr<Object> ptr1 = new Object;
    SafePtr<Object> ptr2 = new Object;  // different
    if ( ptr1 == ptr2 ) return Fail();
    if ( ptr1.PTR() == ptr2.PTR() ) return Fail();
    }
 }

 //-----------------------------------------------------------------------------
 // Test PTR()
 {
    Object* obj1a = new Object;
    SafePtr<Object> ptr( obj1a );
    Object* obj1b = ptr.PTR();
    if ( obj1a == obj1b )
    {
        CDEBUG << "ok: PTR()" << std::endl;
    }
    else
    {
        CDEBUG << "ERROR: PTR()" << std::endl;
        return Fail();
    }
 }

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

    Object::msObjectCount = Derived::msDerivedCount = 0;
    SafePtr<Object> ptrBase = derived;             // this calls ctor, not operator=()
    SafePtr<Object> ptrBase2( NULL ); ptrBase2 = derived;  // this calls operator=()
    ptrBase->Count();  // this is confusing: really calls Derived::Count()
    if ( Object::msObjectCount == 0
      && Derived::msDerivedCount == 1
      && ptrBase == derived  && derived == ptrBase      // test SafePtr<>::operator==()
      && ptrBase != derived2 && derived2 != ptrBase     // test SafePtr<>::operator!=()
      && ptrBase2 == derived && derived == ptrBase2     // test SafePtr<>::operator==()
      && ptrBase2 != derived2 && derived2 != ptrBase2 )  // test SafePtr<>::operator!=()
    {
        CDEBUG << "ok: SafePtr<Base> = pDerived;" << std::endl;
    }
    else
    {
        CDEBUG << "ERROR: SafePtr<Base> = pDerived;" << std::endl;
        return Fail();
    }
 }

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

    Object::msObjectCount = Derived::msDerivedCount = 0;

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

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

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

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

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

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

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

    return true;
}

/*****************************************************************************
 * 
 *****************************************************************************/
int main( int argc, char** argv )
{
    CDEBUG << std::endl;
    CDEBUG << "-------------------------------------------------------" << std::endl;
    CDEBUG << "NOTE: This test is also for TESTING COMPILE ERRORS."     << std::endl;
    CDEBUG << "Test compiling with '#define TEST_COMPILE_ERRORS 1'"     << std::endl;
    CDEBUG << "to catch if the C++ compiler allows bad SafePtr<> code." << std::endl;
    CDEBUG << "make TEST_COMPILE_ERRORS=1"                              << std::endl;
    CDEBUG << "-------------------------------------------------------" << std::endl;
    CDEBUG << std::endl;

    RUN_TEST( "SafePtr", TEST_ERROR_MODE_ABORT,
              TestSafePtr() )
}
