/*
   (c) Copyright 2001-2011  The world wide DirectFB Open Source Community (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              Claudio Ciccani <klan@users.sf.net>.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

//#define DIRECT_ENABLE_DEBUG

#include <config.h>

#include <algorithm>

extern "C" {
#include <direct/debug.h>
#include <direct/mem.h>
#include <direct/memcpy.h>
#include <direct/messages.h>
#include <direct/thread.h>
#include <direct/util.h>

#include <voodoo/conf.h>
#include <voodoo/internal.h>
#include <voodoo/link.h>
}

#include <voodoo/connection_packet.h>
#include <voodoo/connection_raw.h>
#include <voodoo/dispatcher.h>
#include <voodoo/manager.h>
#include <voodoo/packet.h>


//namespace Voodoo {

D_DEBUG_DOMAIN( Voodoo_Dispatch, "Voodoo/Dispatch", "Voodoo Dispatch" );
D_DEBUG_DOMAIN( Voodoo_Manager,  "Voodoo/Manager",  "Voodoo Manager" );

/**********************************************************************************************************************/

VoodooManager::VoodooManager( VoodooLink    *link,
                              VoodooContext *context )
     :
     magic(0),
     is_quit(false),
     msg_count(0),
     msg_serial(0)
{
     D_ASSERT( link != NULL );

     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     /* Store link and context */
     this->link    = link;
     this->context = context;


     instances.last   = 0;

     response.current = NULL;


     /* Initialize all locks. */
     direct_recursive_mutex_init( &instances.lock );
     direct_recursive_mutex_init( &response.lock );

     /* Initialize all wait conditions. */
     direct_waitqueue_init( &response.wait_get );
     direct_waitqueue_init( &response.wait_put );

     D_MAGIC_SET( this, VoodooManager );


     dispatcher = new VoodooDispatcher( this );


     /* Add connection */
     if ((link->code & 0x8000ffff) == 0x80008676) {
          D_INFO( "Voodoo/Manager: Connection mode is PACKET\n" );

          connection = new VoodooConnectionPacket( this, link );
     }
     else {
          D_INFO( "Voodoo/Manager: Connection mode is RAW\n" );

          connection = new VoodooConnectionRaw( this, link );

          // FIXME: query manager dynamically for compression instead
          voodoo_config->compression_min = 0;
     }

     connection->Start();
}

static void
instance_iterator( std::pair<VoodooInstanceID,VoodooInstance*> pair )
{
     D_DEBUG_AT( Voodoo_Manager, "%s( id %u, instance %p )\n", __func__, pair.first, pair.second );

     pair.second->Release();
}

VoodooManager::~VoodooManager()
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     D_MAGIC_ASSERT( this, VoodooManager );

     if (!is_quit)
          quit();

     connection->Stop();

     /* Destroy dispatcher */
     delete dispatcher;

     /* Remove connection */
     delete connection;

     /* Destroy conditions. */
     direct_waitqueue_deinit( &response.wait_get );
     direct_waitqueue_deinit( &response.wait_put );

     /* Destroy locks. */
     direct_mutex_deinit( &instances.lock );
     direct_mutex_deinit( &response.lock );

     /* Release all remaining interfaces. */
     std::for_each( instances.remote.begin(), instances.remote.end(), instance_iterator );
     std::for_each( instances.local.begin(), instances.local.end(), instance_iterator );

     D_MAGIC_CLEAR( this );
}

/**********************************************************************************************************************/

void
VoodooManager::DispatchPacket( VoodooPacket *packet )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p, packet %p )\n", __func__, this, packet );

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSUME( !is_quit );

     if (is_quit)
          return;

     dispatcher->PutPacket( packet );
}

bool
VoodooManager::DispatchReady()
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     D_MAGIC_ASSERT( this, VoodooManager );
//     D_ASSUME( !is_quit );

     if (is_quit)
          return false;

     return dispatcher->Ready();
}

/**********************************************************************************************************************/

