//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
#ifndef __smtk_model_SessionRegistrar_h
#define __smtk_model_SessionRegistrar_h
#ifndef SHIBOKEN_SKIP

#include "smtk/PublicPointerDefs.h"
#include "smtk/Function.h"
#include "smtk/model/StringData.h"

#include <map>

namespace smtk {
  namespace model {

/**\brief A (generic) function-pointer to perform pre-construction session setup.
  *
  * When a session is registered (by calling SessionRegistrar::registerSession),
  * a function of this signature may be passed to it, indicating that this
  * function may be called before a session is constructed.
  *
  * The method may be called multiple times before a session instance is
  * constructed with a cumulative effect if the setup option names vary.
  *
  * If the function returns a positive number, then the setup is accepted as
  * valid and construction may proceed (or setup called again).
  * If it returns 0, then the setup was ignored but construction may proceed.
  * If it returns a negative number then construction should not proceed.
  * The latter can happen for a variety of reasons, including a license limit
  * being reached; an invalid modeling kernel specified; or an impossible
  * configuration.
  *
  * The effect of setup option names is entirely dependent on the session
  * and thus methods to recover from an impossible configuration are left
  * to Session subclasses to implement.
  */
typedef smtk::function<
  int (const std::string&, const StringList& val)> SessionStaticSetup;

/// A (generic) function-pointer to construct a session instance.
typedef smtk::function<SessionPtr()> SessionConstructor;

#if defined(_MSC_VER) && _MSC_VER >= 1800
#define SMTK_FUNCTION_INIT nullptr
#else
#define SMTK_FUNCTION_INIT NULL
#endif

/// A record associating session information with a constructor method.
struct StaticSessionInfo {
  std::string Name;
  SessionStaticSetup Setup;
  SessionConstructor Constructor;
  std::string Tags;
  bool TagsParsed;
  std::string Site;
  StringList Engines;
  StringData FileTypes;

  StaticSessionInfo() : TagsParsed(false) { }
  StaticSessionInfo(
    const std::string& bname,
    const std::string& btags,
    SessionStaticSetup bsetup,
    SessionConstructor bctor)
    : Name(bname), Setup(bsetup), Constructor(bctor), Tags(btags), TagsParsed(false)
    { }
};

/// A map of session names to constructors.
typedef std::map<std::string,StaticSessionInfo> SessionConstructors;

/**\brief A helper for sessions that do not perform static setup.
  *
  * If a session subclass does not require setup,
  * pass this to SessionRegistrar::registerSession().
  */
inline int SessionHasNoStaticSetup(
  const std::string&,
  const StringList&)
{
  return 1;
}

/**\brief A static class for holding information about sessions to modeling kernels.
  *
  * This class is not wrapped for use in Python because shiboken
  * cannot parse headers for boost::bind and boost::function.
  * Use the model manager to identify available sessions and create sessions
  * as Manager (a subclass of Manager) exposes methods that can be wrapped.
  */
class SMTKCORE_EXPORT SessionRegistrar
{
public:
  static bool registerSession(
    const std::string& bname,
    const std::string& tags,
    SessionStaticSetup bsetup,
    SessionConstructor bctor);
  static StringList sessionTypeNames();
  static std::string sessionTags(const std::string& bname);
  static std::string sessionSite(const std::string& bname);
  static StringList sessionEngines(const std::string& bname);
  static StringData sessionFileTypes(
    const std::string& bname, const std::string& engine = std::string());
  static SessionStaticSetup sessionStaticSetup(const std::string& bname);
  static SessionConstructor sessionConstructor(const std::string& bname);
  static SessionPtr createSession(const std::string& bname);

  static std::string fileTypesTag() { return "filetypes"; }

protected:
  static void cleanupSessions();
  static void parseTags(StaticSessionInfo& session);

  static SessionConstructors* s_sessions(bool del = false);
};

  } // namespace model
} // namespace smtk

#endif // SHIBOKEN_SKIP
#endif // __smtk_model_SessionRegistrar_h
