// Rbox - Copyright (C) 2010-2013 T. Mouton
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <thibaud.mouton@gmail.com>.

#include "rbox.h"
#include <string.h>

void options::parse ( int argc, char **argv )
{
  for ( int i = 0; i < argc; i++ )
    sprintf ( cmdline, "%s %s", cmdline, argv[i] );

  int c;
  while ( 1 )
  {
    static struct option long_options[] =
    {
      {"debug",                no_argument,       0, 0},
      {"dim",                  required_argument, 0, 1},
      {"random",               required_argument, 0, 2},
      {"circle",               required_argument, 0, 3},
      {"line",                 required_argument, 0, 4},
      {"hline",                required_argument, 0, 5},
      {"vline",                required_argument, 0, 6},
      {"sampling",             required_argument, 0, 7},
      {"t0",                   no_argument,       0, 8},
      {"speed",                no_argument,       0, 9},
      {"angle",                no_argument,       0, 10},
      {"aniso",                no_argument,       0, 11},
      {"nf",                   required_argument, 0, 12},
      {"nbpt",                 required_argument, 0, 13},
      {                     0,                 0, 0, 0}
    };
    /* getopt_long stores the option index here. */
    int option_index = 0;

    c = getopt_long ( argc, argv, "", long_options, &option_index );

    /* Detect the end of the options. */
    if ( c == -1 )
      break;

    switch ( c )
    {
      case 0:
        debug = 1;
        break;
      case 1:
        if ( optarg[0] == '-' )
        {
          printf ( "rbox: '--dim' needs a parameter\n" );
          usage ( "rbox" );
        }
        dim = atoi ( optarg );
        if ( dim != 2 && dim != 3 )
        {
          printf ( "rbox: '--dim' needs to be 2 or 3\n" );
          usage ( "rbox" );
        }
//         printf ( "dim %d\n", dim );
        break;
      case 2:
        if ( optarg[0] == '-' )
        {
          printf ( "rbox: '--random' needs a parameter\n" );
          usage ( "rbox" );
        }
        random = atoi ( optarg );
//         printf ( "random %d\n", dim );
        break;
      case 3:
        if ( optarg[0] == '-' )
        {
          printf ( "rbox: '--circle' needs a parameter\n" );
          usage ( "rbox" );
        }
        circle = atoi ( optarg );
//         printf ( "circle %d\n", circle );
        break;
      case 4:
        if ( optarg[0] == '-' )
        {
          printf ( "rbox: '--line' needs a parameter\n" );
          usage ( "rbox" );
        }
        line = atoi ( optarg );
//         printf ( "line %d\n", line );
        break;
      case 5:
        if ( optarg[0] == '-' )
        {
          printf ( "rbox: '--hline' needs a parameter\n" );
          usage ( "rbox" );
        }
        hline = atoi ( optarg );
//         printf ( "hline %d\n", hline );
        break;
      case 6:
        if ( optarg[0] == '-' )
        {
          printf ( "rbox: '--vline' needs a parameter\n" );
          usage ( "rbox" );
        }
        vline = atoi ( optarg );
//         printf ( "vline %d\n", vline );
        break;
      case 7:
        if ( optarg[0] == '-' )
        {
          printf ( "rbox: '--sampling' needs a parameter\n" );
          usage ( "rbox" );
        }
        sampling = atoi ( optarg );
//         printf ( "sampling %d\n", sampling );
        break;
      case 8:
//         printf ( "t0 activated\n" );
        t0 = true;
        break;
      case 9:
//         printf ( "speed activated\n" );
        speed = true;
        break;
      case 10:
//         printf ( "angle activated\n" );
        angle = true;
        break;
      case 11:
//         printf ( "aniso activated\n" );
        aniso = true;
        break;
       case 12:
        if ( optarg[0] == '-' && optarg[1] != '1' )
        {
          printf ( "rbox: '--nf' needs a parameter\n" );
          usage ( "rbox" );
        }
        nf = atoi ( optarg );
        break;
      case 13:
        if ( optarg[0] == '-' )
        {
          printf ( "rbox: '--nbpt' needs a parameter\n" );
          usage ( "rbox" );
        }
        nbpt = atoi ( optarg );
//         printf ( "nbpt %d\n", nbpt );
        break;
      default:
        usage ( "rbox" );
    }
  }

  if ( nbpt == 0 )
  {
    printf ( "rbox: needs to specifiy number of points\n" );
    usage ( "rbox" );
  }
}

