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

#include "granular_gouge.h"

// --- project includes ---
#include "BoxWithPlanes3D.h"
#include "TriBox.h"
#include "Plane.h"
#include "MNTable3D.h"
#include "SphereVol.h"
#include "src/InsertGenerator3D.h"

/*!
  \param NT
  \param G
  \param xt total x-dim
  \param yt total y-dim
  \param zt total z-dim
  \param y0 y-offset
  \param nr nr raised ridges
  \param yr height of ridges
*/
void generate_upper_rough_block(MNTable3D* NT,
				AGenerator3D* G,
				double xt,
				double yt,
				double zt,
				double y0,
				int nr,
				double yr)
{
  Vector3 min=Vector3(0.0,y0,0.0); 
  Vector3 min_2=Vector3(0.0,y0+yr,0.0); 
  Vector3 max=Vector3(xt,y0+yt,zt);
  double xr=xt/(2.0*double(nr));

  Plane top_plane=Plane(max,Vector3(0.0,-1.0,0.0));
  Plane bottom_plane_1=Plane(min,Vector3(0.0,1.0,0.0));
  Plane bottom_plane_2=Plane(min_2,Vector3(0.0,1.0,0.0));
  Plane front_plane=Plane(min,Vector3(0.0,0.0,1.0));
  Plane back_plane=Plane(max,Vector3(0.0,0.0,-1.0));
  Plane left_plane=Plane(min,Vector3(1.0,0.0,0.0));
  Plane right_plane=Plane(max,Vector3(-1.0,0.0,0.0));

  Vector3 diff(1.0,0.0,0.0);
  BoxWithPlanes3D Box(min_2-diff,max+diff);
  
  Box.addPlane(top_plane);
  Box.addPlane(bottom_plane_2);
  Box.addPlane(front_plane);
  Box.addPlane(back_plane);
  Box.addPlane(left_plane);
  Box.addPlane(right_plane);
    
  G->generatePacking(&Box,NT,0);
  
  if(yt>0.0){
    for(int i=0;i<nr;i++){
      double x1=2.0*double(i)*xr;
      double x2=(2.0*double(i)+1.0)*xr;
      
      // ridge
      Vector3 lmin=Vector3(x1,y0,0.0);
      Vector3 lmax=Vector3(x2,y0+yt,zt);
      left_plane=Plane(lmin,Vector3(1.0,0.0,0.0));
      right_plane=Plane(lmax,Vector3(-1.0,0.0,0.0));
      
      BoxWithPlanes3D RBox(lmin,lmax);
      RBox.addPlane(bottom_plane_1);
      RBox.addPlane(front_plane);
      RBox.addPlane(back_plane);
      RBox.addPlane(left_plane);
      RBox.addPlane(right_plane);
      
      G->generatePacking(&RBox,NT,0);
    }
  }
}

/*!
  \param NT
  \param G
  \param xt total x-dim
  \param yt total y-dim
  \param zt total z-dim
  \param y0 y-offset
  \param nr nr raised ridges
  \param yr height of ridges
*/
void generate_lower_rough_block(MNTable3D* NT,
				AGenerator3D* G,
				double xt,
				double yt,
				double zt,
				double y0,
				int nr,
				double yr)
{  
  Vector3 min=Vector3(0.0,y0,0.0); 
  Vector3 max_2=Vector3(xt,y0+(yt-yr),zt); 
  Vector3 max=Vector3(xt,y0+yt,zt);
  double xr=xt/(2.0*double(nr));

  Plane top_plane=Plane(max,Vector3(0.0,-1.0,0.0));
  Plane top_plane_2=Plane(max_2,Vector3(0.0,-1.0,0.0));
  Plane bottom_plane=Plane(min,Vector3(0.0,1.0,0.0));
  Plane front_plane=Plane(min,Vector3(0.0,0.0,1.0));
  Plane back_plane=Plane(max,Vector3(0.0,0.0,-1.0));
  Plane left_plane=Plane(min,Vector3(1.0,0.0,0.0));
  Plane right_plane=Plane(max,Vector3(-1.0,0.0,0.0));

  Vector3 diff(1.0,0.0,0.0);
  BoxWithPlanes3D Box(min-diff,max_2+diff);
  
  Box.addPlane(top_plane_2);
  Box.addPlane(bottom_plane);
  Box.addPlane(front_plane);
  Box.addPlane(back_plane);
  Box.addPlane(left_plane);
  Box.addPlane(right_plane);
    
  G->generatePacking(&Box,NT,0);
  

  if(yt>0.0){
    for(int i=0;i<nr;i++){
      double x1=2.0*double(i)*xr;
      double x2=(2.0*double(i)+1.0)*xr;
      
      // ridge
      Vector3 lmin=Vector3(x1,y0,0.0);
      Vector3 lmax=Vector3(x2,y0+yt,zt);
      left_plane=Plane(lmin,Vector3(1.0,0.0,0.0));
      right_plane=Plane(lmax,Vector3(-1.0,0.0,0.0));
      
      BoxWithPlanes3D RBox(lmin,lmax);
      RBox.addPlane(top_plane);
      RBox.addPlane(front_plane);
      RBox.addPlane(back_plane);
      RBox.addPlane(left_plane);
      RBox.addPlane(right_plane);
      
      G->generatePacking(&RBox,NT,0);
    }
  }
}


