#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "likelihood.h"
#include "chisquared.h"
#include <gsl/gsl_rng.h>
#include <gsl/gsl_histogram2d.h>

/* Options for the program */
struct options {
  int show_hist;      // whether to print the 2D histogram
  int only_hist;      // whether to skip the minimisations
  size_t num_events;  // number of generated events
  size_t bins_theta;  // number of bins for θ
  size_t bins_phi;    // number of bins for φ
};


int main(int argc, char **argv) {
  /* Set default options */
  struct options opts;
  opts.num_events = 50000;
  opts.show_hist  = 0;
  opts.only_hist  = 0;
  opts.bins_theta = 30;
  opts.bins_phi   = 60;

  /* Process CLI arguments */
  for (size_t i = 1; i < argc; i++) {
         if (!strcmp(argv[i], "-i")) opts.show_hist  = 1;
         if (!strcmp(argv[i], "-I")) opts.only_hist  = 1;
    else if (!strcmp(argv[i], "-n")) opts.num_events = atol(argv[++i]);
    else if (!strcmp(argv[i], "-t")) opts.bins_theta = atol(argv[++i]);
    else if (!strcmp(argv[i], "-p")) opts.bins_phi   = atol(argv[++i]);
    else {
      fprintf(stderr, "Usage: %s -[hiIntp]\n", argv[0]);
      fprintf(stderr, "\t-h\tShow this message.\n");
      fprintf(stderr, "\t-i\tPrint the histogram to stdout.\n");
      fprintf(stderr, "\t-I\tPrint *only* the histogram.\n");
      fprintf(stderr, "\t-n N\tThe number of events to generate.\n");
      fprintf(stderr, "\t-t N\tThe number of θ bins.\n");
      fprintf(stderr, "\t-p N\tThe number of φ bins.\n");
      return EXIT_FAILURE;
    }
  }

  /* Initialize an RNG. */
  gsl_rng_env_setup();
  gsl_rng *r = gsl_rng_alloc(gsl_rng_default);

  /* Initialise the sample data structure with
   * the number of events and allocate an array
   * for the events.
   */
  struct sample s = { opts.num_events, NULL };
  s.events = calloc(s.size, sizeof(struct event));

  /* Prepare a 2D histogram of the sample
   * with 30x60 bins.
   */
  gsl_histogram2d* hist =
    gsl_histogram2d_alloc(opts.bins_theta, opts.bins_phi);
  // set the ranges θ∈[0, π] and φ∈[0, 2π]
  gsl_histogram2d_set_ranges_uniform(hist, 0,M_PI, 0,2*M_PI);

  /* F(θ.φ) parameters we will generate
   * a sample with, and later try to recover.
   * Note: for this choice max F(θ,φ) ≈ 0.179.
   */
  gsl_vector *def_par = gsl_vector_alloc(3);
  gsl_vector_set(def_par, 0,  0.65);  // α
  gsl_vector_set(def_par, 1,  0.06);  // β
  gsl_vector_set(def_par, 2, -0.18);  // γ

  /* Generate the points (θ, φ) on the unit sphere
   * using the inverse transform:
   *   θ = acos(1 - 2X)
   *   φ = 2πY
   * where X,Y are random uniform variables in [0,1).
   * This ensures that θ,φ are uniformly distributed, then we
   * proceed to reject the events as follow:
   *   1. generate a y ∈ [0, max F(θ,φ)] uniformly
   *   2. reject the sample if y > F(θ,φ) else save it.
   */
  fputs("# event sampling\n\n", stderr);
  fprintf(stderr, "generating %ld events...", opts.num_events);
  struct event *e;
  for (size_t i = 0; i < s.size; i++){
    e = &s.events[i];
    do {
      e->th = acos(1 - 2*gsl_rng_uniform(r));
      e->ph = 2 * M_PI * gsl_rng_uniform(r);
    } while(0.2 * gsl_rng_uniform(r) > distr(def_par, e));

    // update the histogram
    gsl_histogram2d_increment(hist, e->th, e->ph);
  }
  fputs("done\n", stderr);

  // print histogram to stdout
  if (opts.show_hist || opts.only_hist) {
    fputs("\n# histogram\n", stderr);
    gsl_histogram2d_fprintf(stdout, hist, "%g", "%g");
  }

  if (!opts.only_hist) {
    /* Estimate the parameters α,β,γ by
     * the maximum likelihood method.
     */
    min_result like = maxLikelihood(&s);

    /* Estimate the parameters α,β,γ by 
     * a minimum χ² method.
     */
    struct hist data;
    data.h = hist;
    data.n = s.size;
    min_result chi2 = minChiSquared(data);

    /* Compare the results with the original ones
     * and check whether they are compatible.
     */
    fputs("\n# vector decay compatibility\n", stderr);

    fputs("\n## likelihood results\n\n", stderr);
    compatibility(def_par, like);

    fputs("\n## χ² results\n\n", stderr);
    compatibility(def_par, chi2);

    /* Test the isotropic decay hypothesys
     * with the χ² results
     */
    fputs("\n# isotropic hypotesys\n\n", stderr);

    /* If the decay is isotropic then F = 1/4π.
     * Solving for α,β,γ yields α=1/3,β=γ=0.
     */
    gsl_vector *iso_par = gsl_vector_alloc(3);
    gsl_vector_set(iso_par, 0, 0.33);  // α
    gsl_vector_set(iso_par, 1, 0.00);  // β
    gsl_vector_set(iso_par, 2, 0.00);  // γ

    compatibility(iso_par, chi2);

    // free memory
    min_result_free(like);
    min_result_free(chi2);
  };

  // free memory
  gsl_rng_free(r);
  free(s.events);
  gsl_histogram2d_free(hist);
  gsl_vector_free(def_par);

  return EXIT_SUCCESS;
}
