#include "pointset.h"
#include <cassert>

// for subprocesses
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>

// extern "C" {
//   #include "discr_calc/dem_discr.h"
// }

pointset::pointset(int dim, int npoints) : dim(dim), npoints(npoints) {
  assert(dim > 0);
  assert(npoints > 0);
  data = new double*[npoints];
  for(int y = 0; y < npoints; y++)
    data[y] = new double[dim];
}

pointset::~pointset() {
  for(int y = 0; y < npoints; y++)
    delete[] data[y];
  delete[] data;
}

double* pointset::point(int i) {
  assert(0 <= i && i < npoints);
  return data[i];
}

double pointset::discrepancy() {
  // computing by direct call
  // (it does not seem to work several times... -> have a look on global variables)
  // double lower;
  // return oydiscr(data, dim, npoints, &lower);

  // computing by launching discr_calc/dem_discr as a child process
  double result;
  int status = -1, pid;
  int pipefd_in[2], pipefd_out[2], null; // in/out : from parent's point of view
  if(pipe(pipefd_in) || pipe(pipefd_out)) {
    perror("pipe()");
    exit(1);
  }
  switch(pid = fork()) {
    case -1: // error
      perror("fork()");
      exit(1);
    case 0: // child
      close(pipefd_in[0]);
      close(pipefd_out[1]);
      // replace stdin
      dup2(pipefd_out[0], 0);
      // replace stdout
      dup2(pipefd_in[1], 1);
      // replace stderr :P
      null = open("/dev/null", O_RDONLY);
      dup2(null, 2);
      close(null);
      // launch process !
      char dim_str[20], npoints_str[20];
      sprintf(dim_str, "%d", dim);
      sprintf(npoints_str, "%d", npoints);
      execl("discr_calc/dem_discr", "dem_discr", dim_str, npoints_str, (char*)NULL);
      // if we get to this line, the command has failed
      perror("execl()");
      exit(1);
    default: // father
      close(pipefd_in[1]);
      close(pipefd_out[0]);
      // write data into pipefd_in
      FILE *f = fdopen(pipefd_out[1], "w");
      if(f == NULL) {
        perror("fdopen()");
        exit(1);
      }
      dump(f);
      fclose(f);
      close(pipefd_out[1]);
      // read result
      f = fdopen(pipefd_in[0], "r");
      if(f == NULL) {
        perror("fdopen()");
        exit(1);
      }
      if(fscanf(f, "%lf", &result) != 1) {
        perror("fscanf()");
        exit(1);
      }
      fclose(f);
      close(pipefd_in[0]);
      // wait for child to terminate
      waitpid(pid, &status, 0);
      if(status != 0) {
        fprintf(stderr, "child process terminate with status %d\n", status);
        exit(1);
      }
  }
  return result;
}

void pointset::dump(FILE* f) {
  for(int y = 0; y < npoints; y++)
    for(int x = 0; x < dim; x++)
      fprintf(f, "%lf%c", data[y][x], (x==dim-1) ? '\n' : '\t');
}
