/////////////////////////////////////////////////////////////
//                                                         //
// 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              //
//                                                         //
/////////////////////////////////////////////////////////////

// --- Project includes ---
#include "HGrainGenerator.h"

// --- system includes ---
#include <cmath>

using std::ceil;
using std::sqrt;

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

HGrainGenerator2D::HGrainGenerator2D()
{}

/*!
  constructor

  \param rad particle radius
*/
HGrainGenerator2D::HGrainGenerator2D(double rad)
{
  m_rad=rad;
}

/*!
  destructor - do nothing
*/
HGrainGenerator2D::~HGrainGenerator2D()
{}

/*!
  generate packing
  
  \param vol a pointer to the volume in which the packing is generated
  \param ntable a pointer to the neighbour table used 
  \param gid particle group id
  \param tag the particle tag
*/
void HGrainGenerator2D::generatePacking(AVolume2D* vol,MNTable2D* ntable,int gid, int tag) 
{
  const double tol=1e-5;
  // --- GENERATE REGULAR LATTICE ---
  // get bounding box
  pair<Vector3,Vector3> bbx=vol->getBoundingBox();
  double dx=(bbx.second.X()-bbx.first.X())-2.0*m_rad;
  double dy=(bbx.second.Y()-bbx.first.Y())-2.0*m_rad;
  // get index limits for seeding
  int imax=int(floor(dx/(m_rad*2.0)))+1;
  if(dx-(double(imax)*m_rad*2.0)>0.5*m_rad) imax++;
  int jmax=int(floor(dy/(m_rad*sqrt(3.0))))+1;
  // check if odd or even
  bool even=(dx-(double(imax)*m_rad*2.0)>0.5*m_rad);
  std::cout << "imax, jmax, even  " << imax << " " << jmax << " " << even << std::endl; 
  // seed positions
  for(int i=0;i<imax-1;i++){
    for(int j=0;j<jmax;j++){
      // get position
      double px=bbx.first.X()+tol+m_rad+(double(i)+0.5*double(j%2))*m_rad*2.0;
      double py=bbx.first.Y()+tol+m_rad+double(j)*sqrt(3.0)*m_rad;

      Sphere S(Vector3(px,py,0.0),m_rad);
      S.setTag(tag);
      ntable->insert(S,gid);	
    }
  }
  for(int j=0;j<jmax;j++){
    if(even || ((j%2)==0)){
      // get position
      double px=bbx.first.X()+tol+m_rad+(double(imax-1)+0.5*double(j%2))*m_rad*2.0;
      double py=bbx.first.Y()+tol+m_rad+double(j)*sqrt(3.0)*m_rad;

      Sphere S(Vector3(px,py,0.0),m_rad);
      S.setTag(tag);
      ntable->insert(S,gid);	
    }
  }
  // pick hex grains
  if(!even){
    double x0=bbx.first.X()+tol+4.0*m_rad;
    double y0=bbx.first.Y()+tol+(1.0+sqrt(3.0))*m_rad;
    double dxi=5.0*m_rad;
    double dxi3=m_rad;
    double dyi=sqrt(3.0)*m_rad;
    int igmax=ceil(dx/5.0*m_rad);
    double dxj=m_rad;
    double dyj=3.0*sqrt(3.0)*m_rad;
    double dyj3=sqrt(3.0)*m_rad;
    int jgmax=ceil(dy/(3.0*sqrt(3.0))*m_rad);
    for(int i=0;i<igmax;i++){
      for(int j=0;j<jgmax;j++){
	double px=x0+double(i)*dxi-double(i/3)*dxi3+double(j%5)*dxj;
	double py=y0+double(i%3)*dyi+double(j)*dyj-double(j/5)*dyj3;
	// check if far enough inside
	if((px-bbx.first.X()>=3.0*m_rad) && 
	   (bbx.second.X()-px>=3.0*m_rad)&& 
	   (py-bbx.first.Y()>=(sqrt(3.0)+1.0)*m_rad) &&
	   (bbx.second.Y()-py>=(sqrt(3.0)+1.0)*m_rad)){
	  ntable->tagParticlesNear(Vector3(px,py,0.0),m_rad+tol,gid,2);
	  ntable->generateBondsWithMask(gid,tol,2,2,2);
	  ntable->tagParticlesNear(Vector3(px,py,0.0),m_rad+tol,gid,1);
	}
      }
    }
  }
  ntable->removeTagged(gid,0,7);
}

ostream& operator << (ostream& ost,const HGrainGenerator2D& T)
{
   return ost;
}