void
VoodooManager::quit()
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSUME( !is_quit );

     if (is_quit)
          return;

     /* Have all threads quit upon this. */
     is_quit = true;

     /* Acquire locks and wake up waiters. */
     direct_mutex_lock( &response.lock );
     direct_waitqueue_broadcast( &response.wait_get );
     direct_waitqueue_broadcast( &response.wait_put );
     direct_mutex_unlock( &response.lock );
}

void
VoodooManager::handle_disconnect()
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     D_MAGIC_ASSERT( this, VoodooManager );

     D_DEBUG_AT( Voodoo_Manager, "  -> Remote site disconnected from manager at %p!\n", this );

     quit();
}

void
VoodooManager::handle_super( VoodooSuperMessage *super )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     DirectResult  ret;
     const char   *name;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( super != NULL );
     D_ASSERT( super->header.size >= (int) sizeof(VoodooSuperMessage) );
     D_ASSERT( super->header.type == VMSG_SUPER );

     name = (const char *) (super + 1);

     D_DEBUG_AT( Voodoo_Dispatch, "  -> Handling SUPER message %llu for '%s' (%d bytes).\n",
                 (unsigned long long)super->header.serial, name, super->header.size );

     VoodooInstanceID instance_id;

     ret = context->HandleSuper( this, name, &instance_id );
     if (ret)
          do_respond( true, super->header.serial, ret );
     else
          do_respond( true, super->header.serial, DR_OK, instance_id );
}

typedef struct {
     VoodooManager        *manager;
     VoodooInstance       *instance;
     VoodooRequestMessage *request;
} DispatchAsyncContext;

void *
VoodooManager::dispatch_async_thread( DirectThread *thread,
                                      void         *arg )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, arg );

     DirectResult          ret;
     DispatchAsyncContext *context  = (DispatchAsyncContext*) arg;
     VoodooManager        *manager  = context->manager;
     VoodooInstance       *instance = context->instance;
     VoodooRequestMessage *request  = context->request;

     ret = instance->Dispatch( manager, request );

     if (ret && (request->flags & VREQ_RESPOND))
          manager->do_respond( true, request->header.serial, ret );

     D_FREE( context );

     return NULL;
}

void
VoodooManager::handle_request( VoodooRequestMessage *request )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     DirectResult    ret;
     VoodooInstance *instance;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( request != NULL );
     D_ASSERT( request->header.size >= (int) sizeof(VoodooRequestMessage) );
     D_ASSERT( request->header.type == VMSG_REQUEST );

     D_DEBUG_AT( Voodoo_Dispatch, "  -> Handling REQUEST message %llu to %u::%u %s%s(%d bytes).\n",
                 (unsigned long long)request->header.serial, request->instance, request->method,
                 (request->flags & VREQ_RESPOND) ? "[RESPONDING] " : "",
                 (request->flags & VREQ_ASYNC) ? "[ASYNC] " : "",
                 request->header.size );

     direct_mutex_lock( &instances.lock );

     InstanceMap::iterator itr = instances.local.find( request->instance );

     if (itr == instances.local.end()) {
          direct_mutex_unlock( &instances.lock );

          D_ERROR( "Voodoo/Dispatch: "
                   "Requested instance %u doesn't exist (anymore)!\n", request->instance );

          if (request->flags & VREQ_RESPOND)
               do_respond( true, request->header.serial, DR_NOSUCHINSTANCE );

          return;
     }

     instance = (*itr).second;

     if (request->flags & VREQ_ASYNC) {
          DirectThread         *thread;
          DispatchAsyncContext *context;

          context = (DispatchAsyncContext*) D_MALLOC( sizeof(DispatchAsyncContext) + request->header.size );
          if (!context) {
               D_WARN( "out of memory" );
               direct_mutex_unlock( &instances.lock );
               return;
          }

          context->manager  = this;
          context->instance = instance;
          context->request  = (VoodooRequestMessage*) (context + 1);

          direct_memcpy( context->request, request, request->header.size );

          thread = direct_thread_create( DTT_DEFAULT, dispatch_async_thread, context, "Voodoo Async" );
          direct_thread_detach( thread );
          // FIXME: free thread?
     }
     else {
          ret = instance->Dispatch( this, request );

          if (ret && (request->flags & VREQ_RESPOND))
               do_respond( true, request->header.serial, ret );
     }

     direct_mutex_unlock( &instances.lock );
}

