// Filename: notifyCategoryProxy.h
// Created by:  drose (04Mar00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#ifndef NOTIFYCATEGORYPROXY_H
#define NOTIFYCATEGORYPROXY_H

#include "dtoolbase.h"

#include "notifyCategory.h"
#include "notifySeverity.h"
#include "notify.h"

////////////////////////////////////////////////////////////////////
//       Class : NotifyCategoryProxy
// Description : A handy wrapper around a NotifyCategory pointer.
//               This wrapper pretends to be a NotifyCategory object
//               itself, except that it is capable of initializing its
//               pointer if it is NULL.
//
//               The advantage to this over a normal pointer is that
//               it can be used in functions that run at static init
//               time, without worrying about ordering issues among
//               static init routines.  If the pointer hasn't been
//               initialized yet, no sweat; it can initialize itself.
//
//               This must be a template class so it can do this
//               magic; it templates on a class with a static method
//               called get_category() that returns a new pointer to
//               the NotifyCategory.  This way the compiler can
//               generate correct static-init-independent code to
//               initialize the proxy.
//
//               In general, if the proxy object is treated as if it
//               were itself a NotifyCategory object, then it doesn't
//               check whether its category is initialized, and so may
//               not be run at static init time.  That is, you may
//               call proxy.info(), but only when you are not running
//               at static init time.  This is an optimization so you
//               can avoid this unnecessary check when you know (as in
//               most cases) the code does not run at static init.
//
//               On the other hand, if the proxy object is treated as
//               if it were a *pointer* to a NotifyCategory object,
//               then it *does* check whether its category is
//               initialized; you may safely use it in this way at
//               static init time.  Thus, you may call proxy->info()
//               safely whenever you like.
////////////////////////////////////////////////////////////////////
template<class GetCategory>
class NotifyCategoryProxy {
public:
  // This should be set to be called at static init time; it
  // initializes the pointer if it is not already.
  NotifyCategory *init();

  // You don't normally need to call these directly, but they're here
  // anyway.  get_unsafe_ptr() assumes the pointer has been
  // initialized; it should be called only when you know static init
  // has completed (i.e. in any function that is not executing at
  // static init time).  get_safe_ptr() should be called when it is
  // possible that static init has not yet completed (i.e. in a
  // function that might execute at static init time); it calls init()
  // first.
  INLINE NotifyCategory *get_unsafe_ptr();
  INLINE NotifyCategory *get_safe_ptr();

  // The following functions, which may be accessed using the
  // proxy.function() syntax, call get_unsafe_ptr().  They should be
  // used only in non-static-init functions.

  INLINE bool is_on(NotifySeverity severity);

  INLINE bool is_spam();
  INLINE bool is_debug();
  INLINE bool is_info();
  INLINE bool is_warning();
  INLINE bool is_error();
  INLINE bool is_fatal();

  INLINE ostream &out(NotifySeverity severity, bool prefix = true);
  INLINE ostream &spam(bool prefix = true);
  INLINE ostream &debug(bool prefix = true);
  INLINE ostream &info(bool prefix = true);
  INLINE ostream &warning(bool prefix = true);
  INLINE ostream &error(bool prefix = true);
  INLINE ostream &fatal(bool prefix = true);

  // The same functions as above, when accessed using
  // proxy->function() syntax, call get_safe_ptr().  These can be used
  // safely either in static-init or non-static-init functions.
  INLINE NotifyCategory *operator -> ();
  INLINE NotifyCategory &operator * ();
  INLINE operator NotifyCategory * ();

private:
  NotifyCategory *_ptr;
};

template<class GetCategory>
INLINE ostream &operator << (ostream &out, NotifyCategoryProxy<GetCategory> &proxy) {
  return out << proxy->get_fullname();
}

// Finally, here is a set of handy macros to define and reference a
// NotifyCategoryProxy object in each package.

// Following the config convention, this macro defines an external
// reference to a suitable NotifyCategoryProxy object; it should
// appear in the config_*.h file.  The proxy object will be named
// basename_cat.

#if defined(WIN32_VC) && !defined(CPPPARSER)

#define NotifyCategoryDecl(basename, expcl, exptp) \
  class expcl NotifyCategoryGetCategory_ ## basename { \
  public: \
    NotifyCategoryGetCategory_ ## basename(); \
    static NotifyCategory *get_category(); \
  }; \
  exptp template class expcl NotifyCategoryProxy<NotifyCategoryGetCategory_ ## basename>; \
  extern expcl NotifyCategoryProxy<NotifyCategoryGetCategory_ ## basename> basename ## _cat;

#else // WIN32_VC

#define NotifyCategoryDecl(basename, expcl, exptp) \
  class NotifyCategoryGetCategory_ ## basename { \
  public: \
    NotifyCategoryGetCategory_ ## basename(); \
    static NotifyCategory *get_category(); \
  }; \
  extern NotifyCategoryProxy<NotifyCategoryGetCategory_ ## basename> basename ## _cat;

#endif  // WIN32_VC

// This macro is the same as the above, except that it declares a category
// that is not intended to be exported from any DLL.

#define NotifyCategoryDeclNoExport(basename) \
  class NotifyCategoryGetCategory_ ## basename { \
  public: \
    NotifyCategoryGetCategory_ ## basename(); \
    static NotifyCategory *get_category(); \
  }; \
  extern NotifyCategoryProxy<NotifyCategoryGetCategory_ ## basename> basename ## _cat;

// This macro defines the actual declaration of the
// NotifyCategoryProxy object defined above; it should appear in the
// config_*.C file.  In this macro, parent_category may either be the
// NotifyCategoryProxy object of the parent category
// (e.g. parent_cat), or it may be the quoted fullname of the parent.

#ifdef CPPPARSER
#define NotifyCategoryDefName(basename, actual_name, parent_category)
#define NotifyCategoryDef(basename, parent_category)

#else
#define NotifyCategoryDefName(basename, actual_name, parent_category) \
  NotifyCategoryProxy<NotifyCategoryGetCategory_ ## basename> basename ## _cat; \
  static NotifyCategoryGetCategory_ ## basename force_init_ ## basename ## _cat; \
  NotifyCategoryGetCategory_ ## basename:: \
  NotifyCategoryGetCategory_ ## basename() { \
    basename ## _cat.init(); \
  } \
  NotifyCategory *NotifyCategoryGetCategory_ ## basename:: \
  get_category() { \
    return Notify::ptr()->get_category(string(actual_name), parent_category); \
  }
#define NotifyCategoryDef(basename, parent_category) \
  NotifyCategoryDefName(basename, #basename, parent_category);

#endif // CPPPARSER


#include "notifyCategoryProxy.I"

#endif
