/*
 * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic
 * James S. Plank, Ethan L. Miller, Kevin M. Greenan,
 * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride.
 *
 * gf_method.c
 *
 * Parses argv to figure out the mult_type and arguments.  Returns the gf.
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "gf_complete.h"
#include "gf_int.h"
#include "gf_method.h"

int create_gf_from_argv(gf_t *gf, int w, int argc, char **argv, int starting)
{
  int mult_type, divide_type, region_type;
  int arg1, arg2;
  uint64_t prim_poly;
  gf_t *base;

  mult_type = GF_MULT_DEFAULT;
  region_type = GF_REGION_DEFAULT;
  divide_type = GF_DIVIDE_DEFAULT;
  prim_poly = 0;
  base = NULL;
  arg1 = 0;
  arg2 = 0;
  while (1) {
    if (argc > starting) {
      if (strcmp(argv[starting], "-m") == 0) {
        starting++;
        if (mult_type != GF_MULT_DEFAULT) {
          if (base != NULL) gf_free(base, 1);
          _gf_errno = GF_E_TWOMULT;
          return 0;
        }
        if (strcmp(argv[starting], "SHIFT") == 0) {
          mult_type = GF_MULT_SHIFT;
          starting++;
        } else if (strcmp(argv[starting], "CARRY_FREE") == 0) {
          mult_type = GF_MULT_CARRY_FREE;
          starting++;
        } else if (strcmp(argv[starting], "CARRY_FREE_GK") == 0) {
          mult_type = GF_MULT_CARRY_FREE_GK;
          starting++;
        } else if (strcmp(argv[starting], "GROUP") == 0) {
          mult_type = GF_MULT_GROUP;
          if (argc < starting + 3) {
            _gf_errno = GF_E_GROUPAR;
            return 0;
          }
          if (sscanf(argv[starting+1], "%d", &arg1) == 0 ||
              sscanf(argv[starting+2], "%d", &arg2) == 0) {
            _gf_errno = GF_E_GROUPNU;
            return 0;
          }
          starting += 3;
        } else if (strcmp(argv[starting], "BYTWO_p") == 0) {
          mult_type = GF_MULT_BYTWO_p;
          starting++;
        } else if (strcmp(argv[starting], "BYTWO_b") == 0) {
          mult_type = GF_MULT_BYTWO_b;
          starting++;
        } else if (strcmp(argv[starting], "TABLE") == 0) {
          mult_type = GF_MULT_TABLE;
          starting++;
        } else if (strcmp(argv[starting], "LOG") == 0) {
          mult_type = GF_MULT_LOG_TABLE;
          starting++;
        } else if (strcmp(argv[starting], "LOG_ZERO") == 0) {
          mult_type = GF_MULT_LOG_ZERO;
          starting++;
        } else if (strcmp(argv[starting], "LOG_ZERO_EXT") == 0) {
          mult_type = GF_MULT_LOG_ZERO_EXT;
          starting++;
        } else if (strcmp(argv[starting], "SPLIT") == 0) {
          mult_type = GF_MULT_SPLIT_TABLE;
          if (argc < starting + 3) {
            _gf_errno = GF_E_SPLITAR;
            return 0;
          }
          if (sscanf(argv[starting+1], "%d", &arg1) == 0 ||
              sscanf(argv[starting+2], "%d", &arg2) == 0) {
            _gf_errno = GF_E_SPLITNU;
            return 0;
          }
          starting += 3;
        } else if (strcmp(argv[starting], "COMPOSITE") == 0) {
          mult_type = GF_MULT_COMPOSITE;
          if (argc < starting + 2) { _gf_errno = GF_E_FEWARGS; return 0; }
          if (sscanf(argv[starting+1], "%d", &arg1) == 0) {
            _gf_errno = GF_E_COMP_A2;
            return 0;
          }
          starting += 2;
          base = (gf_t *) malloc(sizeof(gf_t));
          starting = create_gf_from_argv(base, w/arg1, argc, argv, starting);
          if (starting == 0) {
            free(base);
            return 0;
          }
        } else {
          _gf_errno = GF_E_UNKNOWN;
          return 0;
        }
      } else if (strcmp(argv[starting], "-r") == 0) {
        starting++;
        if (strcmp(argv[starting], "DOUBLE") == 0) {
          region_type |= GF_REGION_DOUBLE_TABLE;
          starting++;
        } else if (strcmp(argv[starting], "QUAD") == 0) {
          region_type |= GF_REGION_QUAD_TABLE;
          starting++;
        } else if (strcmp(argv[starting], "LAZY") == 0) {
          region_type |= GF_REGION_LAZY;
          starting++;
        } else if (strcmp(argv[starting], "SIMD") == 0) {
          region_type |= GF_REGION_SIMD;
          starting++;
        } else if (strcmp(argv[starting], "NOSIMD") == 0) {
          region_type |= GF_REGION_NOSIMD;
          starting++;
        } else if (strcmp(argv[starting], "SSE") == 0) {
          region_type |= GF_REGION_SIMD;
          starting++;
        } else if (strcmp(argv[starting], "NOSSE") == 0) {
          region_type |= GF_REGION_NOSIMD;
          starting++;
        } else if (strcmp(argv[starting], "CAUCHY") == 0) {
          region_type |= GF_REGION_CAUCHY;
          starting++;
        } else if (strcmp(argv[starting], "ALTMAP") == 0) {
          region_type |= GF_REGION_ALTMAP;
          starting++;
        } else {
          if (base != NULL) gf_free(base, 1);
          _gf_errno = GF_E_UNK_REG;
          return 0;
        }
      } else if (strcmp(argv[starting], "-p") == 0) {
        starting++;
        if (sscanf(argv[starting], "%llx", (long long unsigned int *)(&prim_poly)) == 0) {
          if (base != NULL) gf_free(base, 1);
          _gf_errno = GF_E_POLYSPC;
          return 0;
        }
        starting++;
      } else if (strcmp(argv[starting], "-d") == 0) {
        starting++;
        if (divide_type != GF_DIVIDE_DEFAULT) {
          if (base != NULL) gf_free(base, 1);
          _gf_errno = GF_E_TWO_DIV;
          return 0;
        } else if (strcmp(argv[starting], "EUCLID") == 0) {
          divide_type = GF_DIVIDE_EUCLID;
          starting++;
        } else if (strcmp(argv[starting], "MATRIX") == 0) {
          divide_type = GF_DIVIDE_MATRIX;
          starting++;
        } else {
          _gf_errno = GF_E_UNK_DIV;
          return 0;
        }
      } else if (strcmp(argv[starting], "-") == 0) {
         /*
         printf("Scratch size: %d\n", gf_scratch_size(w, 
                                      mult_type, region_type, divide_type, arg1, arg2));
         */
        if (gf_init_hard(gf, w, mult_type, region_type, divide_type, 
                         prim_poly, arg1, arg2, base, NULL) == 0) {
          if (base != NULL) gf_free(base, 1);
          return 0;
        } else
          return starting + 1;
      } else {
        if (base != NULL) gf_free(base, 1);
        _gf_errno = GF_E_UNKFLAG;
        return 0;
      }
    } else {
      if (base != NULL) gf_free(base, 1);
      _gf_errno = GF_E_FEWARGS;
      return 0;
    }
  }
}
