//=========================================================================
//  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_SubphraseGenerator_h
#define __smtk_model_SubphraseGenerator_h

#include "smtk/model/DescriptivePhrase.h"
#include "smtk/model/EntityPhrase.h"
#include "smtk/model/EntityListPhrase.h"
#include "smtk/model/MeshPhrase.h"
#include "smtk/model/MeshListPhrase.h"
#include "smtk/model/PropertyListPhrase.h"

#include "smtk/model/Manager.h" // For PropertyType enum.

namespace smtk {
  namespace model {

class Instance;

/**\brief Generate subphrases to display for a given descriptive phrase.
  *
  * This abstract class should be subclassed by user interfaces
  * to provide control over what information is presented about a
  * given entity or set of entities.
  *
  * Its subphrases() method takes in a single phrase and
  * returns an ordered array of phrases.
  *
  * Besides this one pure virtual method, some utility routines
  * are provided to fetch phrases for common information.
  * Subclasses may use these in their implementations of subphrases().
  */
class SMTKCORE_EXPORT SubphraseGenerator : smtkEnableSharedPtr(SubphraseGenerator)
{
public:
  smtkTypeMacro(SubphraseGenerator);
  virtual ~SubphraseGenerator() { }

  virtual DescriptivePhrases subphrases(DescriptivePhrase::Ptr src);
  virtual int directLimit() const;
  virtual bool setDirectLimit(int val);
  virtual bool shouldOmitProperty(
    DescriptivePhrase::Ptr parent, PropertyType ptype, const std::string& pname) const;

  virtual void setSkipProperties(bool val);
  virtual bool skipProperties() const;
  virtual void setSkipAttributes(bool val);
  virtual bool skipAttributes() const;

protected:
  SubphraseGenerator();

  void instancesOfEntity(DescriptivePhrase::Ptr src, const EntityRef& ent, DescriptivePhrases& result);
  void attributesOfEntity(DescriptivePhrase::Ptr src, const EntityRef& ent, DescriptivePhrases& result);
  void propertiesOfEntity(DescriptivePhrase::Ptr src, const EntityRef& ent, DescriptivePhrases& result);
  void floatPropertiesOfEntity(DescriptivePhrase::Ptr src, const EntityRef& ent, DescriptivePhrases& result);
  void stringPropertiesOfEntity(DescriptivePhrase::Ptr src, const EntityRef& ent, DescriptivePhrases& result);
  void integerPropertiesOfEntity(DescriptivePhrase::Ptr src, const EntityRef& ent, DescriptivePhrases& result);

  void cellOfUse(DescriptivePhrase::Ptr src, const UseEntity& ent, DescriptivePhrases& result);
  void boundingShellsOfUse(DescriptivePhrase::Ptr src, const UseEntity& ent, DescriptivePhrases& result);
  void toplevelShellsOfUse(DescriptivePhrase::Ptr src, const UseEntity& ent, DescriptivePhrases& result);

  void usesOfCell(DescriptivePhrase::Ptr src, const CellEntity& ent, DescriptivePhrases& result);
  void inclusionsOfCell(DescriptivePhrase::Ptr src, const CellEntity& ent, DescriptivePhrases& result);
  void boundingCellsOfCell(DescriptivePhrase::Ptr src, const CellEntity& ent, DescriptivePhrases& result);

  void usesOfShell(DescriptivePhrase::Ptr src, const ShellEntity& ent, DescriptivePhrases& result);

  void membersOfGroup(DescriptivePhrase::Ptr src, const Group& grp, DescriptivePhrases& result);

  void freeSubmodelsOfModel(DescriptivePhrase::Ptr src, const Model& mod, DescriptivePhrases& result);
  void freeGroupsInModel(DescriptivePhrase::Ptr src, const Model& mod, DescriptivePhrases& result);
  void freeCellsOfModel(DescriptivePhrase::Ptr src, const Model& mod, DescriptivePhrases& result);

  void prototypeOfInstance(DescriptivePhrase::Ptr src, const Instance& ent, DescriptivePhrases& result);

  void modelsOfSession(DescriptivePhrase::Ptr src, const SessionRef& sess, DescriptivePhrases& result);

  void entitiesOfEntityList(EntityListPhrase::Ptr src, const EntityRefArray& ents, DescriptivePhrases& result);
  void propertiesOfPropertyList(PropertyListPhrase::Ptr src, PropertyType p, DescriptivePhrases& result);

  void meshesOfModel(DescriptivePhrase::Ptr src, const Model& mod, DescriptivePhrases& result);
  void meshsetsOfMesh(MeshPhrase::Ptr meshphr, DescriptivePhrases& result);
  void meshesOfMeshList(MeshListPhrase::Ptr src, DescriptivePhrases& result);
  void meshsetsOfCollectionByDim(
  MeshPhrase::Ptr meshphr, smtk::mesh::DimensionType dim, DescriptivePhrases& result);

  void addEntityProperties(
    PropertyType ptype, std::set<std::string>& props,
    DescriptivePhrase::Ptr parent, DescriptivePhrases& result);

  template<typename T>
  void addEntityPhrases(const T& ents, DescriptivePhrase::Ptr parent, int limit, DescriptivePhrases& result);

  template<typename T>
  void addMeshPhrases(const T& ents, DescriptivePhrase::Ptr parent, int limit, DescriptivePhrases& result);

  int m_directlimit;
  bool m_skipAttributes;
  bool m_skipProperties;
};

template<typename T>
void SubphraseGenerator::addEntityPhrases(
  const T& ents, DescriptivePhrase::Ptr parent, int limit, DescriptivePhrases& result)
{
  if (limit < 0 || static_cast<int>(ents.size()) < limit)
    {
    for (typename T::const_iterator it = ents.begin(); it != ents.end(); ++it)
      {
      result.push_back(
        EntityPhrase::create()->setup(*it, parent));
      }
    }
  else
    {
    result.push_back(
      EntityListPhrase::create()->setup(ents, parent));
    }
}

/**\brief Add child MeshPhrases with an iterable container of meshes.
  *
  * This templated method is provided so that arrays of MeshSets or Collections
  * are both accepted.
  */

template<typename T>
void SubphraseGenerator::addMeshPhrases(
  const T& meshes, DescriptivePhrase::Ptr parent, int limit, DescriptivePhrases& result)
{
  if (limit < 0 || static_cast<int>(meshes.size()) < limit)
    {
    for (typename T::const_iterator it = meshes.begin(); it != meshes.end(); ++it)
      {
      result.push_back(
        MeshPhrase::create()->setup(*it, parent));
      }
    }
  else
    {
    result.push_back(
      MeshListPhrase::create()->setup(meshes, parent));
    }
}

  } // namespace model
} // namespace smtk

#endif // __smtk_model_SubphraseGenerator_h