void
VoodooManager::handle_response( VoodooResponseMessage *msg )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( msg != NULL );
     D_ASSERT( msg->header.size >= (int) sizeof(VoodooResponseMessage) );
     D_ASSERT( msg->header.type == VMSG_RESPONSE );
     D_ASSERT( msg->request < msg_serial );

     D_DEBUG_AT( Voodoo_Dispatch, "  -> Handling RESPONSE message %llu (%s) with instance %u for request "
                 "%llu (%d bytes).\n", (unsigned long long)msg->header.serial, DirectResultString( msg->result ),
                 msg->instance, (unsigned long long)msg->request, msg->header.size );

     direct_mutex_lock( &response.lock );

     D_ASSERT( response.current == NULL );

     response.current = msg;

     direct_mutex_unlock( &response.lock );


     direct_waitqueue_broadcast( &response.wait_get );

     direct_mutex_lock( &response.lock );

     while (response.current && !is_quit)
          direct_waitqueue_wait( &response.wait_put, &response.lock );

     direct_mutex_unlock( &response.lock );
}

/**************************************************************************************************/

DirectResult
VoodooManager::lock_response( VoodooMessageSerial     request,
                              VoodooResponseMessage **ret_response )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     VoodooResponseMessage *msg = NULL;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( ret_response != NULL );

     D_DEBUG_AT( Voodoo_Manager, "  -> Locking response to request %llu...\n", (unsigned long long)request );

     direct_mutex_lock( &response.lock );

     while (!is_quit) {
          msg = response.current;
          if (msg && msg->request == request)
               break;

          if (msg)
               D_DEBUG_AT( Voodoo_Manager, "  -> ...current response is for request %llu...\n", (unsigned long long)msg->request );

          D_DEBUG_AT( Voodoo_Manager, "  -> ...(still) waiting for response to request %llu...\n", (unsigned long long)request );

          direct_waitqueue_wait( &response.wait_get, &response.lock );
     }

     if (is_quit) {
          D_ERROR( "Voodoo/Manager: Quit while waiting for response!\n" );
          direct_mutex_unlock( &response.lock );
          return DR_DESTROYED;
     }

     D_DEBUG_AT( Voodoo_Manager, "  -> ...locked response %llu to request %llu (%d bytes).\n",
                 (unsigned long long)msg->header.serial, (unsigned long long)request, msg->header.size );

     *ret_response = msg;

     return DR_OK;
}

DirectResult
VoodooManager::unlock_response( VoodooResponseMessage *msg )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( msg != NULL );
     D_ASSERT( msg == response.current );

     D_DEBUG_AT( Voodoo_Manager, "  -> Unlocking response %llu to request %llu (%d bytes)...\n",
                 (unsigned long long)msg->header.serial, (unsigned long long)msg->request, msg->header.size );

     response.current = NULL;

     direct_mutex_unlock( &response.lock );

     direct_waitqueue_broadcast( &response.wait_put );

     return DR_OK;
}




