#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <sysexits.h>
#include <vlcutils/error.h>
#include <config.h>
#include "index.h"
#include "matchtable.h"

static void usage(FILE *stream)
{
     fprintf(stream, "Usage: %s [OPTION]... INDEX\n", program_name);
     fprintf(stream, "Construct the match table for an INDEX and QUERY\n");
     if (stream == stderr)
	  fprintf(stream, "Try `%s -h` for more information.\n", program_name);
}

#define DEFAULT_ERROR_RATE 0.01

static void help(void)
{
     usage(stdout);
     printf("\n"
"      -h            Display this help.\n"
"      -V            Display version number.\n"
"      -a            Output match table in ASCII format.\n"
"      -b            Output match table in binary format.\n"
"      -B            Use boxes for both strings.\n"
"      -t            Use B+-trees.  (Cannot be used with -n.)\n"
"      -n            Nested-loop join.  (Cannot be used with -t.)\n"
"      -p NUMBER     Partition the vector space NUMBER ways in each dimension.\n"
"      -e NUMBER     Use error rate NUMBER (default: %f).", DEFAULT_ERROR_RATE);
     printf("\n"
"      -i FILENAME   Read query string from FILENAME instead of standard input.\n"
"      -o FILENAME   Write matchtable to FILENAME instead of standard output.\n"
"      -q            Be quiet, don't print statistics to standard output.\n");
}

static const char *index_filename, *index2_filename;
static const char *query_filename = NULL, *output_filename = NULL;
static double error_rate = DEFAULT_ERROR_RATE;
static int quiet = 0, boxes = 0, bplus = 0, nlj = 0, partitions = 1,
     output_ascii = 0, output_binary = 0;

static void parse_options(int argc, char *argv[])
{
     int opt;
     char *parse_end;

     while ((opt = getopt(argc, argv, "hVabBtnp:e:i:o:q")) != -1)
	  switch (opt) {
	  case 'h':
	       help();
	       exit(EX_OK);
	  case 'V':
	       printf("%s %s\n", PACKAGE, VERSION);
	       exit(EX_OK);
          case 'a':
               output_ascii = 1;
               break;
          case 'b':
               output_binary = 1;
               break;
          case 'B':
               boxes = 1;
               break;
          case 't':
               bplus = 1;
               break;
          case 'n':
               nlj = 1;
               break;
          case 'p':
               partitions = strtol(optarg, &parse_end, 10);
               if (parse_end == optarg)
                    fatal_error("Invalid number: %s", optarg);
               if (partitions < 0)
                    fatal_error("Negative number of parititions: %d", 
                                partitions);
               break;
	  case 'e':
	       error_rate = strtod(optarg, &parse_end);
	       if (parse_end == optarg)
		    fatal_error("Invalid number: %s", optarg);
	       if (error_rate < 0)
		    fatal_error("Negative error rate: %f", error_rate);
	       break;
	  case 'i':
	       query_filename = optarg;
	       break;
	  case 'o':
	       output_filename = optarg;
	       break;
	  case 'q':
	       quiet = 1;
	  default:
	       usage(stderr);
	       exit(EX_USAGE);
	  }
     if (argc != optind + (boxes ? 2 : 1)) {
	  usage(stderr);
	  exit(EX_USAGE);
     }
     index_filename = argv[optind];
     if (boxes) {
          index2_filename = argv[optind + 1];
          if (query_filename)
               fatal_error("Cannot use options -b and -i together.");
     }
     if (bplus && nlj)
          fatal_error("Cannot use options -t and -n together.");
     if (partitions > 1 && !nlj)
          fatal_error("Option -p only works together with -n.");
     if (partitions > 1 && boxes)
          fatal_error("Cannot use options -p and -b together.");
     if (output_ascii && output_binary)
          fatal_error("Cannot use options -a and -b together.");
}

int main(int argc, char *argv[])
{
     FILE *input, *output;
     struct index *index, *index2;
     struct matchtable *matchtable;

     program_name = argv[0];
     parse_options(argc, argv);
     
     if (query_filename) {
	  input = fopen(query_filename, "r");
	  if (!input)
	       fatal_perror("%s", query_filename);
     } else
	  input = stdin;

     if (output_filename) {
	  output = fopen(output_filename, "w");
	  if (!output)
	       fatal_perror("%s", output_filename);
     } else
	  output = stdout;

     index = read_index(index_filename);
     if (boxes) {
          index2 = read_index(index2_filename);
          if (nlj)
               matchtable = compute_matchtable_nlj_boxes(index, index2, 
                                                         error_rate);
          else
               matchtable = compute_matchtable_boxes(index, index2, 
                                                     error_rate);
     } else {
          if (nlj) {
               if (partitions > 1)
                    matchtable = compute_matchtable_partitions
                         (input, index, error_rate, partitions);
               else
                    matchtable = compute_matchtable_nlj
                         (input, index, error_rate);
          } else if (bplus)
               matchtable = compute_matchtable_bplus(input, index, error_rate);
          else
               matchtable = compute_matchtable(input, index, error_rate);
     }
     if (output_ascii)
          fprint_matchtable(output, matchtable);
     else if (output_binary)
          fwrite_matchtable(output, matchtable);
     fclose(output);
     free_matchtable(matchtable);
     return EX_OK;
}
