//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2016 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
//
#ifndef ATTRIBUTES_HPP
#define ATTRIBUTES_HPP

/**
 * \file Attributes.hpp
 *
 * This file contains the attribute system
 */

#include <Config.hpp>
#include <QFile>
#include <QTextStream>

#include <Hash.hpp>
#include <Types.hpp>
#include <Information.hpp>
#include <Progress.hpp>
#include <Triangle.hpp>
#include <Colors.hpp>
#include <tbb/concurrent_unordered_map.h>

#include <omp.h>

#include <Features.hpp>

// GCC support for C++11 lacks is_trivially_copyable before 5.x (which exactly?)
#if __cplusplus >= 201103L && !__GNUC_LESS(5, 0)
  #include <type_traits>
  #define is_trivially_copyable std::is_trivially_copyable
#else
  #include <tr1/type_traits>
  #define is_trivially_copyable std::tr1::has_trivial_copy
#endif

#include <Common.hpp>

// Use TBB concurrent unordered map
#define STD_MAP tbb::concurrent_unordered_map

template<bool, typename _Tp = void>
struct enable_if {};

template<typename _Tp>
struct enable_if<true, _Tp> { typedef _Tp type; };

/** Determines if template arguments are plain old data (POD) types.
 * TODO: what exactly does it mean? That they are always save/loadable to/from memory dump?
 */
#define IS_POD(T) typename enable_if<is_trivially_copyable<T>::value, T>::type

namespace  mgx
{
  typedef Vector<2, Colorb> Vec2Colorb;
  typedef Vector<3, Colorb> Vec3Colorb;

  namespace
  {
    // Functions to read/write QByteArray

    /** @brief Read POD type from QByteArray
     * This function does not care about endianness.
     * @param data: output object
     * @param bd: input byte array
     * @param pos: in/out: current position in the array
     */
    template<typename T>
    bool readPOD(IS_POD(T) &data, const QByteArray &ba, size_t &pos)
    {
      if(qint64(pos + sizeof(T)) > ba.size()) {
        Information::out << "Error reading past end of attribute" << endl;
        return false;
      }
      memcpy(&data, ba.data() + pos, sizeof(T));
      pos += sizeof(T);
      return true;
    }

    // Read a vector of POD type from QByteArray (use readAttr instead for matrices)
    template<typename T>
    bool readPODVector(IS_POD(T) &data, size_t n, const QByteArray &ba, size_t &pos)
    {
      if(qint64(pos + sizeof(T) * n) > ba.size()) {
        Information::out << "Error reading past end of attribute" << endl;
        return false;
      }
      memcpy(&data, ba.data() + pos, sizeof(T) * n);
      pos += sizeof(T) * n;
      return true;
    }

    inline bool readChar(void *data, uint sz, const QByteArray &ba, size_t &pos)
    {
      if(qint64(pos + sz) > ba.size()) {
        Information::out << "Error reading past end of attribute" << endl;
        return false;
      }
      memcpy(data, ba.data() + pos, sz);
      pos += sz;
      return true;
    }

    // Write to QByteArray
    template<typename T>
    bool writePOD(const IS_POD(T) &data, QByteArray &ba)
    {
      QByteArray qba(sizeof(T), 0);
      memcpy(qba.data(), &data, sizeof(T));
      ba.append(qba);
      return true;
    }

    template<typename T>
    bool writePODVector(const IS_POD(T) &data, size_t n, QByteArray &ba)
    {
      QByteArray qba(sizeof(T) * n, 0);
      memcpy(qba.data(), &data, sizeof(T) * n);
      ba.append(qba);
      return true;
    }

    // Write an array of char
    inline bool writeChar(const void *data, size_t sz, QByteArray &ba)
    {
      QByteArray qba(sz, 0);
      memcpy(qba.data(), data, sz);
      ba.append(qba);
      return true;
    }

    // File read/write functions

    // Write some (POD) data to a file
    template <typename T>
    bool writeFile(QIODevice &file, const T &data)
    {
      size_t qty = file.write((char*)&data, sizeof(T));
      if(qty != sizeof(T))
        throw QString("Attributes::write:Error on write");
      return true;
    }

    // Write a standard vector to a file
    template <typename T>
    bool writeFile(QIODevice &file, const std::vector<T> &data)
    {
      size_t sz = data.size();
      writeFile(file, sz);
      for(size_t i = 0; i < sz; i++)
        writeFile(file, data[i]);
      return true;
    }

    // Write some data to a file
    inline bool writeFile(QIODevice &file, char *data, size_t size)
    {
      size_t qty = file.write(data, size);
      if(qty != size)
        throw QString("Attributes::write:Error on write");
      return true;
    }

    // Write a QString to a file
    inline bool writeFileQString(QIODevice &file, const QString &s)
    {
      QByteArray ba = s.toUtf8();
      uint sz = ba.size();
      return writeFile(file, sz)
          and writeFile(file, ba.data(), sz);
    }

    // Specialize for QString. We need to do this for any other complex types required
    inline bool writeFile(QIODevice &file, const QString &s)
    {
      return writeFileQString(file, s);
    }

    // Read some (POD) data from a file
    template <typename T>
    bool readFile(QIODevice &file, T &data)
    {
      size_t count = file.read((char*)&data, sizeof(T));
      if(count != sizeof(T))
        return false;

      return true;
    }

    // Read a standard vector from a file
    template <typename T>
    bool readFile(QIODevice &file, std::vector<T> &data)
    {
      size_t sz;
      bool result = readFile(file, sz);
      data.reserve(data.size() + sz);
      for(size_t i = 0; i < sz; i++) {
        T value;
        result &= readFile(file, value);
        data.push_back(value);
      }
      return result;
    }

