//=========================================================================
//  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_Resource_h
#define __smtk_model_Resource_h
/*!\file */

#include "smtk/PublicPointerDefs.h"
#include "smtk/SharedFromThis.h"
#include "smtk/SharedPtr.h"
#include "smtk/SystemConfig.h"

#include "smtk/model/Arrangement.h"
#include "smtk/model/AttributeAssignments.h"
#include "smtk/model/AuxiliaryGeometry.h"
#include "smtk/model/Entity.h"
#include "smtk/model/Events.h"
#include "smtk/model/FloatData.h"
#include "smtk/model/IntegerData.h"
#include "smtk/model/Session.h"
#include "smtk/model/SessionRef.h"
#include "smtk/model/StringData.h"
#include "smtk/model/Tessellation.h"

#include "smtk/common/UUID.h"

#include "smtk/resource/DerivedFrom.h"

#include "smtk/io/Logger.h"

#include <map>

#include <algorithm>
#include <map>
#include <set>
#include <vector>

#include <sstream>

/**\brief The name of the model resource used to filter resources of this type.
  *
  * \sa  SMTK_MODEL_RESOURCE_NAME
  */
#define SMTK_MODEL_RESOURCE_NAME "model"

/**\brief The name of an integer property used to store display Tessellation generation numbers.
  *
  * Starting with "_" indicates internal-use-only.
  * Short (8 bytes or less) means single word comparison suffices on many platforms => fast.
  */
#define SMTK_TESS_GEN_PROP "_tessgen"

/**\brief The name of an integer property used to store mesh Tessellation generation numbers.
  *
  * \sa SMTK_MESH_GEN_PROP
  */
#define SMTK_MESH_GEN_PROP "_meshgen"

/**\brief The name of an integer property used to store the geometric representation style of a model.
  *
  * \sa ModelGeometryStyle
  */
#define SMTK_GEOM_STYLE_PROP "_geomstyle"

/**\brief The name of a float property used to store the bounding box of a model entity or an auxiliary geometry
  *
  * \sa  SMTK_BOUNDING_BOX_PROP
  */
#define SMTK_BOUNDING_BOX_PROP "_boundingBox"