/*!
  \param NT
  \param G
  \param xt total x-dim
  \param yt total y-dim
  \param zt total z-dim
  \param y0 y-offset
  \param nr nr raised ridges
  \param yr height of ridges
*/
void generate_upper_tri_rough_block(MNTable3D* NT,
				    AGenerator3D* G,
				    double xt,
				    double yt,
				    double zt,
				    double y0,
				    int nr,
				    double yr)
{
  Vector3 min=Vector3(0.0,y0,0.0); 
  Vector3 min_2=Vector3(0.0,y0+yr,0.0); 
  Vector3 max=Vector3(xt,y0+yt,zt);
  double xr=xt/double(nr);

  Plane top_plane=Plane(max,Vector3(0.0,-1.0,0.0));
  Plane bottom_plane_1=Plane(min,Vector3(0.0,1.0,0.0));
  Plane bottom_plane_2=Plane(min_2,Vector3(0.0,1.0,0.0));
  Plane front_plane=Plane(min,Vector3(0.0,0.0,1.0));
  Plane back_plane=Plane(max,Vector3(0.0,0.0,-1.0));
//   Plane left_plane=Plane(min,Vector3(1.0,0.0,0.0));
//   Plane right_plane=Plane(max,Vector3(-1.0,0.0,0.0));

  Vector3 diff(1.0,0.0,0.0);
  BoxWithPlanes3D Box(min_2-diff,max+diff);
  
  Box.addPlane(top_plane);
  Box.addPlane(bottom_plane_2);
  Box.addPlane(front_plane);
  Box.addPlane(back_plane);
//   Box.addPlane(left_plane);
//   Box.addPlane(right_plane);
    
  G->generatePacking(&Box,NT,0);
  

  for(int i=0;i<nr;i++){
    double x1=double(i)*xr;
    double x2=double(i+1)*xr;

    // ridge
    Vector3 lmin=Vector3(x1,y0,0.0);
    Vector3 lmax=Vector3(x2,y0+yr,zt);
    Plane left_plane=Plane(lmin+Vector3(xr/2.0,0.0,0.0),(Vector3(yr,-0.5*xr,0.0)).unit()); // top left
    Plane right_plane=Plane(lmax,(Vector3(-1.0*yr,-0.5*xr,0.0)).unit()); // top right
    
    TriBox RBox(lmin,lmax,true);
    RBox.addPlane(front_plane);
    RBox.addPlane(back_plane);
    RBox.addPlane(left_plane);
    RBox.addPlane(right_plane);
    
    G->generatePacking(&RBox,NT,0);
  }
  NT->tagParticlesAlongPlane(top_plane,0.5,3,0);

}