void options::usage ( const char *program_name )
{
  printf ( "usage: %s [ options ]\n", program_name );
  printf ( "  Point datasets generation.\n" );
  printf ( "    [ --dim ] (type=INTEGER) : The dimension (2 or 3)\n" );
  printf ( "    [ --random ] (type=INTEGER) : Generate random points\n" );
  printf ( "    [ --circle ] (type=INTEGER) : Generate random points on circle\n" );
  printf ( "    [ --line ] (type=INTEGER) : Generate random points on line\n" );
  printf ( "    [ --hline ] (type=INTEGER) : Generate random points on horizontal line\n" );
  printf ( "    [ --vline ] (type=INTEGER) : Generate random points on vertical line\n" );
  printf ( "    [ --sampling ] (type=INTEGER) : Number of points per cycle\n" );
  printf ( "    [ --t0 ] : Output random starting time\n" );
  printf ( "    [ --speed ] : Output random speed\n" );
  printf ( "    [ --angle ] : Output random angle\n" );
  printf ( "    [ --aniso ] : Output random anisotropy\n" );
  printf ( "    [ --nf ] (type=INTEGER) : Output the given number of facets (-1 = random)\n" );
  printf ( "    [ --nbpt ] (type=INTEGER) : Total number of points\n" );
  exit ( 0 );
}


void generate_random ( options *opt, int &seek, point *xyz )
{
//   printf ( "generating %d random points\n", nbpf );
  int nbpf = opt->sampling;
  for ( int i = 0; i < nbpf; i++ )
  {
    xyz[seek].x = drand48();
    xyz[seek].y = drand48();
    if ( opt->t0 )
      xyz[seek].t0 = drand48();
    if ( opt->speed )
      xyz[seek].v1 = xyz[seek].v2 = drand48();
    if ( opt->angle )
      xyz[seek].angle = drand48() *2.0*M_PI;
    if ( opt->aniso )
    {
      xyz[seek].v1 = drand48();
      xyz[seek].v2 = drand48();
    }
    if ( opt->nf == -1 )
      xyz[seek].nf = (int)(drand48()*50);
    else
      xyz[seek].nf = opt->nf;
    seek++;
  }
}

void generate_random_circle ( options *opt, int &seek, point *xyz )
{
//   printf ( "generating %d random points on circle\n", nbpf );
  int nbpf = opt->sampling;
  // generate random point on circle
  double cx = drand48();
  double cy = drand48();
  double cr = drand48();
  while ( ( cx-cr ) <0.0 || ( cx+cr ) >1.0 || ( cy-cr ) <0.0 || ( cy+cr ) >1.0 )
    cr *= 0.9;
  for ( int i = 0; i < nbpf; i++ )
  {
    double ca = drand48() *2.0*M_PI;
    xyz[seek].x = cx + cr*cos ( ca );
    xyz[seek].y = cy + cr*sin ( ca );
    if ( opt->t0 )
      xyz[seek].t0 = drand48();
    if ( opt->speed )
      xyz[seek].v1 = xyz[seek].v2 = drand48();
    if ( opt->angle )
      xyz[seek].angle = drand48() *2.0*M_PI;
    if ( opt->aniso )
    {
      xyz[seek].v1 = drand48();
      xyz[seek].v2 = drand48();
    }
    if ( opt->nf == -1 )
      xyz[seek].nf = (int)(drand48()*50);
    else
      xyz[seek].nf = opt->nf;
    seek++;
  }
}