    // Read a QString from a file
    inline bool readFileQString(QIODevice &file, QString &s)
    {
      uint sz;
      if(!readFile(file, sz))
        return false;
      QByteArray ba(sz, 0);
      if(file.read(ba.data(), sz) != sz)
        return false;
      s = QString::fromUtf8(ba);
      return true;
    }

    // Specialize for QString. We need to do this for any other complex types required
    inline bool readFile(QIODevice &file, QString &s)
    {
      return readFileQString(file, s);
    }
  }

  /**
   * Maps for saveId for vertices and cells, created when file is read
   * Globals required for overloads of readAttr and writeAttr for custom types
   */
  extern mgx_EXPORT IntVtxMap *vMap;
  extern mgx_EXPORT IntCellMap *cMap;

  /**
    * Read the attribute value from a QByteArray
    */
  template<typename T>
  bool readAttr(T &data, const QByteArray &ba, size_t &pos)
  {
    return readPOD<T>(data, ba, pos);
  }

  template<size_t n, size_t m, typename T>
  bool readAttr(Matrix<n, m, T> &data, const QByteArray &ba, size_t &pos)
  {
    for(size_t i = 0; i < n; ++i)
      if(!readPODVector<T>(data[i][0], m, ba, pos))
        return false;
    return true;
  }

  template<size_t n, typename T>
  bool readAttr(Vector<n, T> &data, const QByteArray &ba, size_t &pos)
  {
    return readPODVector<T>(data[0], n, ba, pos);
  }

  template<size_t n>
  bool readAttr(Vector<n, Colorb> &data, const QByteArray &ba, size_t &pos)
  {
    for(size_t i = 0; i < n; ++i)
      if(!readChar(&data[i][0], sizeof(Colorb), ba, pos))
        return false;
    return true;
  }

  template<typename T, typename U>
  bool readAttr(std::pair<T,U> &data, const QByteArray &ba, size_t &pos)
  {
    bool ok = readAttr(data.first,ba,pos);
    if(ok) ok = readAttr(data.second,ba,pos);
    return ok;
  }

  template<>
  inline bool readAttr(SymmetricTensor &data, const QByteArray &ba, size_t &pos)
  {
    return readPODVector<float>(data.ev1()[0], 9, ba, pos);
  }

  // Specialize for vertex
  // RSS: Maybe these 4 could be done with a pair of function templates
  template<>
  inline bool readAttr(vertex &v, const QByteArray &ba, size_t &pos)
  {
    uint vId = 0;
    if(!readPOD<uint>(vId, ba, pos))
      return false;
    v = vertex(0);
    if(vMap) {
      IntVtxMap::iterator it = vMap->find(vId);
      if(it != vMap->end())
        v = it->second;
      else
        Information::out << "Vertex not found reading attribute" << endl;
    }
    return true;
  }

  // Specialize for edge
  template<>
  inline bool readAttr(VtxVtxPair &pr, const QByteArray &ba, size_t &pos)
  {
    uint v1Id = 0, v2Id = 0;
    if(!readPOD<uint>(v1Id, ba, pos))
      return false;
    if(!readPOD<uint>(v2Id, ba, pos))
      return false;
    if(vMap) {
      IntVtxMap::iterator it = vMap->find(v1Id);
      if(it != vMap->end())
        pr.first = it->second;
      else {
        pr.first = vertex(0);
        Information::out << "Vertex not found reading edge attribute" << endl;
      }
      it = vMap->find(v2Id);
      if(it != vMap->end())
        pr.second = it->second;
      else {
        pr.second = vertex(0);
        Information::out << "Vertex not found reading edge attribute" << endl;
      }
    }
    return true;
  }

  // Specialize for triangle
  template<>
  inline bool readAttr(Triangle &t, const QByteArray &ba, size_t &pos)
  {
    uint v1Id = 0, v2Id = 0, v3Id = 0;
    if(!readPOD<uint>(v1Id, ba, pos))
      return false;
    if(!readPOD<uint>(v2Id, ba, pos))
      return false;
    if(!readPOD<uint>(v3Id, ba, pos))
      return false;
    vertex v1(0), v2(0), v3(0);
    if(vMap) {
      IntVtxMap::iterator it = vMap->find(v1Id);
      if(it != vMap->end())
        v1 = it->second;
      else
        Information::out << "Vertex 1 not found reading triangle attribute" << endl;
      it = vMap->find(v2Id);
      if(it != vMap->end())
        v2 = it->second;
      else
        Information::out << "Vertex 2 not found reading triangle attribute" << endl;
      it = vMap->find(v3Id);
      if(it != vMap->end())
        v3 = it->second;
      else
        Information::out << "Vertex 3 not found reading triangle attribute" << endl;
      t = Triangle(v1, v2, v3);
    }
    return true;
  }

  // Specialize for cell
  template<>
  bool inline readAttr(cell &c, const QByteArray &ba, size_t &pos)
  {
    uint cId = 0;
    if(!readPOD<uint>(cId, ba, pos))
      return false;
    c = cell(0);
    if(cMap) {
      IntCellMap::iterator it = cMap->find(cId);
      if(it != cMap->end())
        c = it->second;
      else
        Information::out << "Cell not found reading attribute" << endl;
    }
    return true;
  }

