// Copyright (C) 2010-2013  MarketFactory, Inc. All Rights Reserved.
#ifndef __MFAPI_MFMESSAGE__
#define __MFAPI_MFMESSAGE__
#include <API/Clients/CPP/Proto/APITypes.h>

#include <boost/shared_ptr.hpp>

namespace MarketFactory {
class FastOStream;
}
namespace MFAPI {
using MarketFactory::FastOStream;

// The type of ID of a message.
typedef uint16_t MsgType_t;

// Shared pointer.
class MFMessage;
typedef boost::shared_ptr<MFMessage> ShpMFMessage;

/**
 * A base class for all generate message instances.
 */
class MFMessage {
 protected:
  MFMessage();
  virtual ~MFMessage();

 public:
  MFMessage(const MFMessage&);
  MFMessage& operator=(const MFMessage&);

  // Obtain a typed pointer from the message.
  template <class T>
  inline T* getPtr(bool undress = true) {
    return static_cast<T*>(this);
  }
  template <class T>
  inline const T* getPtrC() const {
    // Note: accessing 'ID' insures we're casting to one of the message types!
    return static_cast<const T*>(this);
  }

  // Obtain a typed reference from the message.
  // Use 'undress = false' in the rare case where undress is accounted for.
  template <class T>
  T& getRef(bool undress = true) {
    return *(getPtr<T>(undress));
  }
  template <class T>
  const T& getRefC() const {
    return *(getPtrC<T>());
  }

  // Note that the following methods do not modify the reference argument; the
  // argument is merely used to provide appropriate dispatch on the given
  // type. Typically you can use the same variable it is assigned to, and
  // compilers supports that, e.g.:
  //
  //     MyMessageType& ref = msg->getRef(ref);
  //
  // Note: resist temptation to make any of these methods 'const'--the
  // compiler will not select them if you do, even if the parameter is const.

  template <class T>
  T* getPtr(T*& ptr) {
    ptr = getPtr<T>();
    MF_ASSERT(ptr != 0);
    return ptr;
  }
  template <class T>
  const T* getPtr(const T*& ptr) /* non-const */
  {
    ptr = getPtrC<T>();
    MF_ASSERT(ptr != 0);
    return ptr;
  }

  template <class T>
  T& getRef(T&) {
    return *(getPtr<T>());
  }
  template <class T>
  const T& getRef(const T&) /* non-const */
  {
    return *(getPtrC<T>());
  }

  // Return the type of this message.
  virtual MsgType_t getMsgType() const = 0;

  // Methods for serialization.
  virtual void dump(std::ostream& os, bool pretty) const = 0;
  virtual void read(void*) = 0;
  virtual void write(void*) const = 0;
  virtual void dump(void*) const = 0;

 public:
  //--------------------------------------------------------------------------
  // Static methods.

  // Decode an encoded message from 'buffer' with 'length' bytes and return a
  // new decoded message object from it.
  static ShpMFMessage decodeMessage(MsgType_t id, const char* buffer,
                                    size_t length);

  // Encode the provided message to a buffer pre-allocated with at least
  // 'maxsize' bytes, and return the number of bytes actually filled in.
  static size_t encodeMessage(const MFMessage& msg, char* buffer,
                              size_t maxsize);
};

std::ostream& operator<<(std::ostream& os, const MFMessage& o);
FastOStream& operator<<(FastOStream& os, const MFMessage& o);
}
#endif
// InclKwds: MFMessage ShpMFMessage
