/*
 * This file is part of the Poliqarp suite.
 * 
 * Copyright (C) 2004-2009 by Instytut Podstaw Informatyki Polskiej
 * Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 * Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file gpl.txt included in the packaging
 * of this file.  (See http://www.gnu.org/licenses/translations.html for
 * unofficial translations.)
 * 
 * A commercial license is available from IPI PAN (contact
 * Michal.Ciesiolka@ipipan.waw.pl or ipi@ipipan.waw.pl for more
 * information).  Licensees holding a valid commercial license from IPI
 * PAN may use this file in accordance with that license.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

#include <errno.h>
#include <getopt.h>
#include <stdio.h>

#include <sakura/backend-index.h>

#include <bp/common.h>

#include "cmdline.h"

#define VERSION PACKAGE_VERSION

struct command_line_data cmdline = {
   NULL, NULL, 30 * 1024 * 1024, 
   POLIQARP_INDEX_DEFAULT_GRANULARITY, 
   POLIQARP_INDEX_ORTH | POLIQARP_INDEX_DISAMB | POLIQARP_INDEX_AMB,
};

static void set_memory_usage()
{
   char *end;
   bool err = false;
   errno = 0;
   unsigned long ul_mem = strtoul(optarg, &end, 10);
   size_t mem = ul_mem;
   if (*optarg == '\0')
      err = true; /* no digits converted */
   else if (errno == ERANGE || ul_mem != mem)
      err = true; /* integer overflow */
   else
   switch (*end) {
      case 0:
         break;
      case 'K': case 'k':
         if (*(end + 1))
            err = true;
         else if (mem != (mem << 10) >> 10)
            err = true; /* integer overflow */
         else
            mem <<= 10;
         break;
      case 'M': case 'm':
         if (*(end + 1))
            err = true;
         else if (mem != (mem << 20) >> 20)
            err = true; /* integer overflow */
         else
            mem <<= 20;
         break;
      case 'G': case 'g':
         if (*(end + 1))
            err = true;
         if (mem != (mem << 30) >> 30)
            err = true; /* integer overflow */
         else
            mem <<= 30;
         break;
      default:
         err = true;
   }
   if (mem < 100000 || err)
      bp_fatal("incorrect memory limit");
   cmdline.memory = mem;
}

static void set_granularity()
{
   char *end;
   errno = 0;
   unsigned long arg = strtoul(optarg, &end, 10);
   if (errno == ERANGE || *optarg == '\0' || *end != '\0' ||
      arg < POLIQARP_INDEX_MIN_GRANULARITY ||
      arg > POLIQARP_INDEX_MAX_GRANULARITY)
   {
      bp_fatal("incorrect index granularity");
   }
   cmdline.granularity = arg;
}

static void set_indices()
{
   int indices = poliqarp_index_calculate(optarg);
   if (indices == -1) 
      bp_fatal("incorrect index specification character");
   cmdline.indices = indices;
}

static void display_usage(const char *argv0)
{
   bp_display_usage(argv0,
      "[option...] <corpus> [index]",
      "Create inverted indices for binary corpora in the IPI PAN format.",
      "  -h, --help              display this help and exit\n"
      "  -v, --version           display version information and exit\n"
      "  -q, --quiet             be quiet, suppress progress information\n"
      "  -m, --memory-usage=MEM  set an upper limit on memory usage\n"
      "  -g, --granularity=GRAN  set the granularity of the resulting index\n"
      "  -i, --indices=LIST      create only the given indices\n",
      "MEM is the approximation of the limit on memory usage, in bytes.\n"
      "This number can be followed by a letter K (which specifies kilobytes\n"
      "as the unit) or M (megabytes) or G (gigabytes).\n"
      "GRAN must be a value between 100 and 1000000.\n"
      "LIST is a string of one to three different letters, with the following\n"
      "meanings:\n"
      "   o    create an index of orthographic forms\n"
      "   d    create an index of disambiguated interpretations\n"
      "   a    create an index of ambiguous interpretations\n\n"
      "If no options are given, bpindexer defaults to -m 30M -g 1024 -i oda.\n"
   );
}

void cmdline_process(int argc, char *argv[])
{
   const struct option options[] = {
      { "help",         0, NULL, 'h' },
      { "version",      0, NULL, 'v' },
      { "quiet",        0, NULL, 'q' },
      { "memory-usage", 1, NULL, 'm' },
      { "granularity",  1, NULL, 'g' },
      { "indices",      1, NULL, 'i' },
      { 0,              0, 0,    0 }
   };
   int opt;

   while ((opt = getopt_long(argc, argv, "hvqm:g:i:", options, NULL)) != -1)
   {
      switch (opt) {
         case 'h':
            display_usage(*argv);
            break;
         case 'v':
            bp_display_version();
            break;
         case 'q':
            bp_set_verbosity(0);
            break;
         case 'm':
            set_memory_usage();
            break;
         case 'g':
            set_granularity();
            break;
         case 'i':
            set_indices();
            break;
         default:
            exit(EXIT_FAILURE);
      }
   }
   if (optind == argc)
      bp_fatal("no corpus image to invert");
   cmdline.input_file = argv[optind++];
   if (optind == argc) {
      cmdline.output_file = cmdline.input_file;
   } else {
      cmdline.output_file = argv[optind++];
   }
   if (optind != argc)
      bp_warning("ignoring extra parameters");
}