  // Specialize for wall
  template<>
  inline bool readAttr(CellCellPair &pr, const QByteArray &ba, size_t &pos)
  {
    uint c1Id = 0, c2Id = 0;
    if(!readPOD<uint>(c1Id, ba, pos))
      return false;
    if(!readPOD<uint>(c2Id, ba, pos))
      return false;
    pr.first = cell(0);
    pr.second = cell(0);
    if(cMap) {
      IntCellMap::iterator it = cMap->find(c1Id);
      if(it != cMap->end())
        pr.first = it->second;
      else
        Information::out << "Cell not found reading wall attribute" << endl;
      it = cMap->find(c2Id);
      if(it != cMap->end())
        pr.second = it->second;
      else
        Information::out << "Cell not found reading wall attribute" << endl;
    }
    return true;
  }

  // Specialize for QString
  template<>
  inline bool readAttr(QString &data, const QByteArray &ba, size_t &pos)
  {
    // get the string size
    //#pragma GCC diagnostic push
    //#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
    uint sz = 0;
    if(!readPOD<uint>(sz, ba, pos))
      return false;

    // Now read the data
    QByteArray qba(sz, 0);
    readChar(qba.data(), sz, ba, pos);
    data = QString::fromUtf8(qba);
    //#pragma GCC diagnostic pop
    return true;
  }
  // Specialize for QStringList
  template<>
  inline bool readAttr(QStringList &data, const QByteArray &ba, size_t &pos)
  {
    // get the stringList size
    uint sz = 0;
    if(!readPOD<uint>(sz, ba, pos))
      return false;
    //Loop over the QStringList
    for(uint i = 0; i < sz; i++){
      readAttr(data[i], ba, pos);
    }
    return true;
  }

  // Specialize for QByteArray
  template<>
  inline bool readAttr(QByteArray &qba, const QByteArray &ba, size_t &pos)
  {
    // get the data size
    uint sz = 0;
    if(!readPOD<uint>(sz, ba, pos))
      return false;

    // Now read the data
    qba.resize(sz);
    readChar(qba.data(), sz, ba, pos);
    return true;
  }


  // Specialize for Colorb
  template<>
  inline bool readAttr(Colorb &color, const QByteArray &ba, size_t &pos)
  {
    if(!readChar(&color[0], sizeof(Colorb), ba, pos))
      return false;
    return true;
  }

  // Standard vectors
  template <typename T>
  inline bool readAttr(std::vector<T> &data, const QByteArray &ba, size_t &pos)
  {
    // get the data size
    uint n = 0;
    if(!readPOD<uint>(n, ba, pos))
      return false;

    data.resize(n);
    for(size_t i = 0; i < n; ++i)
      if(!readAttr(data[i], ba, pos))
        return false;
    return true;
  }

  // Standard maps
  template <typename KeyT, typename ValueT>
  inline bool readAttr(std::map<KeyT, ValueT> &data, const QByteArray &ba, size_t &pos)
  {
    // get the data size
    uint n = 0;
    if(!readPOD<uint>(n, ba, pos))
      return false;

    for(size_t i = 0; i < n; ++i) {
      KeyT key;
      if(!readAttr(key, ba, pos))
        return false;
      ValueT value;
      if(!readAttr(value, ba, pos))
        return false;
      data[key] = value;
    }
    return true;
  }

  /**
    * Write the attribute value from a QByteArray
    */
  template<typename T>
  bool writeAttr(const T &data, QByteArray &ba)
  {
    return writePOD<T>(data, ba);
  }

  template<size_t n, size_t m, typename T>
  bool writeAttr(const Matrix<n, m, T> &data, QByteArray &ba)
  {
    for(size_t i = 0; i < n; ++i)
      if(!writePODVector<T>(data[i][0],  m, ba))
        return false;
    return true;
  }

  template<size_t n, typename T>
  bool writeAttr(const Vector<n, T> &data, QByteArray &ba)
  {
    return writePODVector<T>(data[0], n, ba);
  }

  template<size_t n>
  bool writeAttr(const Vector<n, Colorb> &data, QByteArray &ba)
  {
    for(size_t i = 0; i < n; ++i)
      if(!writeChar(&data[i][0], sizeof(Colorb), ba))
        return false;
    return true;
  }

  template<typename T,typename U>
  bool writeAttr(const std::pair<T,U> &data, QByteArray &ba)
  {
    bool ok = writeAttr(data.first,ba);
    if(ok) ok = writeAttr(data.second,ba);
    return ok;
  }

  template<>
  inline bool writeAttr(const SymmetricTensor &data, QByteArray &ba)
  {
    return writePODVector<float>(data.ev1()[0], 9, ba);
  }

  // Specialize for vertex
  template<>
  inline bool writeAttr(const vertex &v, QByteArray &ba)
  {
    if(v.isNull()) {
      Information::out << "writeAttr:Error, null vertex as key" << endl;
      writePOD<uint>(std::numeric_limits<uint>::max(), ba); // A hack, but what else can we do?
      return false;
    }
    return writePOD<uint>(v->saveId, ba);
  }

  // Specialize for edge
  template<>
  inline bool writeAttr(const VtxVtxPair &e, QByteArray &ba)
  {
    return writeAttr(e.first, ba) and writeAttr(e.second, ba);
  }

  // Specialize for triangle
  template<>
  inline bool writeAttr(const Triangle &t, QByteArray &ba)
  {
    return writePOD<uint>(t.v[0]->saveId, ba)
        and writePOD<uint>(t.v[1]->saveId, ba)
        and writePOD<uint>(t.v[2]->saveId, ba);
  }

  // Specialize for cell
  template<>
  bool inline writeAttr(const cell &c, QByteArray &ba)
  {
    if(c.isNull()) {
      Information::out << "writeAttr:Error, null cell as key" << endl;
      writePOD<uint>(std::numeric_limits<uint>::max(), ba); // A hack, but what else can we do?
      return false;
    }
    return writePOD<uint>(c->saveId, ba);
  }

