/////////////////////////////////////////////////////////////
//                                                         //
// Copyright (c) 2007-2017 by The University of Queensland //
// Centre for Geoscience Computing                         //
// http://earth.uq.edu.au/centre-geoscience-computing      //
//                                                         //
// Primary Business: Brisbane, Queensland, Australia       //
// Licensed under the Open Software License version 3.0    //
// http://www.apache.org/licenses/LICENSE-2.0              //
//                                                         //
/////////////////////////////////////////////////////////////

#ifndef __MNTCELL_H
#define __MNTCELL_H

// --- STL includes ---
#include <vector>
#include <map>
#include <utility>

using std::vector; 
using std::map;
using std::multimap;
using std::pair;

// --- IO includes ---
#include <iostream>

using std::ostream;

// --- Project includes ---
#include "geometry/Sphere.h"
#include "AVolume.h"


struct BondWithPos
{
  int first;
  int second;
  Vector3 firstPos;
  Vector3 secondPos;
};

/*!
  \class MNTCell
  \brief class for a cell in a neighbor table (MNTable2D or MNTable3D)
*/
class MNTCell
{
 private:
  vector<vector<Sphere> > m_data;
  static int s_output_style;

 public:
  MNTCell(unsigned int ngroups=1);

  void SetNGroups(unsigned int);

  void insert(const Sphere&,int);
  int NParts() const;

  const multimap<double,const Sphere*> getSpheresNear(const Vector3&,double) const;
  const multimap<double,const Sphere*> getSpheresFromGroupNear(const Vector3&,double,int) const;
  multimap<double,Sphere*> getSpheresFromGroupNearNC(const Vector3&,double,int);
  const multimap<double,const Sphere*> getSpheresClosestTo(const Vector3&,unsigned int) const;
  const Sphere* getClosestSphereFromGroup(const Sphere&,int,double) const;  
  const Sphere* getClosestSphereFromGroup(const Sphere&,int,double,double,double,double) const;  
  Sphere* getClosestSphereFromGroup(const Vector3&,int,double);  
  const vector<const Sphere*> getAllSpheresFromGroup(int) const;
  vector<Sphere*> getAllSpheresFromGroupNC(int);
  const vector<Sphere*> getSpheresNearObject(const AGeometricObject*,double,unsigned int); 
  const vector<Sphere*> getSpheresInVolume(const AVolume*,unsigned int); 

  vector<int> getIdList();
  void remove(int,int);
  void removeTagged(int,int,int);
  void removeInVolume(const AVolume*,int,bool);
  void tagSpheresNear(const Vector3&,double,int,int); 
  void tagClosestParticle(const Vector3&,int,int); 
  void tagSpheresInGroup(int,int,int);
  int renumberParticlesContinuous(int);


  // get bonds
  vector<pair<int,int> > getBonds(int,double);
  vector<pair<int,int> > getBonds(int,double,const MNTCell&);
  vector<BondWithPos> getBondsWithPos(int,double);
  vector<BondWithPos> getBondsWithPos(int,double,const MNTCell&);
  vector<pair<int,int> > getBonds(int,double,int,int);
  vector<pair<int,int> > getBonds(int,double,const MNTCell&,int,int);
  vector<pair<int,int> > getBondsSame(int,double);
  vector<pair<int,int> > getBondsSame(int,double,const MNTCell&);
  vector<pair<int,int> > getBondsDiff(int,double);
  vector<pair<int,int> > getBondsDiff(int,double,const MNTCell&);
  vector<pair<int,int> > getBondsTagged(int,double,int,int);
  vector<pair<int,int> > getBondsTagged(int,double,const MNTCell&,int,int);
  vector<pair<int,int> > getBondsTaggedMasked(int,double,int,int,int,int);
  vector<pair<int,int> > getBondsTaggedMasked(int,double,const MNTCell&,int,int,int,int);
  
  // output 
  double getSumVolume2D(unsigned int);
  double getSumVolume3D(unsigned int);
  int getNrParticles(int);
  static void SetOutputStyle(int);
  friend ostream& operator << (ostream&,const MNTCell&);
  void writePositions(ostream&);
  void writeRadii(ostream&);
  void writeIDs(ostream&);
  void writeTags(ostream&);
  // for block writing
  unsigned int writeParticlesInBlock(ostream&, const Vector3&, const Vector3&);
};

#endif // MNTCELL_H