DirectResult
VoodooManager::do_super( const char       *name,
                         VoodooInstanceID *ret_instance )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     DirectResult           ret;
     int                    len;
     int                    size;
     VoodooPacket          *packet;
     VoodooMessageSerial    serial;
     VoodooSuperMessage    *msg;
     VoodooResponseMessage *response;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( name != NULL );
     D_ASSERT( ret_instance != NULL );

     if (is_quit) {
          D_DEBUG_AT( Voodoo_Manager, "  -> QUIT!\n" );
          return DR_IO;
     }

     /* Calculate the total message size. */
     len  = strlen( name ) + 1;
     size = sizeof(VoodooSuperMessage) + len;


     /* Lock the output buffer for direct writing. */
     packet = connection->GetPacket( size );
     if (!packet)
          return DR_FAILURE;

     msg = (VoodooSuperMessage*) packet->data_raw();

     serial = msg_serial++;

     /* Fill message header. */
     msg->header.size   = size;
     msg->header.serial = serial;
     msg->header.type   = VMSG_SUPER;

     /* Append the name of the super interface to create. */
     direct_memcpy( msg + 1, name, len );

     D_DEBUG_AT( Voodoo_Manager, "  -> Sending SUPER message %llu for '%s' (%d bytes).\n", (unsigned long long)serial, name, size );

     /* Unlock the output buffer. */
     connection->PutPacket( packet, true );


     /* Wait for and lock the response buffer. */
     ret = lock_response( serial, &response );
     if (ret) {
          D_ERROR( "Voodoo/Manager: "
                   "Waiting for the response failed (%s)!\n", DirectResultString( ret ) );
          return ret;
     }

     D_DEBUG_AT( Voodoo_Manager, "  -> Got response %llu (%s) with instance %u for request %llu "
                 "(%d bytes).\n", (unsigned long long)response->header.serial, DirectResultString( ret ),
                 response->instance, (unsigned long long)response->request, response->header.size );

     ret = response->result;
     if (ret) {
          D_ERROR( "Voodoo/Manager: Could not create remote super interface '%s' (%s)!\n",
                   name, DirectResultString( ret ) );
          unlock_response( response );
          return ret;
     }

     D_INFO( "Voodoo/Manager: Created remote super interface '%s'.\n", name );

     /* Return the new instance ID. */
     *ret_instance = response->instance;

     /* Unlock the response buffer. */
     unlock_response( response );

     return DR_OK;
}

void
VoodooManager::write_blocks( void                     *dst,
                             const VoodooMessageBlock *blocks,
                             size_t                    num )
{
     size_t  i;
     u32    *d32 = (u32*) dst;

     for (i=0; i<num; i++) {
          /* Write block type and length. */
          d32[0] = blocks[i].type;
          d32[1] = blocks[i].len;

          /* Write block content. */
          if (blocks[i].ptr) {
               u32 *s32 = (u32*) blocks[i].ptr;

               switch (blocks[i].len) {
                    case 16:
                         d32[5] = s32[3];
                    case 12:
                         d32[4] = s32[2];
                    case 8:
                         d32[3] = s32[1];
                    case 4:
                         d32[2] = s32[0];
                         break;

                    default:
                         direct_memcpy( &d32[2], blocks[i].ptr, blocks[i].len );
               }
          }
          else if (blocks[i].len) {
               D_ASSERT( blocks[i].len == 4 );

               d32[2] = blocks[i].val;
          }

          /* Advance message data pointer. */
          d32 += 2 + (VOODOO_MSG_ALIGN(blocks[i].len) >> 2);
     }

     /* Write terminator. */
     d32[0] = VMBT_NONE;
}

DirectResult
VoodooManager::do_request( VoodooInstanceID         instance,
                           VoodooMethodID           method,
                           VoodooRequestFlags       flags,
                           VoodooResponseMessage  **ret_response,
                           VoodooMessageBlock      *blocks,
                           size_t                   num_blocks,
                           size_t                   data_size )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     DirectResult          ret;
     size_t                size;
     VoodooPacket         *packet;
     VoodooMessageSerial   serial;
     VoodooRequestMessage *msg;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( instance != VOODOO_INSTANCE_NONE );
     D_ASSERT( ret_response != NULL || !(flags & VREQ_RESPOND) );
     D_ASSUME( (flags & (VREQ_RESPOND | VREQ_QUEUE)) != (VREQ_RESPOND | VREQ_QUEUE) );

     D_DEBUG_AT( Voodoo_Manager, "  -> Instance %u, method %u, flags 0x%08x...\n", instance, method, flags );

     if (is_quit) {
          D_DEBUG_AT( Voodoo_Manager, "  -> QUIT!\n" );
          return DR_IO;
     }

     /* Calculate the total message size. */
     size = sizeof(VoodooRequestMessage) + data_size;

     D_DEBUG_AT( Voodoo_Manager, "  -> complete message size: %d\n", size );

     /* Lock the output buffer for direct writing. */
     packet = connection->GetPacket( size );
     if (!packet)
          return DR_FAILURE;

     msg = (VoodooRequestMessage*) packet->data_raw();

     serial = msg_serial++;

     /* Fill message header. */
     msg->header.size   = size;
     msg->header.serial = serial;
     msg->header.type   = VMSG_REQUEST;

     /* Fill message body. */
     msg->instance = instance;
     msg->method   = method;
     msg->flags    = flags;

     /* Append custom data. */
     write_blocks( msg + 1, blocks, num_blocks );


     D_DEBUG_AT( Voodoo_Manager, "  -> Sending REQUEST message %llu to %u::%u %s(%d bytes).\n",
                 (unsigned long long)serial, instance, method, (flags & VREQ_RESPOND) ? "[RESPONDING] " : "", size );

     /* Unlock the output buffer. */
     connection->PutPacket( packet, !(flags & VREQ_QUEUE) );

     /* Wait for and lock the response buffer. */
     if (flags & VREQ_RESPOND) {
          VoodooResponseMessage *response;

          ret = lock_response( serial, &response );
          if (ret) {
               D_ERROR( "Voodoo/Manager: "
                        "Waiting for the response failed (%s)!\n", DirectResultString( ret ) );
               return ret;
          }

          D_DEBUG_AT( Voodoo_Manager, "  -> Got response %llu (%s) with instance %u for request %llu "
                      "(%d bytes).\n", (unsigned long long)response->header.serial, DirectResultString( response->result ),
                      response->instance, (unsigned long long)response->request, response->header.size );

          *ret_response = response;
     }

     return DR_OK;
}