  // Specialize for wall
  template<>
  inline bool writeAttr(const CellCellPair &w, QByteArray &ba)
  {
    return writeAttr(w.first, ba) and writeAttr(w.second, ba);
  }

  // Specialize for QString
  template<>
  inline bool writeAttr(const QString &s, QByteArray &ba)
  {
    QByteArray qba = s.toUtf8();
    uint sz = qba.size();
    return writeChar((char *)&sz, sizeof(uint), ba)
        and writeChar((char *)qba.data(), sz, ba);
  }

  // Specialize for QStringList
  template<>
  inline bool writeAttr(const QStringList &s, QByteArray &ba)
  {
    bool result = true;
    for(int i = 0; i < s.size(); i++){
      result &= writeAttr(s[i], ba);
    }
    return result;
  }

  // Specialize for QByteArray
  template<>
  inline bool writeAttr(const QByteArray &qba, QByteArray &ba)
  {
    uint sz = qba.size();
    return writeChar((char *)&sz, sizeof(uint), ba)
        and writeChar((char *)qba.data(), sz, ba);
  }

  // Specialize for Colorb
  template<>
  inline bool writeAttr(const Colorb &color, QByteArray &ba)
  {
    if(!writeChar(&color[0], sizeof(Colorb), ba))
      return false;
    return true;
  }

  // Standard vectors
  template <typename T>
  bool writeAttr(const std::vector<T> &data, QByteArray &ba)
  {
    uint n = data.size();
    if(!writeAttr(n, ba))
      return false;

    for(size_t i = 0; i < n; ++i)
      if(!writeAttr(data[i], ba))
        return false;
    return true;
  }

  // Standard maps
  template <typename KeyT, typename ValueT>
  bool writeAttr(const std::map<KeyT, ValueT> &data, QByteArray &ba)
  {
    uint n = data.size();
    if(!writeAttr(n, ba))
      return false;

    for(typename std::map<KeyT, ValueT>::const_iterator i = data.begin(); i != data.end(); ++i) {
      if(!writeAttr(i->first, ba))
        return false;
      if(!writeAttr(i->second, ba))
        return false;
    }
    return true;
  }

  /**
   * \class AttrMap Attributes.hpp <Attributes.hpp>
   * \brief Attribute map wraps std::map
   *
   * The following customizations are made to std::map's behavior:
   * - you can assign a default value, which is normally obtained with the default constructor
   * - if you read an entry (const []) that does not exist, it does not create a new one, it
   *   returns a reference to the default value.
   * - the ->* operator will be defined to allow nicer syntax, mostly when dealing with vertices
   * - when new entries are added to the map, the insert is locked from other threads
   *
   * Note that this does not make the class thread safe, but reads and writes that go to
   * different entries are safe.
   */

  template<typename KeyT, typename ValueT>
  class AttrMap
  {
  public:
    typedef std::pair<KeyT, ValueT> pair;
    typedef typename STD_MAP<KeyT, ValueT>::iterator iterator;
    typedef typename STD_MAP<KeyT, ValueT>::const_iterator const_iterator;

    // Constructor
    AttrMap() : _defaultVal(ValueT())
    {
      omp_init_lock(&_lock);
    };
    explicit AttrMap(const ValueT &defaultVal) : _defaultVal(defaultVal)
    {
      omp_init_lock(&_lock);
    };

    // Range constructor
    template<typename IterT>
    AttrMap(IterT first, IterT last, const ValueT &defaultVal = ValueT()) : _defaultVal(defaultVal)
    {
      _map.insert(first, last);
      omp_init_lock(&_lock);
    }

    // Copy constructor
    AttrMap(const AttrMap &a) : _map(a._map), _defaultVal(a._defaultVal)
    {
      omp_init_lock(&_lock);
    }

    // Assignment
    AttrMap &operator=(const AttrMap &a)
    {
      _map = a._map;
      _defaultVal = a._defaultVal;

      return *this;
    }

    // Destructor
    ~AttrMap()
    {
    }

    /**
     * const Accessor
     *
     * If value does not exist, return a reference to the default value. Unlike map,
     * this method is const
     */
    const ValueT &operator[](const KeyT &key) const
    {
      const_iterator it = _map.find(key);
      if(it == _map.end())
        return _defaultVal;
      return it->second;
    }

    /**
     * Accessor
     *
     * Put a lock around this for omp. Note that this will not protect an iterator somewhere
     * else.
     */
    ValueT &operator[](const KeyT &key)
    {
      return _map[key];
    }

    /**
     * Counterpart to map::at(), which returns reference to default value if key is not found.
     */
    const ValueT &at(const KeyT &key) const
    {
      return (*this)[key];
    }

    /**
     * Get a reference to the default value
     */
    const ValueT &defaultVal() const
    {
      return _defaultVal;
    };
    ValueT &defaultVal()
    {
      return _defaultVal;
    };

// RSS The map should not be called directly, it may change
//    /**
//     * Get a reference to the map
//     */
//    const STD_MAP<KeyT, ValueT> &map() const
//    {
//      return _map;
//    };
//    STD_MAP<KeyT, ValueT> &map()
//    {
//      return _map;
//    };

    /**
     * The default value
     */
    ValueT &defaulVal()
    {
      return _defaultVal;
    }
    const ValueT &defaulVal() const
    {
      return _defaultVal;
    }

    // The rest are boilerplate methods.
    size_t size() const { return _map.size(); }
    bool empty() const { return _map.empty(); }
    size_t count (const KeyT &key) const { return _map.count(key); }
    iterator begin() { return _map.begin(); }
    const_iterator begin() const { return _map.begin(); }
    iterator end() { return _map.end(); }
    const_iterator end() const { return _map.end(); }
    iterator find(const KeyT &key) { return _map.find(key); }
    const_iterator find(const KeyT &key) const { return _map.find(key); }