void generate_random_line ( options *opt, int &seek, point *xyz )
{
//   printf ( "generating %d random points on line\n", nbpf );
  int nbpf = opt->sampling;
  double cdir = drand48();
  double d = drand48();

  int sign = rand() %2;

  if ( sign )
    cdir *= -1.0;

  double tmin = 0.0;
  double tmax = -d/cdir;

  if ( tmax > 1.0 )
    tmax = 1.0;

  if ( tmax < 0.0 )
  {
    tmax = ( 1.0-d ) /cdir;
    if ( tmax > 1.0 )
      tmax = 1.0;
  }

  for ( int i = 0; i < nbpf; i++ )
  {
    double t = drand48();
    xyz[seek].x = tmin+t* ( tmax-tmin );
    xyz[seek].y = cdir*xyz[i].x + d;
    if ( opt->t0 )
      xyz[seek].t0 = drand48();
    if ( opt->speed )
      xyz[seek].v1 = xyz[seek].v2 = drand48();
    if ( opt->angle )
      xyz[seek].angle = drand48() *2.0*M_PI;
    if ( opt->aniso )
    {
      xyz[seek].v1 = drand48();
      xyz[seek].v2 = drand48();
    }
    if ( opt->nf == -1 )
      xyz[seek].nf = (int)(drand48()*50);
    else
      xyz[seek].nf = opt->nf;
    seek++;
  }
}

void generate_random_hline ( options *opt, int &seek, point *xyz )
{
//   printf ( "generating %d random points on horizontal line\n", nbpf );
  int nbpf = opt->sampling;
  double d = drand48();

//       double tmin = 0.0;
//       double tmax = 1.0;

  for ( int i = 0; i < nbpf; i++ )
  {
    double t = drand48();
    xyz[seek].x = t;
    xyz[seek].y = d;
    if ( opt->t0 )
      xyz[seek].t0 = drand48();
    if ( opt->speed )
      xyz[seek].v1 = xyz[seek].v2 = drand48();
    if ( opt->angle )
      xyz[seek].angle = drand48() *2.0*M_PI;
    if ( opt->aniso )
    {
      xyz[seek].v1 = drand48();
      xyz[seek].v2 = drand48();
    }
    if ( opt->nf == -1 )
      xyz[seek].nf = (int)(drand48()*50);
    else
      xyz[seek].nf = opt->nf;
    seek++;
  }
}

void generate_random_vline ( options *opt, int &seek, point *xyz )
{
//   printf ( "generating %d random points on vertical line\n", nbpf );
  int nbpf = opt->sampling;
  double d = drand48();

//       double tmin = 0.0;
//       double tmax = 1.0;

  for ( int i = 0; i < nbpf; i++ )
  {
    double t = drand48();
    xyz[seek].x = d;
    xyz[seek].y = t;
    if ( opt->t0 )
      xyz[seek].t0 = drand48();
    if ( opt->speed )
      xyz[seek].v1 = xyz[seek].v2 = drand48();
    if ( opt->angle )
      xyz[seek].angle = drand48() *2.0*M_PI;
    if ( opt->aniso )
    {
      xyz[seek].v1 = drand48();
      xyz[seek].v2 = drand48();
    }
    if ( opt->nf == -1 )
      xyz[seek].nf = (int)(drand48()*50);
    else
      xyz[seek].nf = opt->nf;
    seek++;
  }
}

