#include <iostream>
#include <map>
#include <boost/smart_ptr.hpp>

using namespace std;
using namespace boost;

// Both handles and objects must use this interface
class ObjectInterface
{
public:
    virtual void move(void) = 0;

};

// Implementation of an object
class Object : ObjectInterface
{
public:
    void move(void)
    {
        cout << mName << " is moving" << endl;
    }

    inline const string& getName(void) { return mName; }

    Object(const string& name) : mName(name) { cout << mName << " created" << endl; }
    virtual ~Object() { cout << mName << " destroyed" << endl; }

protected:
    string mName;

};

typedef map<string, shared_ptr<Object> > ObjectMap;

// Handle to an object.  Allows ObjectManager to take sole ownership of Objects, while allowing
// the user to have access to the Objects.  As an added effect, it also handles the case where
// the user tries to access an Object that doesn't exist.
class ObjectHandle : ObjectInterface
{
public:
    void move(void)
    {
        if (!mObject.expired())
            mObjectRaw->move();
        else
            cout << "Can't move " << mObjectName << " as it not longer exists" << endl;
    };

    // The handle keeps a raw pointer to the object to prevent it having to call mObject.lock(),
    // and therefore create a new shared_ptr, each time it accesses the object.
    ObjectHandle(const shared_ptr<Object>& object) :
        mObject(object), mObjectRaw(mObject.lock().get()), mObjectName(mObjectRaw->getName())
    {
        cout << "Handle to " << mObjectName << " created" << endl;
    }

    virtual ~ObjectHandle() { cout << "Handle to " << mObjectName << " destroyed" << endl; }

protected:
    weak_ptr<Object> mObject;
    Object* mObjectRaw;
    string mObjectName; // Store local copy of the object's name, for convenience

};

// This example doesn't really need a factory, but why not include one ;).
class ObjectFactory
{
public:
    virtual shared_ptr<Object> createObject(const string& name)
    {
        shared_ptr<Object> object(new Object(name));
        return object;
    }

    virtual ~ObjectFactory() { }

};

// The manager keeps a map of all objects by name.  The user creates and
// destroys Objects via the manager.  The manager creates and object and
// passes back a handle to the user.
class ObjectManager
{
public:
    shared_ptr<ObjectHandle> createObject(const string& name)
    {
        // Create actual object and store in map
        shared_ptr<Object> object = mFactory.createObject(name);
        mObjects[name] = object;

        // Create handle to object and return
        shared_ptr<ObjectHandle> handle(new ObjectHandle(object));
        return handle;
    }

    shared_ptr<Object> createObjectBad(const string& name)
    {
        // Create actual object and store in map
        shared_ptr<Object> object = mFactory.createObject(name);
        mObjects[name] = object;

        return object;
    }

    void destroyObject(const string& name)
    {
        mObjects.erase(name);
    }


protected:
    ObjectMap mObjects;
    ObjectFactory mFactory;

};

int main()
{
    scoped_ptr<ObjectManager> mgr(new ObjectManager);

    // Create chair and move it.
    shared_ptr<ObjectHandle> chair = mgr->createObject("Chair");
    chair->move();

    // Destroy chair and try to move it.
    // Handle means object IS destroyed, and also prevents code from crashing.
    mgr->destroyObject("Chair");
    chair->move();

    // Create table and get non-handle reference back.
    // Now we also own the object.
    shared_ptr<Object> table = mgr->createObjectBad("Table");
    table->move();

    // Destroy chair.  It will still exist because the manager was not the sole owner.
    // Moving the object still works, but we don't want it to, because it shouldn't exist!
    mgr->destroyObject("Chair");
    table->move();

    cout << "END" << endl;
}