    // RSS we need to clear the cache if the methods exist
    void erase(iterator pos) { _map.unsafe_erase(pos); }
    size_t erase(const KeyT &key) { return _map.unsafe_erase(key); }
    void erase(iterator first, iterator last) { _map.unsafe_erase(first, last); }

    void clear() { _map.clear(); }
    template<typename IterT>
    void insert(IterT first, IterT last) { _map.insert(first, last); }

  private:
    STD_MAP<KeyT, ValueT, std::hash<KeyT> > _map;
    omp_lock_t _lock;
    ValueT _defaultVal;
  };

  // Convenience typedefs
  typedef AttrMap<int, int> IntIntAttr;
  typedef AttrMap<int, float> IntFloatAttr;
  typedef AttrMap<int, Point3f> IntPoint3fAttr;
  typedef AttrMap<int, Colorb> IntColorbAttr;
  typedef AttrMap<int, Vec2Colorb> IntVec2ColorbAttr;
  typedef AttrMap<int, Vec3Colorb> IntVec3ColorbAttr;
  typedef AttrMap<int, Matrix3f> IntMatrix3fAttr;
  typedef AttrMap<int, SymmetricTensor> IntSymTensorAttr;
  typedef AttrMap<int, QString> IntQStringAttr;
  typedef AttrMap<IntIntPair, float> IntIntFloatAttr;
  typedef AttrMap<IntIntPair, Point3f> IntIntPoint3fAttr;

  typedef AttrMap<vertex, int> VtxIntAttr;
  typedef AttrMap<vertex, bool> VtxBoolAttr;
  typedef AttrMap<vertex, float> VtxFloatAttr;
  typedef AttrMap<vertex, Point2f> VtxPoint2fAttr;
  typedef AttrMap<vertex, Point3f> VtxPoint3fAttr;
  typedef AttrMap<vertex, Colorb> VtxColorbAttr;
  typedef AttrMap<vertex, Vec2Colorb> VtxVec2ColorbAttr;
  typedef AttrMap<vertex, Vec3Colorb> VtxVec3ColorbAttr;
  typedef AttrMap<vertex, Matrix3f> VtxMatrix3fAttr;
  typedef AttrMap<vertex, SymmetricTensor> VtxSymTensorAttr;
  typedef AttrMap<vertex, QString> VtxQStringAttr;

  typedef AttrMap<edge, int> EdgeIntAttr;
  typedef AttrMap<edge, bool> EdgeBoolAttr;
  typedef AttrMap<edge, float> EdgeFloatAttr;
  typedef AttrMap<edge, Point2f> EdgePoint2fAttr;
  typedef AttrMap<edge, Point3f> EdgePoint3fAttr;
  typedef AttrMap<edge, Colorb> EdgeColorbAttr;
  typedef AttrMap<edge, Vec2Colorb> EdgeVec2ColorbAttr;
  typedef AttrMap<edge, Vec3Colorb> EdgeVec3ColorbAttr;
  typedef AttrMap<edge, Matrix3f> EdgeMatrix3fAttr;
  typedef AttrMap<edge, SymmetricTensor> EdgeSymTensorAttr;
  typedef AttrMap<edge, QString> EdgeQStringAttr;

  typedef AttrMap<cell, int> CellIntAttr;
  typedef AttrMap<cell, bool> CellBoolAttr;
  typedef AttrMap<cell, float> CellFloatAttr;
  typedef AttrMap<cell, Point2f> CellPoint2fAttr;
  typedef AttrMap<cell, Point3f> CellPoint3fAttr;
  typedef AttrMap<cell, Colorb> CellColorbAttr;
  typedef AttrMap<cell, Vec2Colorb> CellVec2ColorbAttr;
  typedef AttrMap<cell, Vec3Colorb> CellVec3ColorbAttr;
  typedef AttrMap<cell, Matrix3f> CellMatrix3fAttr;
  typedef AttrMap<cell, SymmetricTensor> CellSymTensorAttr;
  typedef AttrMap<cell, QString> CellQStringAttr;

  typedef AttrMap<wall, int> WallIntAttr;
  typedef AttrMap<wall, bool> WallBoolAttr;
  typedef AttrMap<wall, float> WallFloatAttr;
  typedef AttrMap<wall, Point2f> WallPoint2fAttr;
  typedef AttrMap<wall, Point3f> WallPoint3fAttr;
  typedef AttrMap<wall, Colorb> WallColorbAttr;
  typedef AttrMap<wall, Vec2Colorb> WallVec2ColorbAttr;
  typedef AttrMap<wall, Vec3Colorb> WallVec3ColorbAttr;
  typedef AttrMap<wall, Matrix3f> WallMatrix3fAttr;
  typedef AttrMap<wall, SymmetricTensor> WallSymTensorAttr;
  typedef AttrMap<wall, QString> WallQStringAttr;

  typedef AttrMap<Triangle, int> TriIntAttr;
  typedef AttrMap<Triangle, bool> TriBoolAttr;
  typedef AttrMap<Triangle, float> TriFloatAttr;
  typedef AttrMap<Triangle, Point2f> TriPoint2fAttr;
  typedef AttrMap<Triangle, Point3f> TriPoint3fAttr;
  typedef AttrMap<Triangle, Colorb> TriColorbAttr;
  typedef AttrMap<Triangle, Vec2Colorb> TriVec2ColorbAttr;
  typedef AttrMap<Triangle, Vec3Colorb> TriVec3ColorbAttr;
  typedef AttrMap<Triangle, Matrix3f> TriMatrix3fAttr;
  typedef AttrMap<Triangle, SymmetricTensor> TriSymTensorAttr;
  typedef AttrMap<Triangle, QString> TriQStringAttr;

