/**
 * An example of creating a concrete object factory which returns shared 
 * pointers to pooled resources.  The code takes advantage of shared_ptr and 
 * weak_ptr, which are scheduled for inclusion in C++ TR1.  
 *
 * This code compiles under gcc4.
 *
 * This code falls into the do-whatever-you-want-with-it category.
 * 
 * Cheers,
 *     Niek Sanders
 *     http://www.cis.rit.edu/~njs8030/
 * 
 **/
#include <tr1/memory>
#include <map>
#include <iostream>

struct Resource { 
    double id; 
    Resource( double myId ) : id( myId ) {}
    bool operator<( const Resource& rhs ) { return ( this->id < rhs.id ); }
};


class PooledFactory {

    public:
        typedef std::tr1::shared_ptr<Resource> SharedResPtr;
        typedef std::tr1::weak_ptr<Resource> WeakResPtr;

    private:
        typedef std::map< double, WeakResPtr > ObjectPool;
        mutable ObjectPool objectPool;

    public:

        /*
         * Default ctor
         */
        PooledFactory() : objectPool() {};


        /*
         * Dump a list of all the weak ptrs in our pool.  Note that some may 
         * point to resources that are no longer allocated.
         */
        void dumpPool() const {
            ObjectPool::const_iterator ending = objectPool.end();
            for ( ObjectPool::const_iterator it = objectPool.begin(); 
                  it != ending; 
                  ++it ) {
                std::cout << it->first << " --> ";
                std::cout << it->second.use_count() << '\n';
            }
            std::cout << std::flush;
        }


        /*
         * Creates a resource for a given type.  If the type id already exists, 
         * than a shared handle is returned.
         */
        SharedResPtr getResource( double id ) const {

            // pulls existing weak pointer or makes new (default ctor)
            WeakResPtr& wPtr = objectPool[ id ];

            // create shared ptr from potentially empty object
            SharedResPtr pooledObject = wPtr.lock();
            
            // create a new allocated resource if needed
            if ( pooledObject == 0 ) {
                pooledObject.reset( new Resource( id ) );
                wPtr = pooledObject;
            }

            return pooledObject;
            
        }

};


/*
 * When the scope of this function expires, the shared pointers will dealloc the
 * resource they point to.  The weak point in the pool will now have a count of 
 * zero.
 */
void doIt( PooledFactory& factory ) {

    PooledFactory::SharedResPtr shared1 = factory.getResource( 2.0 );
    PooledFactory::SharedResPtr shared2 = factory.getResource( 2.0 );
    PooledFactory::SharedResPtr shared3 = factory.getResource( 3.5 );

    std::cout << "Pool after 2 shared, 1 unique created" << std::endl;
    factory.dumpPool();

}


int main( int argc, char* argv[] ) {

    PooledFactory factory;
    doIt( factory );

    std::cout << "Pool after all out of scope" << std::endl;
    factory.dumpPool();

    std::cout << "Pool after shared ptr to expired (recreated) resource" 
              << std::endl;
    PooledFactory::SharedResPtr neoShare = factory.getResource( 2.0 );
    factory.dumpPool();

    return 0;
}