/*!
  \param NT
  \param G
  \param xt total x-dim
  \param yt total y-dim
  \param zt total z-dim
  \param y0 y-offset
  \param nr nr raised ridges
  \param yr height of ridges
*/
void generate_lower_tri_rough_block(MNTable3D* NT,
				    AGenerator3D* G,
				    double xt,
				    double yt,
				    double zt,
				    double y0,
				    int nr,
				    double yr)
{  
  Vector3 min=Vector3(0.0,y0,0.0); 
  Vector3 max_2=Vector3(xt,y0+(yt-yr),zt); 
  Vector3 max=Vector3(xt,y0+yt,zt);
  double xr=xt/(double(nr));

  Plane top_plane=Plane(max,Vector3(0.0,-1.0,0.0));
  Plane top_plane_2=Plane(max_2,Vector3(0.0,-1.0,0.0));
  Plane bottom_plane=Plane(min,Vector3(0.0,1.0,0.0));
  Plane front_plane=Plane(min,Vector3(0.0,0.0,1.0));
  Plane back_plane=Plane(max,Vector3(0.0,0.0,-1.0));
//   Plane left_plane=Plane(min,Vector3(1.0,0.0,0.0));
//   Plane right_plane=Plane(max,Vector3(-1.0,0.0,0.0));

  Vector3 diff(1.0,0.0,0.0);
  BoxWithPlanes3D Box(min-diff,max_2+diff);
  
  Box.addPlane(top_plane_2);
  Box.addPlane(bottom_plane);
  Box.addPlane(front_plane);
  Box.addPlane(back_plane);
//   Box.addPlane(left_plane);
//   Box.addPlane(right_plane);
    
  G->generatePacking(&Box,NT,0);
  

  for(int i=0;i<nr;i++){
    double x1=double(i)*xr;
    double x2=double(i+1)*xr;

    // ridge
    Vector3 lmin=Vector3(x1,y0+(yt-yr),0.0);
    Vector3 lmax=Vector3(x2,y0+yt,zt);
    Plane left_plane=Plane(lmin,(Vector3(yr,-0.5*xr,0.0)).unit()); // top left
    Plane right_plane=Plane(lmax-Vector3(xr/2.0,0.0,0.0),(Vector3(-1.0*yr,-0.5*xr,0.0)).unit()); // top right
    
    TriBox RBox(lmin,lmax);
    RBox.addPlane(front_plane);
    RBox.addPlane(back_plane);
    RBox.addPlane(left_plane);
    RBox.addPlane(right_plane);
    
    G->generatePacking(&RBox,NT,0);
  }

  NT->tagParticlesAlongPlane(bottom_plane,0.5,4,0);
}

/*!
  generate spherical grains in the middle 

  \param NT
  \param G
  \param Pmin minimum corner of the gouge box
  \param Pmax maximum corner of the gouge box
  \param grmin minimun grain radius
  \param grmax maximum grain radius
  \param gntry nr. of tries for grain fitting
*/
void generate_granular_gouge(MNTable3D* NT,
			     AGenerator3D* G,
			     const Vector3& Pmin,
			     const Vector3& Pmax,
			     double grmin,
			     double grmax,
			     int gntry)
{
  std::cout << "Granular Gouge: " << Pmin << " - " << Pmax << "  " << grmin << " " << grmax << " " << gntry <<  std::endl;
  // == stage 1 - generate grains ==
  // -- setup auxillary Table,Volume and Generator
  MNTable3D T_aux(Pmin,Pmax,2.1*grmax,1);
  InsertGenerator3D G_aux(grmin,grmax,gntry,1000,1e-5);
  BoxWithPlanes3D V_aux(Pmin,Pmax);

  // -- generate grains
  G_aux.generatePacking(&V_aux,&T_aux,0);
  
  // -- extract grains 
  vector<const Sphere*> grain_sph=T_aux.getAllSpheresFromGroup(0); 

  // == stage 2 - fill grains ==
  // -- get nr. of grains
  int ngr=grain_sph.size();
  std::cout << " found " << ngr << " grains" << std:: endl;
  // -- grow nr. of particle groups in main NTable
  NT->GrowNGroups(1+ngr);
  // -- for each grain, do filling
  int ggid=1; // group id 
  for(vector<const Sphere*>::iterator iter=grain_sph.begin();
      iter!=grain_sph.end();
      iter++){
    // -- setup sphere volume
    SphereVol S=SphereVol((*iter)->Center(),(*iter)->Radius());
    // -- generate packing 
    G->generatePacking(&S,NT,ggid);
    // -- generate bonds
    NT->generateBonds(ggid,1e-5,1);
    ggid++;
  }
}