  typedef AttrMap<QString, int> QStringIntAttr;
  typedef AttrMap<QString, float> QStringFloatAttr;
  typedef AttrMap<QString, Point3f> QStringPoint3fAttr;
  typedef AttrMap<QString, Colorb> QStringColorbAttr;
  typedef AttrMap<QString, Vec2Colorb> QStringVec2ColorbAttr;
  typedef AttrMap<QString, Vec3Colorb> QStringVec3ColorbAttr;
  typedef AttrMap<QString, Matrix3f> QStringMatrix3fAttr;
  typedef AttrMap<QString, SymmetricTensor> QStringSymTensorAttr;
  typedef AttrMap<QString, QString> QStringQStringAttr;

  /**
   * \class Attributes Attributes.hpp <Attributes.hpp>
   * \brief Holds a set of attributes
   *
   * Attributes are a collection of maps (Attribute objects).
   * FIXME: what are the constraints on attributes?
   */
  class mgx_EXPORT Attributes
  {
  private:
    // These next two classes are only used from Attributes
    /**
     * \class AttributeBase Atributes.hpp <Attributes.hpp>
     * \brief Base class for attributes system.
     */
    class AttributeBase
    {
    public:
      /**
       * Default constructor of named attribute
       */
      AttributeBase(const QString &name) : _name(name)
      {
        //Information::out << "Creating attribute:" << _name << endl; //NB
      }

      /**
       * Attribute classes are derived
       */
      virtual ~AttributeBase()
      {
        //Information::out << "Deleting attribute:" << _name << endl;
      }

      /**
       * Get the name of the Attribute
       */
      const QString &name() { return _name; }

      /**
       * Write the attribute to a byte array
       */
      virtual bool write(QByteArray &) { return true; }

      /**
       * Clear the map
       */
      virtual void clear() {}

      /**
       * Size
       */
      virtual size_t size() = 0;

      
    private:
      QString _name;
    };
    /**
     * \class Attribute Attributes.hpp <Attributes.hpp>
     * \brief Derived template class for attributes system.
     *
     * Holds a map of key-value pairs.
     * FIXME: What are the constraints on the pairs?
     * FIXME: Why is it called singular Attribute if it holds a collection?
     */
    template <typename KeyT, typename ValueT>
    class Attribute : public AttributeBase
    {
    public:
      /**
       * Constructor
       * \param name Name of the attribute
       */
      Attribute(const QString &name, const ValueT &defaultVal = ValueT())
        : AttributeBase(name), _map(defaultVal) {}

      /**
       * Destructor
       */
      ~Attribute() {}

      /**
       * Return the map
       */
      AttrMap<KeyT, ValueT> &map() { return _map; }
      const AttrMap<KeyT, ValueT> &map() const { return _map; }

      /**
       * Write the attribute map a file
       * \param file The file to write
       */
      bool write(QByteArray &ba)
      {
        ba.clear();

        // Count the non-default entries
        size_t count = 0;
        for(typename AttrMap<KeyT, ValueT>::iterator i = _map.begin(); i != _map.end(); ++i)
          if(i->second == _map.defaultVal())
            continue;
          else
            count++;

        // Write the count
        writeAttr(count, ba);

        // Now write the map
        for(typename AttrMap<KeyT, ValueT>::iterator i = _map.begin(); i != _map.end(); ++i) {
          // Only write the non-default entries.
          if(i->second == _map.defaultVal())
            continue;

          // Write the key and value types
          if(writeAttr(i->first, ba)) {
            writeAttr(i->second, ba);
          } else {
            Information::out << "Attributes::write Error, key write failed" << endl; 
            writeAttr(_map.defaultVal(), ba); // Happens if key write fails, like for vertices if null
          }
          progressAdvance();
        }

        // Write the default value
        writeAttr(_map.defaultVal(), ba);

        return true;
      }

      /**
       * Read the attribute map from the QByteArray read from the file
       *
       * \param file The file to write
       */
      bool read(QByteArray &ba)
      {
        size_t pos = 0;

        // Read the count
        size_t count = 0;
        if(!readAttr(count, ba, pos))
          return false;

        // Key is overwritten by readAttr and does not need to be initialized.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
        // Read in the map
        for(size_t i = 0; i < count; ++i) {
          KeyT key;
          readAttr(key, ba, pos);
          ValueT value;
          readAttr(value, ba, pos);
          _map[key] = value;
          progressAdvance();
        }
        // Read the default value
        ValueT value;
        if(!readAttr(value, ba, pos))
          return false;
        _map.defaultVal() = value;
#pragma GCC diagnostic pop

        return true;
      }

      /**
       * Clear the attribute map
       */
      void clear()
      {
        _map.clear();
      }

      /**
       * Size
       */
      virtual size_t size() { return _map.size(); }


    private:
      AttrMap<KeyT, ValueT> _map;
    };

  public:
    /**
     * Constructor
     * \param name Name of the attribute
     */
    Attributes() : _attributes(0), _saveRequired(true), _vMapAttr(0), _cMapAttr(0)
    {
      omp_init_lock(&_lock);
    }
    /**
     * Destructor
     */
    virtual ~Attributes ()
    {
      clear();
    }

