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

/*!
  \file main.cc
  $Revision:$
  $Date:$
*/

#include "geometry/Sphere.h"
#include "geometry/Line2D.h"
#include "geometry/LineSegment2D.h"
#include "util/vector3.h"
#include "MNTable2D.h"
#include "MNTable3D.h"
#include "CircMNTable3D.h"
#include "FullCircMNTable3D.h"
#include "CircMNTable2D.h"
#include "CircMNTableXY2D.h"
#include "InsertGenerator2D.h"
#include "HexAggregateInsertGenerator2D.h"
#include "HGrainGenerator.h"
#include "InsertGenerator3D.h"
#include "BoxWithLines2D.h"
#include "BoxWithLines2DSubVol.h"
#include "BoxWithPlanes3D.h"
#include "CylinderVol.h"
#include "DogBone.h"
#include "SphereVol.h"
#include "TriBox.h"

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

using std::cout;
using std::cout;
using std::endl;

// -- System includes --
#include <cstdlib>
#include <sys/time.h>
#include <cmath>

using std::atoi;
using std::atof;
using std::sqrt;

int main(int argc, char** argv)
{
  int ret=0;
  double size=atof(argv[1]);
  int ntry=atoi(argv[2]);
  double rmin=atof(argv[3]);
  int outputstyle=atoi(argv[4]);
  int dim=atoi(argv[5]);
  double vol;

  // seed RNG
  struct timeval tv;
  gettimeofday(&tv,NULL);
  int random_seed=tv.tv_usec;
  srand(random_seed);

  if(dim==2){
    double ratio=atof(argv[6]);
    double xsize=ratio*size;
    double ysize=size;
    MNTable2D T(Vector3(0.0,0.0,0.0),Vector3(xsize,ysize,0.0),2.5,1);
    Vector3 min=Vector3(0.0,0.0,0.0); 
    Vector3 max=Vector3(xsize,ysize,0.0);
    
    MNTable2D::SetOutputStyle(outputstyle);

    InsertGenerator2D *G=new InsertGenerator2D(rmin,1.0,ntry,10000,1e-7);
    BoxWithLines2D Box(Vector3(0.0,0.0,0.0),Vector3(xsize,ysize,0.0));
    Line2D top_line(Vector3(xsize,0.0,0.0),min);
    Line2D bottom_line(max,Vector3(0.0,ysize,0.0));
    Line2D left_line(Vector3(xsize,0.0,0.0),max);
    Line2D right_line(min,Vector3(0.0,ysize,0.0));

    Box.addLine(top_line);
    Box.addLine(bottom_line);
    Box.addLine(left_line);
    Box.addLine(right_line);

    G->generatePacking(&Box,&T,0,0);
    T.generateBonds(0,1e-5,0);

    T.tagParticlesAlongLineWithMask(left_line,0.5,4,4,0);
    T.tagParticlesAlongLineWithMask(right_line,0.5,8,8,0);

    vol=xsize*ysize;
    double poros=(vol-T.getSumVolume(0))/vol;
    cout << "Porosity:  " << poros << endl;

    cout << T << endl;
    
    delete G;
  } else if(dim==3){ // 3D block 1x2x1
    double rmax=atof(argv[6]);
    Vector3 min=Vector3(0.0,0.0,0.0); 
    Vector3 max=Vector3(size,2.0*size,size);
    MNTable3D T(min,max,2.5*rmax,1);
    MNTable3D::SetOutputStyle(outputstyle);
    InsertGenerator3D G(rmin,rmax,ntry,1000,1e-6);
    BoxWithPlanes3D Box(min,max);
    Box.addPlane(Plane(min,Vector3(1.0,0.0,0.0)));
    Box.addPlane(Plane(min,Vector3(0.0,1.0,0.0)));
    Box.addPlane(Plane(min,Vector3(0.0,0.0,1.0)));
    Box.addPlane(Plane(max,Vector3(-1.0,0.0,0.0)));
    Box.addPlane(Plane(max,Vector3(0.0,-1.0,0.0)));
    Box.addPlane(Plane(max,Vector3(0.0,0.0,-1.0)));
    G.generatePacking(&Box,&T,0);
    T.generateBonds(0,1e-5,0);

    double vol=2.0*size*size*size;
    double poros=(vol-T.getSumVolume(0))/vol;
    cout << "Porosity:  " << poros << endl;
    
    cout << T << endl;
  } else if(dim==4){ // Cylinder, 3:1
    Vector3 orig=Vector3(0.0,0.0,0.0); 
    Vector3 axis=Vector3(0.0,1.0,0.0);
    Vector3 min=Vector3(-1.0*size,0.0,-1.0*size); 
    Vector3 max=Vector3(size,3.0*size,size);
    MNTable3D T(min,max,2.2,1);
    MNTable3D::SetOutputStyle(outputstyle);
    InsertGenerator3D G(rmin,1.0,ntry,1000,1e-6);

    CylinderVol Cyl(orig,axis,3.0*size,size);
    G.generatePacking(&Cyl,&T,0);
    cout << T << endl;
  } else if(dim==5){ // Torus, 3:1
    double l2=atof(argv[6]);
    double r2=atof(argv[7]);
    Vector3 orig=Vector3(0.0,0.0,0.0); 
    Vector3 axis=Vector3(0.0,1.0,0.0);
    Vector3 min=Vector3(-1.0*size,0.0,-1.0*size); 
    Vector3 max=Vector3(size,3.0*size,size);
    MNTable3D T(min,max,2.2,1);
    MNTable3D::SetOutputStyle(outputstyle);
    InsertGenerator3D G(rmin,1.0,ntry,1000,1e-6);

    DogBone DB(orig,axis,3.0*size,size,l2,r2);
    G.generatePacking(&DB,&T,0);
    cout << T << endl;
  } else if(dim==6){ // sphere
    Vector3 orig=Vector3(0.0,0.0,0.0); 
    Vector3 min=Vector3(-1.0*size,-1.0*size,-1.0*size); 
    Vector3 max=Vector3(size,size,size);
    MNTable3D T(min,max,2.2,1);
    MNTable3D::SetOutputStyle(outputstyle);
    InsertGenerator3D G(rmin,1.0,ntry,1000,1e-6);
    SphereVol Sph(orig,size);

    G.generatePacking(&Sph,&T,0);
    cout << T << endl;
  } else if(dim==7){
    Vector3 min=Vector3(0.0,0.0,0.0); 
    Vector3 max=Vector3(size,size,size);
    MNTable3D T(min,max,2.5,1);
    MNTable3D::SetOutputStyle(outputstyle);
    InsertGenerator3D G(rmin,1.0,ntry,1000,1e-6);
    TriBox Box(min,max);
    Box.addPlane(Plane(min,Vector3(0.0,1.0,0.0))); // bottom
    Box.addPlane(Plane(min,Vector3(0.0,0.0,1.0))); // front 
    Box.addPlane(Plane(max,Vector3(0.0,0.0,-1.0))); // back
    double dx=(max-min).X();
    double dy=(max-min).Y();
    Box.addPlane(Plane(min,(Vector3(dy,-0.5*dx,0.0)).unit())); // top left
    Box.addPlane(Plane(max-Vector3(dx/2.0,0.0,0.0),(Vector3(-1.0*dy,-0.5*dx,0.0)).unit())); // top right

    G.generatePacking(&Box,&T,0);
    T.generateBonds(0,1e-5,0);

     cout << T << endl;
  } else if(dim==8){
    Vector3 min=Vector3(0.0,0.0,0.0); 
    Vector3 max=Vector3(size,size,size);
    MNTable3D T(min,max,2.5,1);
    MNTable3D::SetOutputStyle(outputstyle);
    InsertGenerator3D G(rmin,1.0,ntry,1000,1e-6);
    TriBox Box(min,max,true);
    Box.addPlane(Plane(max,Vector3(0.0,-1.0,0.0))); // top
    Box.addPlane(Plane(min,Vector3(0.0,0.0,1.0))); // front 
    Box.addPlane(Plane(max,Vector3(0.0,0.0,-1.0))); // back
    double dx=(max-min).X();
    double dy=(min-max).Y();
    Box.addPlane(Plane(min+Vector3(dx/2.0,0.0,0.0),(Vector3(dy,-0.5*dx,0.0)).unit())); // top left
    Box.addPlane(Plane(max,(Vector3(-1.0*dy,-0.5*dx,0.0)).unit())); // top right

    G.generatePacking(&Box,&T,0);
    T.generateBonds(0,1e-5,0);

     cout << T << endl;
  } else if(dim==9){
    CircMNTable2D T(Vector3(0.0,0.0,0.0),Vector3(size,size,0.0),2.5,1);
    Vector3 min=Vector3(0.0,0.0,0.0); 
    Vector3 max=Vector3(size,size,0.0);
    
    CircMNTable2D::SetOutputStyle(outputstyle);

    HexAggregateInsertGenerator2D *G=new HexAggregateInsertGenerator2D(rmin,1.0,ntry,1000,1e-6);
    BoxWithLines2DSubVol Box(Vector3(-0.5,0.0,0.0),Vector3(size+0.5,size,0.0),10.0,10.0);
    Line2D top_line(Vector3(size,0.0,0.0),min);
    Line2D bottom_line(max,Vector3(0.0,size,0.0));

    Box.addLine(top_line);
    Box.addLine(bottom_line);

    G->generatePacking(&Box,&T,0,0);
    T.tagParticlesAlongLineWithMask(top_line,0.5,4,4,0);
    T.tagParticlesAlongLineWithMask(bottom_line,0.5,8,8,0);

    vol=size*size;
    double poros=(vol-T.getSumVolume(0))/vol;
    cout << "Porosity:  " << poros << endl;

    cout << T << endl;
    
    delete G;
  } else if(dim==20){ // hex grain regular
    double xsize=size;
    double ysize=size;

    MNTable2D T(Vector3(0.0,0.0,0.0),Vector3(xsize,ysize,0.0),2.5,1);
    Vector3 min=Vector3(0.0,0.0,0.0); 
    Vector3 max=Vector3(xsize,ysize,0.0);
    
    CircMNTable2D::SetOutputStyle(outputstyle);

    HGrainGenerator2D *G=new HGrainGenerator2D(rmin);
    BoxWithLines2D Box(Vector3(0.0,0.0,0.0),Vector3(xsize,ysize,0.0));
    Line2D top_line(Vector3(xsize,0.0,0.0),min);
    Line2D bottom_line(max,Vector3(0.0,ysize,0.0));
    Line2D left_line(Vector3(xsize,0.0,0.0),max);
    Line2D right_line(min,Vector3(0.0,ysize,0.0));

    Box.addLine(top_line);
    Box.addLine(bottom_line);
    Box.addLine(left_line);
    Box.addLine(right_line);

    G->generatePacking(&Box,&T,0,0);
    T.generateBonds(0,1e-5,0);

    T.tagParticlesAlongLineWithMask(top_line,5.0,4,4,0);
    T.tagParticlesAlongLineWithMask(bottom_line,5.0,8,8,0);
    T.tagParticlesAlongLineWithMask(left_line,8.0,16,16,0);
    T.tagParticlesAlongLineWithMask(right_line,8.0,32,32,0);

    double ldx=0.5*ysize;
    double ldy=(sqrt(3.0)/2.0)*ldx;
    double lx0=atof(argv[6]);
    double ly0=atof(argv[7]);

    LineSegment2D breakLine(Vector3(lx0,ly0,0.0),Vector3(lx0-ldx,ly0+ldy,0.0));

    T.breakBondsAlongLineSegment(breakLine,1.0,0,0);
    
    vol=xsize*ysize;
    double poros=(vol-T.getSumVolume(0))/vol;
    cout << "Porosity:  " << poros << endl;

    cout << T << endl;
    
    delete G;
  } 

  return ret;
}
