//-----------------------------------------------------------------------------
// Copyright (c) 2013 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

/*
** Alive and Ticking
** (c) Copyright 2006 Burnt Wasp
**     All Rights Reserved.
**
** Filename:    dispatcher.h
** Author:      Tom Bampton
** Created:     19/8/2006
** Purpose:
**   Message Dispatcher
**
*/

#include "messaging/message.h"
#include "console/console.h"

#ifndef _DISPATCHER_H_
#define _DISPATCHER_H_

/// @addtogroup msgsys Message System
// @{

//////////////////////////////////////////////////////////////////////////
/// @brief Namespace for the message dispatcher functions
//////////////////////////////////////////////////////////////////////////
namespace Dispatcher
{

// [tom, 2/19/2007] This semi colon prevents VS from auto indenting the comments
// below, which is really annoying when you're trying to write docs.
;

/// @addtogroup msgsys Message System
// @{

//////////////////////////////////////////////////////////////////////////
// Interface for objects that receive messages
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
/// @brief Listener interface for objects that receive messages
///
/// @see ScriptMsgListener
//////////////////////////////////////////////////////////////////////////
class IMessageListener
{
protected:
   /// List of queues this listener is registered with.
   Vector<StringTableEntry> mQueues;

public:
   virtual ~IMessageListener();

   //////////////////////////////////////////////////////////////////////////
   /// @brief Callback for when messages are received
   /// 
   /// @param queue The name of the queue the message was dispatched to
   /// @param msg The type of message
   /// @param data The data for the message
   /// @return false to prevent other listeners receiving this message, true otherwise
   /// @see onMessageObjectReceived()
   //////////////////////////////////////////////////////////////////////////
   virtual bool onMessageReceived(StringTableEntry queue, const char *msg, const char *data) = 0;

   //////////////////////////////////////////////////////////////////////////
   /// @brief Callback for when message objects are received
   /// 
   /// @param queue The name of the queue the message was dispatched to
   /// @param msg The message object
   /// @return false to prevent other listeners receiving this message, true otherwise
   /// @see onMessageReceived()
   //////////////////////////////////////////////////////////////////////////
   virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg ) = 0;


   //////////////////////////////////////////////////////////////////////////
   /// @brief Callback for when the listener is added to a queue
   ///
   /// The default implementation of onAddToQueue() and onRemoveFromQueue()
   /// provide tracking of the queues this listener is added to through the
   /// #mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue()
   /// should ensure they call the parent implementation in any overrides.
   /// 
   /// @param queue The name of the queue that the listener added to
   /// @see onRemoveFromQueue()
   //////////////////////////////////////////////////////////////////////////
   virtual void onAddToQueue(StringTableEntry queue);

   //////////////////////////////////////////////////////////////////////////
   /// @brief Callback for when the listener is removed from a queue
   /// 
   /// The default implementation of onAddToQueue() and onRemoveFromQueue()
   /// provide tracking of the queues this listener is added to through the
   /// #mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue()
   /// should ensure they call the parent implementation in any overrides.
   /// 
   /// @param queue The name of the queue the listener was removed from
   /// @see onAddToQueue()
   //////////////////////////////////////////////////////////////////////////
   virtual void onRemoveFromQueue(StringTableEntry queue);
};

//////////////////////////////////////////////////////////////////////////
/// @brief Internal class for tracking message queues
//////////////////////////////////////////////////////////////////////////
struct MessageQueue
{
   StringTableEntry mQueueName;
   VectorPtr<IMessageListener *> mListeners;

   MessageQueue() : mQueueName("")
   {
   }

   bool isEmpty()    { return mListeners.size() == 0; }

   bool dispatchMessage(const char* event, const char* data)
   {
      for(VectorPtr<IMessageListener *>::iterator i = mListeners.begin();i != mListeners.end();i++)
      {
         if( !(*i)->onMessageReceived(mQueueName, event, data) )
            return false;
      }
      return true;
   }