    /**
     * Get the attribute, if it does not exist create it and add to the set
     * If it exists, and if so load it from the byte array
     * \param name Name of the attribute
     */
    template <typename KeyT, typename ValueT>
    AttrMap<KeyT, ValueT>&attrMap(const QString &name, bool saveRequired = true)
    {
      Attribute<KeyT, ValueT> *a = dynamic_cast<Attribute<KeyT, ValueT> *>(_attributes[name]);
      if(!a) {
        // If the previous attribute with this name was a different type, delete it
        // and print a warning, if it is a system attribute throw an error
        if(_attributes[name]) {
          Information::out << "Warning, changing type for attribute: " << name << endl;
          delete _attributes[name];
        }
        a = new Attribute<KeyT, ValueT>(name);
        _attributes[name] = a;
      }
      // Check if attribute is loaded from byte array
      if(_ba[name].size() > 0) {
        vMap = _vMapAttr;
        cMap = _cMapAttr;
        // If there is a read error, it probably means it has a different layout, clear it
        if(!a->read(_ba[name])) {
          a->clear();
          Information::out << "Read error for attribute: " << name << ", clearing" << endl;
        } else
          Information::out << "Loaded attribute: " << name << ", Size:" << a->map().size() << endl;
        _ba[name].clear();
      }
      if(saveRequired)
        _saveRequired[name] = true;

      return a->map();
    }

    /**
     * Try to get a constant pointer to an attribute. Return null if nothing exists under name or if existing data has another type.
     */
    template <typename KeyT, typename ValueT>
    const AttrMap<KeyT, ValueT>* getAttrMap(const QString &name) const
    {
      const AttributeBase *attrbase = _attributes[name];
      const Attribute<KeyT, ValueT> *a = dynamic_cast<const Attribute<KeyT, ValueT> *>(attrbase);
      if (!a) {
        return 0;
      }
      return &a->map();
    }

    /**
     * Get a reference to the attribute's save status, it will be created if it doesn't exist
     * \param name Name of the attribute
     */
    bool saveRequired(const QString &name) const
    {
      return _saveRequired[name];
    }
    bool setSaveRequired(const QString &name, bool saveReq)
    {
      return _saveRequired[name] = saveReq;
    }

    /**
     * Erase an attribute
     * \param name Name of the attribute
     */
    void erase(const QString &name)
    {
      AttributeBase *a = _attributes[name];
      if(a)
        delete a;
      _attributes.erase(name);
    }


    /**
     * Clear the attributes
     * \param name Name of the attribute
     */
    void clear()
    {
      // First erase the allocated items
      for(STD_MAP<QString, AttributeBase*>::iterator
          a = _attributes.begin(); a != _attributes.end(); ) {
        if(a->second)
          delete a->second;
        // Remove the maps
        AttrMap<QString, bool>::iterator saveIt = _saveRequired.find(a->first);
        if(saveIt != _saveRequired.end())
          _saveRequired.erase(saveIt);
        AttrMap<QString, QByteArray>::iterator baIt = _ba.find(a->first);
        if(baIt != _ba.end())
          _ba.erase(baIt);

        // and the attribute
        _attributes.erase(a++);
      }

      // Clear the vertex and cell maps
      if(_vMapAttr)
        delete _vMapAttr;
      _vMapAttr = 0;
      if(_cMapAttr)
        delete _cMapAttr;
      _cMapAttr = 0;
    }

    /**
     * Write the attributes to a file
     * \param file The file to write
     */
    bool write(QIODevice &file)
    {
      // Find the number of attributes to save
      typedef std::pair<QString, bool> QStringBoolPair;
      size_t count = 0;
      forall(const QStringBoolPair &i, _attributes)
        if(_saveRequired[i.first])
          count++;
      writeFile(file, count);

      // Write the starting locations of each attribute, we'll fill them in as we go
      qint64 attrPos = file.pos();
      file.seek(attrPos + (count + 1) * sizeof(size_t));

      if(count > 0)
        Information::out << "Writing attributes:";
      
      typedef std::pair<QString, AttributeBase*> QStringAttrBasePr;
      forall(const QStringAttrBasePr &i, _attributes) {
        QString name = i.first;
        if(!_saveRequired[name])
          continue;

        // Put address of attribute in table at the start
        qint64 currentPos = file.pos();
        file.seek(attrPos);
        writeFile(file, size_t(currentPos));
        attrPos = file.pos();
        file.seek(currentPos);
        
        AttributeBase* ab = i.second;

        progressSetMsg(QString("Writing attribute: %1").arg(name));

        // Write the name
        writeFile(file, name);

        // If it was converted call the writer, otherwise just write the bytes.
        if(_ba[name].size() == 0) {
          QByteArray ba;
          ab->write(ba);
          file.write((char *)ba.data(),ba.size());
          Information::out << " " << name << ":" << ba.size();
        } else {
          file.write((char *)_ba[name].data(),_ba[name].size());
          Information::out << " " << name << ":" << _ba[name].size();
        }
      }
      if(count > 0)
        Information::out << "." << endl;
      // Put address of attribute in table at the start
      qint64 currentPos = file.pos();
      file.seek(attrPos);
      writeFile(file, size_t(currentPos));
      file.seek(currentPos);

      return true;
    }