namespace smtk
{
namespace model
{

/// Store information mapping IDs to Entity records. This is the primary storage for SMTK models.
typedef std::map<smtk::common::UUID, EntityPtr> UUIDsToEntities;

/// An abbreviation for an iterator into primary model storage.
typedef UUIDsToEntities::iterator UUIDWithEntityPtr;
typedef UUIDsToEntities::const_iterator UUIDWithConstEntityPtr;
/**\brief Store information about solid models.
  *
  */
class SMTKCORE_EXPORT Resource
  : public smtk::resource::DerivedFrom<Resource, smtk::resource::Resource>
{
public:
  typedef UUIDsToEntities storage_type;
  typedef storage_type::iterator iter_type;
  typedef UUIDsToTessellations::iterator tess_iter_type;

  smtkTypeMacro(smtk::model::Resource);
  smtkSharedPtrCreateMacro(smtk::resource::Resource);

  // typedef referring to the parent resource.
  typedef smtk::resource::Resource ParentResource;

  // Associations to other resources and components are managed internally using
  // smtk::resource::Links.
  static constexpr smtk::resource::Links::RoleType AssociationRole = -1;

  // A model resource may be linked to a mesh collection that represents its
  // tessellation. It doesn't have to, though, if the model's tessellation is
  // represented internally.
  static constexpr smtk::resource::Links::RoleType TessellationRole = -2;

  Resource(smtk::resource::ManagerPtr = smtk::resource::ManagerPtr());
  Resource(
    const smtk::common::UUID& uid, smtk::resource::ManagerPtr = smtk::resource::ManagerPtr());
  Resource(shared_ptr<UUIDsToEntities> topology, shared_ptr<UUIDsToTessellations> tess,
    shared_ptr<UUIDsToTessellations> analysismesh, shared_ptr<UUIDsToAttributeAssignments> attribs,
    const smtk::common::UUID& uid, smtk::resource::ManagerPtr = smtk::resource::ManagerPtr());
  virtual ~Resource();

  UUIDsToEntities& topology();
  const UUIDsToEntities& topology() const;

  UUIDsToTessellations& tessellations();
  const UUIDsToTessellations& tessellations() const;

  UUIDsToTessellations& analysisMesh();
  const UUIDsToTessellations& analysisMesh() const;

  bool setMeshTessellations(const smtk::mesh::ResourcePtr&);
  smtk::mesh::ResourcePtr meshTessellations() const;

  /// Remove all entities and properties from this object. Does not change id or emit signals.
  void clear();

  smtk::resource::SetPtr resources(bool skipUpdate = false)
  {
    (void)skipUpdate;
    return m_resources;
  }

  const UUIDsToAttributeAssignments& attributeAssignments() const;

  BitFlags type(const smtk::common::UUID& ofEntity) const;
  int dimension(const smtk::common::UUID& ofEntity) const;

  using smtk::resource::Resource::name;
  std::string name(const smtk::common::UUID& ofEntity) const;

  EntityPtr findEntity(const smtk::common::UUID& uid, bool trySessions = true) const;

  smtk::resource::ComponentPtr find(const smtk::common::UUID& uid) const override;
  std::function<bool(const smtk::resource::ComponentPtr&)> queryOperation(
    const std::string&) const override;
  void visit(smtk::resource::Component::Visitor&) const override;

  virtual SessionInfoBits erase(
    const smtk::common::UUID& uid, SessionInfoBits flags = smtk::model::SESSION_EVERYTHING);
  virtual SessionInfoBits erase(
    const EntityRef& entityref, SessionInfoBits flags = smtk::model::SESSION_EVERYTHING);
  virtual SessionInfoBits erase(
    const EntityPtr& entityrec, SessionInfoBits flags = smtk::model::SESSION_EVERYTHING);
  virtual SessionInfoBits eraseModel(
    const Model& entityref, SessionInfoBits flags = smtk::model::SESSION_EVERYTHING);
  SessionInfoBits hardErase(
    const EntityRef& eref, SessionInfoBits flags = smtk::model::SESSION_EVERYTHING);

  smtk::common::UUIDs bordantEntities(
    const smtk::common::UUID& ofEntity, int ofDimension = -2) const;
  smtk::common::UUIDs bordantEntities(
    const smtk::common::UUIDs& ofEntities, int ofDimension = -2) const;
  smtk::common::UUIDs boundaryEntities(
    const smtk::common::UUID& ofEntity, int ofDimension = -2) const;
  smtk::common::UUIDs boundaryEntities(
    const smtk::common::UUIDs& ofEntities, int ofDimension = -2) const;

  smtk::common::UUIDs lowerDimensionalBoundaries(
    const smtk::common::UUID& ofEntity, int lowerDimension);
  smtk::common::UUIDs higherDimensionalBordants(
    const smtk::common::UUID& ofEntity, int higherDimension);
  smtk::common::UUIDs adjacentEntities(const smtk::common::UUID& ofEntity, int ofDimension);

  smtk::common::UUIDs entitiesMatchingFlags(BitFlags mask, bool exactMatch = true);
  smtk::common::UUIDs entitiesOfDimension(int dim);

  smtk::common::UUID unusedUUID();
  iter_type insertEntityOfTypeAndDimension(BitFlags entityFlags, int dim);
  iter_type insertEntity(EntityPtr cell);
  iter_type setEntityOfTypeAndDimension(
    const smtk::common::UUID& uid, BitFlags entityFlags, int dim);
  iter_type setEntity(EntityPtr cell);

  smtk::common::UUID addEntityOfTypeAndDimension(BitFlags entityFlags, int dim);
  smtk::common::UUID addEntity(EntityPtr cell);
  smtk::common::UUID addEntityOfTypeAndDimensionWithUUID(
    const smtk::common::UUID& uid, BitFlags entityFlags, int dim);
  smtk::common::UUID addEntityWithUUID(const smtk::common::UUID& uid, EntityPtr cell);

  iter_type insertCellOfDimension(int dim);
  iter_type setCellOfDimension(const smtk::common::UUID& uid, int dim);
  smtk::common::UUID addCellOfDimension(int dim);
  smtk::common::UUID addCellOfDimensionWithUUID(const smtk::common::UUID& uid, int dim);

  void insertEntityReferences(const UUIDWithEntityPtr& c);
  bool elideOneEntityReference(const UUIDWithEntityPtr& c, const smtk::common::UUID& r);
  void elideEntityReferences(const UUIDWithEntityPtr& c);
  void removeEntityReferences(const UUIDWithEntityPtr& c);

  virtual void addToGroup(const smtk::common::UUID& groupId, const smtk::common::UUIDs& uids);

  void setFloatProperty(
    const smtk::common::UUID& entity, const std::string& propName, smtk::model::Float propValue);
  void setFloatProperty(const smtk::common::UUID& entity, const std::string& propName,
    const smtk::model::FloatList& propValue);
  smtk::model::FloatList const& floatProperty(
    const smtk::common::UUID& entity, const std::string& propName) const;
  smtk::model::FloatList& floatProperty(
    const smtk::common::UUID& entity, const std::string& propName);
  bool hasFloatProperty(const smtk::common::UUID& entity, const std::string& propName) const;
  bool removeFloatProperty(const smtk::common::UUID& entity, const std::string& propName);
  const UUIDWithFloatProperties floatPropertiesForEntity(const smtk::common::UUID& entity) const;
  UUIDWithFloatProperties floatPropertiesForEntity(const smtk::common::UUID& entity);
  UUIDsToFloatData& floatProperties() { return *m_floatData; }
  UUIDsToFloatData const& floatProperties() const { return *m_floatData; }

  void setStringProperty(const smtk::common::UUID& entity, const std::string& propName,
    const smtk::model::String& propValue);
  void setStringProperty(const smtk::common::UUID& entity, const std::string& propName,
    const smtk::model::StringList& propValue);
  smtk::model::StringList const& stringProperty(
    const smtk::common::UUID& entity, const std::string& propName) const;
  smtk::model::StringList& stringProperty(
    const smtk::common::UUID& entity, const std::string& propName);
  bool hasStringProperty(const smtk::common::UUID& entity, const std::string& propName) const;
  bool removeStringProperty(const smtk::common::UUID& entity, const std::string& propName);
  const UUIDWithStringProperties stringPropertiesForEntity(const smtk::common::UUID& entity) const;
  UUIDWithStringProperties stringPropertiesForEntity(const smtk::common::UUID& entity);
  UUIDsToStringData& stringProperties() { return *m_stringData; }
  UUIDsToStringData const& stringProperties() const { return *m_stringData; }

  void setIntegerProperty(
    const smtk::common::UUID& entity, const std::string& propName, smtk::model::Integer propValue);
  void setIntegerProperty(const smtk::common::UUID& entity, const std::string& propName,
    const smtk::model::IntegerList& propValue);
  smtk::model::IntegerList const& integerProperty(
    const smtk::common::UUID& entity, const std::string& propName) const;
  smtk::model::IntegerList& integerProperty(
    const smtk::common::UUID& entity, const std::string& propName);
  bool hasIntegerProperty(const smtk::common::UUID& entity, const std::string& propName) const;
  bool removeIntegerProperty(const smtk::common::UUID& entity, const std::string& propName);
  const UUIDWithIntegerProperties integerPropertiesForEntity(
    const smtk::common::UUID& entity) const;
  UUIDWithIntegerProperties integerPropertiesForEntity(const smtk::common::UUID& entity);
  UUIDsToIntegerData& integerProperties() { return *m_integerData; }
  UUIDsToIntegerData const& integerProperties() const { return *m_integerData; }

  smtk::common::UUID modelOwningEntity(const smtk::common::UUID& uid) const;
  smtk::common::UUID sessionOwningEntity(const smtk::common::UUID& uid) const;

  void assignDefaultNames();
  void assignDefaultNamesToModelChildren(const smtk::common::UUID& modelId);
  std::string assignDefaultName(const smtk::common::UUID& uid);
  std::string assignDefaultNameIfMissing(const smtk::common::UUID& uid);
  static std::string shortUUIDName(const smtk::common::UUID& uid, BitFlags entityFlags);

  bool closeSession(const SessionRef& sess);

  SessionRef registerSession(SessionPtr session);
  bool unregisterSession(SessionPtr session, bool expungeSession = true);
  SessionPtr sessionData(const smtk::model::SessionRef& sessRef) const;
  SessionRefs sessions() const;

  EntityRefArray findEntitiesByProperty(const std::string& pname, Integer pval);
  EntityRefArray findEntitiesByProperty(const std::string& pname, Float pval);
  EntityRefArray findEntitiesByProperty(const std::string& pname, const std::string& pval);
  EntityRefArray findEntitiesByProperty(const std::string& pname, const IntegerList& pval);
  EntityRefArray findEntitiesByProperty(const std::string& pname, const FloatList& pval);
  EntityRefArray findEntitiesByProperty(const std::string& pname, const StringList& pval);
  EntityRefArray findEntitiesOfType(BitFlags flags, bool exactMatch = true);

  template <typename Collection>
  Collection findEntitiesByPropertyAs(const std::string& pname, Integer pval);
  template <typename Collection>
  Collection findEntitiesByPropertyAs(const std::string& pname, const IntegerList& pval);
  template <typename Collection>
  Collection findEntitiesByPropertyAs(const std::string& pname, Float pval);
  template <typename Collection>
  Collection findEntitiesByPropertyAs(const std::string& pname, const FloatList& pval);
  template <typename Collection>
  Collection findEntitiesByPropertyAs(const std::string& pname, const std::string& pval);
  template <typename Collection>
  Collection findEntitiesByPropertyAs(const std::string& pname, const StringList& pval);
  template <typename Collection>
  Collection entitiesMatchingFlagsAs(BitFlags flags, bool exactMatch = true);

  tess_iter_type setTessellation(
    const smtk::common::UUID& cellId, const Tessellation& geom, int analysis = 0, int* gen = NULL);

  tess_iter_type setTessellationAndBoundingBox(
    const smtk::common::UUID& cellId, const Tessellation& geom, int analysis = 0, int* gen = NULL);
  // if BBox is provided, just replace corrds with BBox else we would loop through coords
  // BBox format: [xmin, xmax, ymin, ymax, zmin, zmax]
  bool setBoundingBox(
    const smtk::common::UUID& cellId, const std::vector<double>& coords, int providedBBox = 0);
  bool removeTessellation(const smtk::common::UUID& cellId, bool removeGen = false);

  int arrangeEntity(
    const smtk::common::UUID& entityId, ArrangementKind, const Arrangement& arr, int index = -1);
  int unarrangeEntity(
    const smtk::common::UUID& entityId, ArrangementKind, int index, bool removeIfLast = false);
  bool clearArrangements(const smtk::common::UUID& entityId);

  const Arrangements* hasArrangementsOfKindForEntity(
    const smtk::common::UUID& cellId, ArrangementKind) const;
  Arrangements* hasArrangementsOfKindForEntity(const smtk::common::UUID& cellId, ArrangementKind);

  Arrangements& arrangementsOfKindForEntity(const smtk::common::UUID& cellId, ArrangementKind);

  const Arrangement* findArrangement(
    const smtk::common::UUID& entityId, ArrangementKind kind, int index) const;
  Arrangement* findArrangement(const smtk::common::UUID& entityId, ArrangementKind kind, int index);
  int findArrangementInvolvingEntity(const smtk::common::UUID& entityId, ArrangementKind kind,
    const smtk::common::UUID& involved) const;
  bool findDualArrangements(const smtk::common::UUID& entityId, ArrangementKind kind, int index,
    ArrangementReferences& duals) const;
  bool addDualArrangement(const smtk::common::UUID& parent, const smtk::common::UUID& child,
    ArrangementKind kind, int sense, Orientation orientation);

  int findCellHasUseWithSense(
    const smtk::common::UUID& cellId, const smtk::common::UUID& use, int sense) const;
  std::set<int> findCellHasUsesWithOrientation(
    const smtk::common::UUID& cellId, Orientation orient) const;

  smtk::common::UUID cellHasUseOfSenseAndOrientation(
    const smtk::common::UUID& cell, int sense, Orientation o) const;
  smtk::common::UUID findCreateOrReplaceCellUseOfSenseAndOrientation(const smtk::common::UUID& cell,
    int sense, Orientation o, const smtk::common::UUID& replacement = smtk::common::UUID::null());

  smtk::common::UUIDs useOrShellIncludesShells(const smtk::common::UUID& cellUseOrShell) const;
  smtk::common::UUID createIncludedShell(const smtk::common::UUID& cellUseOrShell);
  bool findOrAddIncludedShell(
    const smtk::common::UUID& parentUseOrShell, const smtk::common::UUID& shellToInclude);

  //bool shellHasUse(const smtk::common::UUID& shell, const smtk::common::UUID& use) const;
  //smtk::common::UUIDs shellHasUses(const smtk::common::UUID& shell) const;
  bool findOrAddUseToShell(const smtk::common::UUID& shell, const smtk::common::UUID& use);

  bool findOrAddInclusionToCellOrModel(
    const smtk::common::UUID& cell, const smtk::common::UUID& inclusion);

  bool findOrAddEntityToGroup(const smtk::common::UUID& grp, const smtk::common::UUID& ent);

  // Return a set of resources associated to this model resource.
  smtk::resource::ResourceSet associations() const;

  // Add a resource to the set of associated resources, and return true if the
  // association is successful.
  bool associate(const smtk::resource::ResourcePtr& resource);

  // Remove a resource from the set of associated resources, and return true if
  // the disassociation is successful.
  bool disassociate(const smtk::resource::ResourcePtr& resource);

  bool hasAttribute(const smtk::common::UUID& attribId, const smtk::common::UUID& toEntity);
  bool associateAttribute(smtk::attribute::ResourcePtr attResource,
    const smtk::common::UUID& attribId, const smtk::common::UUID& toEntity);
  bool disassociateAttribute(smtk::attribute::ResourcePtr attResource,
    const smtk::common::UUID& attribId, const smtk::common::UUID& fromEntity, bool reverse = true);
  bool insertEntityAssociations(
    const EntityRef& modelEntity, std::set<smtk::attribute::AttributePtr>& associations);
  std::set<smtk::attribute::AttributePtr> associations(const EntityRef& modelEntity);

  Vertex insertVertex(const smtk::common::UUID& uid);
  Edge insertEdge(const smtk::common::UUID& uid);
  Face insertFace(const smtk::common::UUID& uid);
  Volume insertVolume(const smtk::common::UUID& uid);

  Vertex addVertex();
  Edge addEdge();
  Face addFace();
  Volume addVolume();

  VertexUse insertVertexUse(const smtk::common::UUID& uid);
  VertexUse setVertexUse(const smtk::common::UUID& uid, const Vertex& src, int sense);
  EdgeUse insertEdgeUse(const smtk::common::UUID& uid);
  EdgeUse setEdgeUse(const smtk::common::UUID& uid, const Edge& src, int sense, Orientation o);
  FaceUse insertFaceUse(const smtk::common::UUID& uid);
  FaceUse setFaceUse(const smtk::common::UUID& uid, const Face& src, int sense, Orientation o);
  VolumeUse insertVolumeUse(const smtk::common::UUID& uid);
  VolumeUse setVolumeUse(const smtk::common::UUID& uid, const Volume& src);

  VertexUse addVertexUse();
  VertexUse addVertexUse(const Vertex& src, int sense);
  EdgeUse addEdgeUse();
  EdgeUse addEdgeUse(const Edge& src, int sense, Orientation o);
  FaceUse addFaceUse();
  FaceUse addFaceUse(const Face& src, int sense, Orientation o);
  VolumeUse addVolumeUse();
  VolumeUse addVolumeUse(const Volume& src);

  Chain insertChain(const smtk::common::UUID& uid);
  Chain setChain(const smtk::common::UUID& uid, const EdgeUse& use);
  Chain setChain(const smtk::common::UUID& uid, const Chain& parent);
  Loop insertLoop(const smtk::common::UUID& uid);
  Loop setLoop(const smtk::common::UUID& uid, const FaceUse& use);
  Loop setLoop(const smtk::common::UUID& uid, const Loop& parent);
  Shell insertShell(const smtk::common::UUID& uid);
  Shell setShell(const smtk::common::UUID& uid, const VolumeUse& use);
  Shell setShell(const smtk::common::UUID& uid, const Shell& parent);

  Chain addChain();
  Chain addChain(const EdgeUse&);
  Chain addChain(const Chain&);
  Loop addLoop();
  Loop addLoop(const FaceUse&);
  Loop addLoop(const Loop&);
  Shell addShell();
  Shell addShell(const Volume& src);
  Shell addShell(const VolumeUse& src);

  Group insertGroup(
    const smtk::common::UUID& uid, int extraFlags = 0, const std::string& name = std::string());
  Group addGroup(int extraFlags = 0, const std::string& name = std::string());

  AuxiliaryGeometry insertAuxiliaryGeometry(const smtk::common::UUID& uid, int dim = -1);
  AuxiliaryGeometry addAuxiliaryGeometry(int dim = -1);
  AuxiliaryGeometry addAuxiliaryGeometry(const Model& parent, int dim = -1);
  AuxiliaryGeometry addAuxiliaryGeometry(const AuxiliaryGeometry& parent, int dim = -1);

  Model insertModel(const smtk::common::UUID& uid, int parametricDim = 3, int embeddingDim = 3,
    const std::string& name = std::string());
  Model addModel(
    int parametricDim = 3, int embeddingDim = 3, const std::string& name = std::string());

  Instance addInstance();
  Instance addInstance(const EntityRef& instanceOf);

  template <typename T>
  bool insertModelFaceWithOrientedOuterLoop(const smtk::common::UUID& faceId, // to be created
    const smtk::common::UUID& faceUseId,                                      // to be created
    const smtk::common::UUID& loopId,                                         // to be created
    const T& orderedEdgesWithOrientation);

  template <typename T>
  bool insertModelFaceOrientedInnerLoop(const smtk::common::UUID& loopId, // to be created
    const smtk::common::UUID& preExistingLoopId, // the outer loop to contain the inner loop
    const T& orderedEdgesWithOrientation);

  template <typename T, typename U, typename V>
  bool deleteEntities(T& entities, U& modified, V& expunged, bool debugLog);

  void observe(ResourceEventType event, ConditionCallback functionHandle, void* callData);
  void observe(ResourceEventType event, OneToOneCallback functionHandle, void* callData);
  void observe(ResourceEventType event, OneToManyCallback functionHandle, void* callData);
  void unobserve(ResourceEventType event, ConditionCallback functionHandle, void* callData);
  void unobserve(ResourceEventType event, OneToOneCallback functionHandle, void* callData);
  void unobserve(ResourceEventType event, OneToManyCallback functionHandle, void* callData);
  void trigger(ResourceEventType event, const smtk::model::EntityRef& src);
  void trigger(ResourceEventType event, const smtk::model::EntityRef& src,
    const smtk::model::EntityRef& related);
  void trigger(ResourceEventType event, const smtk::model::EntityRef& src,
    const smtk::model::EntityRefArray& related);

  smtk::io::Logger& log() { return smtk::io::Logger::instance(); }

protected:
  friend class smtk::attribute::Resource;

  void assignDefaultNamesWithOwner(const UUIDWithEntityPtr& irec, const smtk::common::UUID& owner,
    const std::string& ownersName, std::set<smtk::common::UUID>& remaining, bool nokids);
  std::string assignDefaultName(const smtk::common::UUID& uid, BitFlags entityFlags);
  IntegerList& entityCounts(const smtk::common::UUID& modelId, BitFlags entityFlags);
  void prepareForEntity(std::pair<smtk::common::UUID, EntityPtr>& entry);

  smtk::common::UUID modelOwningEntityRecursive(
    const smtk::common::UUID& uid, std::set<smtk::common::UUID>& visited) const;
  smtk::common::UUID sessionOwningEntityRecursive(
    const smtk::common::UUID& uid, std::set<smtk::common::UUID>& visited) const;

  // Below are all the different things that can be mapped to a UUID:
  smtk::shared_ptr<UUIDsToEntities> m_topology;
  smtk::shared_ptr<UUIDsToFloatData> m_floatData;
  smtk::shared_ptr<UUIDsToStringData> m_stringData;
  smtk::shared_ptr<UUIDsToIntegerData> m_integerData;
  smtk::shared_ptr<UUIDsToTessellations> m_tessellations;
  smtk::shared_ptr<UUIDsToTessellations> m_analysisMesh;
  smtk::shared_ptr<UUIDsToAttributeAssignments> m_attributeAssignments;
  smtk::shared_ptr<UUIDsToSessions> m_sessions;
  smtk::shared_ptr<resource::Set> m_resources;
  typedef std::owner_less<smtk::attribute::WeakResourcePtr> ResourceLessThan;
  typedef std::set<smtk::attribute::WeakResourcePtr, ResourceLessThan> WeakResourceSet;
  WeakResourceSet m_attributeResources; // weak references to attribute resources

  smtk::shared_ptr<Session> m_defaultSession;

  IntegerList m_globalCounters; // first entry is session counter, second is model counter

  std::set<ConditionTrigger> m_conditionTriggers;
  std::set<OneToOneTrigger> m_oneToOneTriggers;
  std::set<OneToManyTrigger> m_oneToManyTriggers;
};

template <typename Collection>
Collection Resource::findEntitiesByPropertyAs(const std::string& pname, Integer pval)
{
  Collection collection;
  UUIDWithIntegerProperties pit;
  for (pit = m_integerData->begin(); pit != m_integerData->end(); ++pit)
  {
    PropertyNameWithIntegers it;
    for (it = pit->second.begin(); it != pit->second.end(); ++it)
    {
      if (it->first == pname && it->second.size() == 1 && it->second[0] == pval)
      {
        typename Collection::value_type entry(shared_from_this(), pit->first);
        if (entry.isValid())
          collection.insert(collection.end(), entry);
      }
    }
  }
  return collection;
}

template <typename Collection>
Collection Resource::findEntitiesByPropertyAs(const std::string& pname, const IntegerList& pval)
{
  Collection collection;
  UUIDWithIntegerProperties pit;
  for (pit = m_integerData->begin(); pit != m_integerData->end(); ++pit)
  {
    PropertyNameWithIntegers it;
    for (it = pit->second.begin(); it != pit->second.end(); ++it)
    {
      if (it->first == pname && it->second == pval)
      {
        typename Collection::value_type entry(shared_from_this(), pit->first);
        if (entry.isValid())
          collection.insert(collection.end(), entry);
      }
    }
  }
  return collection;
}

template <typename Collection>
Collection Resource::findEntitiesByPropertyAs(const std::string& pname, Float pval)
{
  Collection collection;
  UUIDWithFloatProperties pit;
  for (pit = m_floatData->begin(); pit != m_floatData->end(); ++pit)
  {
    PropertyNameWithFloats it;
    for (it = pit->second.begin(); it != pit->second.end(); ++it)
    {
      if (it->first == pname && it->second.size() == 1 && it->second[0] == pval)
      {
        typename Collection::value_type entry(shared_from_this(), pit->first);
        if (entry.isValid())
          collection.insert(collection.end(), entry);
      }
    }
  }
  return collection;
}

template <typename Collection>
Collection Resource::findEntitiesByPropertyAs(const std::string& pname, const FloatList& pval)
{
  Collection collection;
  UUIDWithFloatProperties pit;
  for (pit = m_floatData->begin(); pit != m_floatData->end(); ++pit)
  {
    PropertyNameWithFloats it;
    for (it = pit->second.begin(); it != pit->second.end(); ++it)
    {
      if (it->first == pname && it->second == pval)
      {
        typename Collection::value_type entry(shared_from_this(), pit->first);
        if (entry.isValid())
          collection.insert(collection.end(), entry);
      }
    }
  }
  return collection;
}

template <typename Collection>
Collection Resource::findEntitiesByPropertyAs(const std::string& pname, const std::string& pval)
{
  Collection collection;
  UUIDWithStringProperties pit;
  for (pit = m_stringData->begin(); pit != m_stringData->end(); ++pit)
  {
    PropertyNameWithStrings it;
    for (it = pit->second.begin(); it != pit->second.end(); ++it)
    {
      if (it->first == pname && it->second.size() == 1 && it->second[0] == pval)
      {
        typename Collection::value_type entry(shared_from_this(), pit->first);
        if (entry.isValid())
          collection.insert(collection.end(), entry);
      }
    }
  }
  return collection;
}

template <typename Collection>
Collection Resource::findEntitiesByPropertyAs(const std::string& pname, const StringList& pval)
{
  Collection collection;
  UUIDWithStringProperties pit;
  for (pit = m_stringData->begin(); pit != m_stringData->end(); ++pit)
  {
    PropertyNameWithStrings it;
    for (it = pit->second.begin(); it != pit->second.end(); ++it)
    {
      if (it->first == pname && it->second == pval)
      {
        typename Collection::value_type entry(shared_from_this(), pit->first);
        if (entry.isValid())
          collection.insert(collection.end(), entry);
      }
    }
  }
  return collection;
}

template <typename Collection>
Collection Resource::entitiesMatchingFlagsAs(BitFlags mask, bool exactMatch)
{
  smtk::common::UUIDs matches = this->entitiesMatchingFlags(mask, exactMatch);
  Collection collection;
  for (smtk::common::UUIDs::iterator it = matches.begin(); it != matches.end(); ++it)
  {
    typename Collection::value_type entry(shared_from_this(), *it);
    if (entry.isValid())
      collection.insert(collection.end(), entry);
  }
  return collection;
}

} // model namespace
} // smtk namespace

#endif // __smtk_model_Resource_h
