#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <args/args.h>
#include <string/conv.h>
#include <string/string.h>
#include <uprintf.h>
#include <errors.h>

int32_t parse_args(char **argv, size_t argc, Arg *defs) {
  #define CONSUME_TK() i++

  if (!argv || !defs) {
    return E_INVALIDARGUMENT;
  }

  for (size_t i = 0; i < argc; i++) {
    char *arg = argv[i];
    if (!arg) {
      continue;
    }

    if (arg[0] == '-' && arg[1] != '\0') {
      size_t j = 0;
      while (!defs[j].end) {
        Arg *def = &defs[j];

        if (!def->shortname || !def->ptr) {
          j++;
          continue;
        }

        if (string_strcmp(def->shortname, arg) == 0) {
          bool have_next = (i + 1) < argc && argv[i + 1] != NULL;
          char *nexttk = have_next ? argv[i + 1] : NULL;

          if (nexttk && nexttk[0] == '-' && nexttk[1] != '\0') {
            have_next = false;
            nexttk = NULL;
          }

          switch (def->expected_value) {
            case ARG_STRING:
              if (!have_next) {
                break;
              }
              *((char **)def->ptr) = nexttk;
              CONSUME_TK();
              break;
            case ARG_BOOL:
              if (!have_next) {
                *((bool *)def->ptr) = true;
              } else {
                if (string_strcmp(nexttk, "yes") == 0) {
                  *((bool *)def->ptr) = true;
                  CONSUME_TK();
                } else if (string_strcmp(nexttk, "no") == 0) {
                  *((bool *)def->ptr) = false;
                  CONSUME_TK();
                } else {
                  *((bool *)def->ptr) = true;
                }
              }
              break;
            case ARG_INT:
              if (!have_next) {
                break;
              }
              char *endptr = NULL;
              long val = string_conv_strtol(nexttk, &endptr, 10);
              if (endptr == NULL) {
                break;
              }
              if (*endptr != '\0') {
                break;
              }
              *((int32_t *)def->ptr) = (int32_t)val;
              CONSUME_TK();
              break;
            default:
              break;
          }

          break;
        }

        j++;
      }
    }
  }

  return E_OK;
}