    /**
     * Read the attributes from a file.
     * \param file The file to read
     * \param clearAttr Clear the existing attributes first
     */
    bool read(QIODevice &file, bool clearAttr = true)
    {
      // First clear whats there
      if(clearAttr)
        clear();

      // Read the number of attributes
      size_t count;
      readFile(file, count);

      // Read the starting location of each attribute, the last one is the end
      std::vector<size_t> attrPos;
      for(size_t i = 0; i <= count; ++i) {
        size_t pos;
        readFile(file, pos);
        attrPos.push_back(pos);
      }

      if(count > 0)
        Information::out << "Read attributes:";

      for(size_t i = 0; i < count; ++i) {
        // Go to attribute
        file.seek(attrPos[i]);

        // Read the name
        QString name;
        readFile(file, name);

        progressSetMsg(QString("Reading attribute: %1").arg(name));

        // If attributes exist for this name, clear them
        if(_attributes[name]) {
          delete _attributes[name];
          _attributes[name] = 0;
        }

        // Read in the data
        size_t sz = attrPos[i+1] - attrPos[i];
        QByteArray ba(sz, 0);
        file.read(ba.data(), sz);
        _ba[name] = ba;
        _saveRequired[name] = true;
        if(qint64(sz) != _ba[name].size())
          Information::out << endl << QString(
                                "Warning, chars read (%1) doesn't match file size (%2) for attribute: %3")
                              .arg(_ba[name].size()).arg(sz).arg(name) << endl;
        else
          Information::out << " " << name << ":" << ba.size();
      }
      if(count > 0)
        Information::out << "." << endl;
      file.seek(attrPos[count]);

      return true;
    }

    /**
     * Read the attributes from a file
     * \param file The file to read
     * \param vMap Map saveIds to vertices
     * \param clear Clear the existing attributes first
     */
    bool read(QIODevice &file, IntVtxMap *vMap, IntCellMap *cMap, bool clearAttr = true)
    {
      if(!read(file, clearAttr))
        return false;
      saveVtxCellMaps(vMap, cMap, clearAttr);
      return true;
    }


    /**
     * Set the saveId-vertex map for loading vertex and Index attributes
     * Since the readAttr and writeAttr are global functions, attributes with
     * vertex, or cell keys will need these tables
     */
    bool saveVtxCellMaps(IntVtxMap *vMap, IntCellMap *cMap, bool clearAttr)
    {
      // Grab a copy of the vertex map, should only be called when loading the file
      // RSS Note that this will also keep the vertices alive, can we delete it at some point?
      if(vMap and _vMapAttr and clearAttr) {
        delete _vMapAttr;
        _vMapAttr = 0;
      }
      if(cMap and _cMapAttr and clearAttr) {
        delete _cMapAttr;
        _cMapAttr = 0;
      }
      if(vMap and !_vMapAttr)
        _vMapAttr = new IntVtxMap;
      if(cMap and !_cMapAttr)
        _cMapAttr = new IntCellMap;

      // Copy the data
      if(vMap)
        *_vMapAttr = *vMap;
      if(cMap)
        *_cMapAttr = *cMap;

      return true;
    }

    virtual size_t size(QString _name)
    {
      if(_name.size() != 0 and _attributes.find(_name) != _attributes.end()){
        if(_attributes[_name] != 0)
          return _attributes[_name]->size();
      }
      return 0;
    }

    /**
     * Return a list of attribute names
     */
    QStringList getAttrList() const //NB
    {
      QStringList attrList;
      //Iterate over the _attributes map to get list of attributes
      for(AttrMap<QString, AttributeBase*>::const_iterator i = _attributes.begin(); i != _attributes.end(); ++i) {
        //qDebug() <<"\nQString in getAttrList\t" <<i->first ;
        
        if(_ba.find(i->first) != _ba.end())
          attrList << i->first;
      }
      return attrList;
    }

    /**
     * Return a list of unparsed attribute names
     */
    QStringList clearNotparsedAttrList() const //NB
    {
      QStringList attrList;
      //Iterate over the _attributes map to get list of attributes
      for(AttrMap<QString, AttributeBase*>::const_iterator i = _attributes.begin(); i != _attributes.end(); ++i) {
        //qDebug() <<"\nQString in getAttrList\t" <<i->first ;
        if(_ba.find(i->first) != _ba.end() and _attributes.find(i->first) != _attributes.end()){  //Attributes that are parsed are put in the tree, rest removed -- check size : TODO
          if(_ba[i->first].size() == 0 and _attributes[i->first]->size() > 0)
            attrList <<i->first;
        }
      }
      //qDebug() <<"\nclearNotparsedAttrList\t" << attrList.size();
      return attrList;
    }

  private:
    // Attribute data and meta-data
    AttrMap<QString, AttributeBase*> _attributes;
    AttrMap<QString, QByteArray> _ba;
    AttrMap<QString, bool> _saveRequired;

    // Pointer to vertex map to decode saveIds
    IntVtxMap *_vMapAttr;
    IntCellMap *_cMapAttr;

    omp_lock_t _lock;
  };

  // Syntax sugar for attributes.
  template<typename KeyT, typename ValueT>
  ValueT &operator->*(const KeyT &key, AttrMap<KeyT, ValueT> &map)
  {
    return map[key];
  }
  template<typename KeyT, typename ValueT>
  ValueT &operator->*(const KeyT &key, AttrMap<KeyT, ValueT> *map)
  {
    return (*map)[key];
  }

  // Since VV edges are second-class, walls and edges need special treatment
  template<typename ValueT>
  ValueT &operator->*(const wall &e, AttrMap<CellCellPair, ValueT> *map)
  {
    return (*map)[CellCellPair(cell(e.source()), cell(e.target()))];
  }
  template<typename ValueT>
  ValueT &operator->*(const wall &e, AttrMap<CellCellPair, ValueT> &map)
  {
    return map[CellCellPair(cell(e.source()), cell(e.target()))];
  }

  template<typename ValueT>
  ValueT &operator->*(const edge &e, AttrMap<VtxVtxPair, ValueT> &map)
  {
    return map[VtxVtxPair(vertex(e.source()), vertex(e.target()))];
  }
  template<typename ValueT>
  ValueT &operator->*(const edge &e, AttrMap<VtxVtxPair, ValueT> *map)
  {
    return (*map)[VtxVtxPair(vertex(e.source()), vertex(e.target()))];
  }
}
#endif 