   bool dispatchMessageObject(Message *msg)
   {
      for(VectorPtr<IMessageListener *>::iterator i = mListeners.begin();i != mListeners.end();i++)
      {
         if( !(*i)->onMessageObjectReceived(mQueueName, msg) )
            return false;
      }
      return true;
   }
};

//////////////////////////////////////////////////////////////////////////
// Message Dispatcher Functions
//////////////////////////////////////////////////////////////////////////

/// @name Message Queue Management
// @{

//////////////////////////////////////////////////////////////////////////
/// @brief Check if a message queue is registered
/// 
/// @param name The name of the message queue
/// @return true if the queue is registered, false otherwise
/// @see registerMessageQueue(), unregisterMessageQueue()
//////////////////////////////////////////////////////////////////////////
extern bool isQueueRegistered(const char *name);

//////////////////////////////////////////////////////////////////////////
/// @brief Register a message queue
/// 
/// @param name The name of the message queue to register
/// @see isQueueRegistered(), unregisterMessageQueue()
//////////////////////////////////////////////////////////////////////////
extern void registerMessageQueue(const char *name);

//////////////////////////////////////////////////////////////////////////
/// @brief Unregister a message queue
/// 
/// @param name The name of the message queue
/// @see registerMessageQueue(), isQueueRegistered()
//////////////////////////////////////////////////////////////////////////
extern void unregisterMessageQueue(const char *name);

//////////////////////////////////////////////////////////////////////////
/// @brief Register a listener with a queue to receive messages
/// 
/// @param queue The name of the queue to register the listener with
/// @param listener The listener interface that receives messages
/// @return true for success, false otherwise
/// @see unregisterMessageListener()
//////////////////////////////////////////////////////////////////////////
extern bool registerMessageListener(const char *queue, IMessageListener *listener);

//////////////////////////////////////////////////////////////////////////
/// @brief Unregister a listener with a queue
/// 
/// @param queue The name of the queue to unregister the listener
/// @param listener The listener interface that was passed to registerMessageListener()
/// @see registerMessageListener()
//////////////////////////////////////////////////////////////////////////
extern void unregisterMessageListener(const char *queue, IMessageListener *listener);

// @}

/// @name Message Dispatcher
// @{

//////////////////////////////////////////////////////////////////////////
/// @brief Dispatch a message to a queue
/// 
/// @param queue Queue to dispatch the message to
/// @param msg Message to dispatch
/// @param data Data for message
/// @return true for success, false for failure
/// @see dispatchMessageObject()
//////////////////////////////////////////////////////////////////////////
extern bool dispatchMessage(const char *queue, const char *msg, const char *data);

//////////////////////////////////////////////////////////////////////////
/// @brief Dispatch a message object to a queue
/// 
/// @param queue Queue to dispatch the message to
/// @param msg Message to dispatch
/// @return true for success, false for failure
/// @see dispatchMessage()
//////////////////////////////////////////////////////////////////////////
extern bool dispatchMessageObject(const char *queue, Message *msg);

// @}

//////////////////////////////////////////////////////////////////////////
// Internal Functions
//////////////////////////////////////////////////////////////////////////

/// @name Internal Functions
// @{

//////////////////////////////////////////////////////////////////////////
/// @brief Internal function: Lock the dispatcher mutex.
/// @return true for success, false for failure
/// @see unlockDispatcherMutex()
//////////////////////////////////////////////////////////////////////////
extern bool lockDispatcherMutex();

//////////////////////////////////////////////////////////////////////////
/// @brief Internal function: Unlock the dispatcher mutex.
/// @see lockDispatcherMutex()
//////////////////////////////////////////////////////////////////////////
extern void unlockDispatcherMutex();

//////////////////////////////////////////////////////////////////////////
/// @brief Internal function: obtain message queue. Dispatcher mutex must be locked.
/// 
/// @param name Name of the queue
/// @return Message queue
/// @see lockDispatcherMutex(), unlockDispatcherMutex()
//////////////////////////////////////////////////////////////////////////
extern MessageQueue *getMessageQueue(const char *name);

// @}

// @}

} // end namespace Dispatcher

// @}

#endif // _DISPATCHER_H_