void generate_2D ( options *opt )
{
  point *xyz = new point[opt->nbpt];
  for ( int i = 0; i < opt->nbpt; i++ )
  {
    xyz[i].x = 0.0;
    xyz[i].y = 0.0;
    xyz[i].t0 = 0.0;
    xyz[i].angle = 0.0;
    xyz[i].v1 = xyz[i].v2 = 1.0;    
    xyz[i].nf = 10;
  }
  int total = opt->random+opt->circle+opt->line+opt->hline+opt->vline;
  int nbvx = 0;
  int nbcycle = -1;

  if ( opt->sampling == 0 )
    opt->sampling = ( int ) ( ( double ) opt->nbpt / ( double ) total );

  nbcycle = ( int ) ( ( opt->random/ ( double ) total ) *opt->nbpt ) / ( double ) opt->sampling;
  for ( int i = 0; i < nbcycle; i++ )
    generate_random ( opt, nbvx, xyz );

  nbcycle = ( ( opt->circle/ ( double ) total ) *opt->nbpt ) / ( double ) opt->sampling;
  for ( int i = 0; i < nbcycle; i++ )
    generate_random_circle ( opt, nbvx, xyz );

  nbcycle = ( ( opt->line/ ( double ) total ) *opt->nbpt ) / ( double ) opt->sampling;
  for ( int i = 0; i < nbcycle; i++ )
    generate_random_line ( opt, nbvx, xyz );

  nbcycle = ( ( opt->hline/ ( double ) total ) *opt->nbpt ) / ( double ) opt->sampling;
  for ( int i = 0; i < nbcycle; i++ )
    generate_random_hline ( opt, nbvx, xyz );
  nbcycle = ( ( opt->vline/ ( double ) total ) *opt->nbpt ) / ( double ) opt->sampling;
  for ( int i = 0; i < nbcycle; i++ )
    generate_random_vline ( opt, nbvx, xyz );

  ////////////////////////////////////////////////////////
  //                   print headers                    //
  ////////////////////////////////////////////////////////
  time_t now = time ( 0 ); // current date/time based on current system
  char* dt = ctime ( &now ); // convert now to string form
  printf ( "# Generated by %s on %s", opt->cmdline, dt );
  printf ( "%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s\n", "index", opt->sep, "x", opt->sep, "y", opt->sep, "t0", opt->sep, "angle", opt->sep, "v1", opt->sep, "v2", opt->sep, "nf" );

  ////////////////////////////////////////////////////////
  //                    print datas                     //
  ////////////////////////////////////////////////////////
  for ( int i = 0; i < nbvx; i++ )
    printf ( "%d%c%.20lf%c%.20lf%c%.20lf%c%.20lf%c%.20lf%c%.20lf%c%d\n",
             i, opt->sep,
             xyz[i].x, opt->sep,
             xyz[i].y, opt->sep,
             xyz[i].t0, opt->sep,
             xyz[i].angle, opt->sep,
             xyz[i].v1, opt->sep,
             xyz[i].v2, opt->sep,
             xyz[i].nf );
}


void generate_3D ( options *opt )
{
//   int count = 0;
//
//   printf ( "%d\n", nb*nbpf );
//
//   // generate random point
//   for ( int i = 0; i < nb*nbpf; i++ )
//   {
//     double x = drand48();
//     double y = drand48();
//     double z = drand48();
//     printf ( "%d %.20lf %.20lf %.20lf\n", count++, x*sx, y*sy, z*sz );
//   }
//
//   return;
//
//   // generate random point on spheres
//   for ( int i = 0; i < nb; i++ )
//   {
//     double cx = drand48();
//     double cy = drand48();
//     double cz = drand48();
//     double cr = drand48();
//     while ( ( cx-cr ) <0.0 || ( cx+cr ) >sx || ( cy-cr ) <0.0 || ( cy+cr ) >sy || ( cz-cr ) <0.0 || ( cz+cr ) >sz )
//       cr *= 0.9;
//     for ( int j = 0; j < nbpf; j++ )
//     {
//       double ca = drand48() *2.0*M_PI;
//       double cb = drand48() *M_PI - M_PI/4.0;
//       double x = cx + cr*cos ( ca ) *cos ( cb );
//       double y = cy + cr*sin ( ca ) *cos ( cb );
//       double z = cz + cr*sin ( cb );
//       printf ( "%d %.20lf %.20lf %.20lf\n", count++, x*sx, y*sy, z*sz );
//     }
//   }
//
//   // generate random point on planes ax + by + cz + d = 0
//   for ( int i = 0; i < nb; i++ )
//   {
//     double a = drand48();
//     double b = drand48();
//     double c = drand48();
//     double d = drand48();
//
//     /*      double tmin = 0.0;
//           double tmax = -d/cdir;
//
//           if (tmax > 1.0)
//       tmax = 1.0;
//
//           if (tmax < 0.0)
//           {
//       tmax = (1.0-d)/cdir;
//       if (tmax > 1.0)
//         tmax = 1.0;
//           }
//
//           for (int j = 0; j < nbpf; j++)
//           {
//       double t = drand48();
//       double x = tmin+t*(tmax-tmin);
//       double y = cdir*x + d;
//       printf("%d %.20lf %.20lf %lf\n", count++, x*sx, y*sy);
//           } */
//   }
}



int main ( int argc, char** argv )
{
  srand48 ( time ( NULL ) );

  options *opt = new options;
  opt->parse ( argc, argv );
  if ( opt->dim == 2 )
    generate_2D ( opt );
  else if ( opt->dim == 3 )
    generate_3D ( opt );
  else
    opt->usage ( "rbox" );
}