DirectResult
VoodooManager::next_response( VoodooResponseMessage  *response,
                              VoodooResponseMessage **ret_response )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     DirectResult        ret;
     VoodooMessageSerial serial;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( response != NULL );

     serial = response->request;

     /* Unlock the response buffer. */
     unlock_response( response );

     ret = lock_response( serial, &response );
     if (ret) {
          D_ERROR( "Voodoo/Manager: "
                   "Waiting for the response failed (%s)!\n", DirectResultString( ret ) );
          return ret;
     }

     D_DEBUG_AT( Voodoo_Manager, "  -> Got response %llu (%s) with instance %u for request %llu "
                 "(%d bytes).\n", (unsigned long long)response->header.serial, DirectResultString( response->result ),
                 response->instance, (unsigned long long)response->request, response->header.size );

     *ret_response = response;

     return DR_OK;
}

DirectResult
VoodooManager::finish_request( VoodooResponseMessage *response )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( response != NULL );

     /* Unlock the response buffer. */
     return unlock_response( response );
}

DirectResult
VoodooManager::do_respond( bool                 flush,
                           VoodooMessageSerial  request,
                           DirectResult         result,
                           VoodooInstanceID     instance,
                           VoodooMessageBlock  *blocks,
                           size_t               num_blocks,
                           size_t               data_size )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     size_t                 size;
     VoodooPacket          *packet;
     VoodooMessageSerial    serial;
     VoodooResponseMessage *msg;

     D_MAGIC_ASSERT( this, VoodooManager );

     D_DEBUG_AT( Voodoo_Manager, "  -> Request %llu, result %d, instance %u...\n", (unsigned long long)request, result, instance );

     if (is_quit) {
          D_DEBUG_AT( Voodoo_Manager, "  -> QUIT!\n" );
          return DR_IO;
     }

     /* Calculate the total message size. */
     size = sizeof(VoodooResponseMessage) + data_size;

     D_DEBUG_AT( Voodoo_Manager, "  -> complete message size: %d\n", size );


     /* Lock the output buffer for direct writing. */
     packet = connection->GetPacket( size );
     if (!packet)
          return DR_FAILURE;

     msg = (VoodooResponseMessage*) packet->data_raw();

     serial = msg_serial++;

     /* Fill message header. */
     msg->header.size   = size;
     msg->header.serial = serial;
     msg->header.type   = VMSG_RESPONSE;

     /* Fill message body. */
     msg->request  = request;
     msg->result   = result;
     msg->instance = instance;

     /* Append custom data. */
     write_blocks( msg + 1, blocks, num_blocks );


     D_DEBUG_AT( Voodoo_Manager, "  -> Sending RESPONSE message %llu (%s) with instance %u for request %llu (%d bytes).\n",
                 (unsigned long long)serial, DirectResultString( result ), instance, (unsigned long long)request, size );

     /* Unlock the output buffer. */
     connection->PutPacket( packet, flush );

     return DR_OK;
}

DirectResult
VoodooManager::register_local( VoodooInstance   *instance,
                               VoodooInstanceID *ret_instance )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     VoodooInstanceID instance_id;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( instance != NULL );
     D_ASSERT( ret_instance != NULL );

     instance->AddRef();

     direct_mutex_lock( &instances.lock );

     instance_id = ++instances.last;

     instances.local[instance_id] = instance;

     direct_mutex_unlock( &instances.lock );

     D_DEBUG_AT( Voodoo_Manager, "  -> Added local instance %u (%p)\n", instance_id, instance );

     *ret_instance = instance_id;

     return DR_OK;
}

DirectResult
VoodooManager::unregister_local( VoodooInstanceID instance_id )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     VoodooInstance *instance;

     D_MAGIC_ASSERT( this, VoodooManager );

     direct_mutex_lock( &instances.lock );

     InstanceMap::iterator itr = instances.local.find( instance_id );

     if (itr == instances.local.end()) {
          direct_mutex_unlock( &instances.lock );
          return DR_NOSUCHINSTANCE;
     }

     instance = (*itr).second;

     instances.local.erase( itr );

     direct_mutex_unlock( &instances.lock );

     instance->Release();

     return DR_OK;
}

DirectResult
VoodooManager::lookup_local( VoodooInstanceID   instance_id,
                             VoodooInstance   **ret_instance )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     VoodooInstance *instance;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( instance_id != VOODOO_INSTANCE_NONE );
     D_ASSERT( ret_instance != NULL );

     direct_mutex_lock( &instances.lock );

     InstanceMap::iterator itr = instances.local.find( instance_id );

     direct_mutex_unlock( &instances.lock );

     if (itr == instances.local.end())
          return DR_NOSUCHINSTANCE;

     instance = (*itr).second;

     // FIXME: addref?

     *ret_instance = instance;

     return DR_OK;
}

DirectResult
VoodooManager::register_remote( VoodooInstance   *instance,
                                VoodooInstanceID  instance_id )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( instance != NULL );
     D_ASSERT( instance_id != VOODOO_INSTANCE_NONE );

     instance->AddRef();

     direct_mutex_lock( &instances.lock );

     instances.remote[instance_id] = instance;

     direct_mutex_unlock( &instances.lock );

     D_DEBUG_AT( Voodoo_Manager, "  -> Added remote instance %u (%p)\n", instance_id, instance );

     return DR_OK;
}

DirectResult
VoodooManager::unregister_remote( VoodooInstanceID instance_id )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     VoodooInstance *instance;

     D_MAGIC_ASSERT( this, VoodooManager );

     direct_mutex_lock( &instances.lock );

     InstanceMap::iterator itr = instances.remote.find( instance_id );

     if (itr == instances.remote.end()) {
          direct_mutex_unlock( &instances.lock );
          return DR_NOSUCHINSTANCE;
     }

     instance = (*itr).second;

     instances.remote.erase( itr );

     direct_mutex_unlock( &instances.lock );

     instance->Release();

     return DR_OK;
}

DirectResult
VoodooManager::lookup_remote( VoodooInstanceID   instance_id,
                              VoodooInstance   **ret_instance )
{
     D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this );

     VoodooInstance *instance;

     D_MAGIC_ASSERT( this, VoodooManager );
     D_ASSERT( instance_id != VOODOO_INSTANCE_NONE );
     D_ASSERT( ret_instance != NULL );

     direct_mutex_lock( &instances.lock );

     InstanceMap::iterator itr = instances.remote.find( instance_id );

     direct_mutex_unlock( &instances.lock );

     if (itr == instances.remote.end())
          return DR_NOSUCHINSTANCE;

     instance = (*itr).second;

     // FIXME: addref?

     *ret_instance = instance;

     return DR_OK;
}

//}

