/****************************************************************************
**  SIONLIB     http://www.fz-juelich.de/jsc/sionlib                       **
*****************************************************************************
**  Copyright (c) 2008-2018                                                **
**  Forschungszentrum Juelich, Juelich Supercomputing Centre               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

#define _XOPEN_SOURCE 700

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

#include "sion_common.h"
#include "sion_flags.h"
#include "sion_internal.h"

#define DFUNCTION "_lsion_flags_init_entry"
void _lsion_flags_init_entry(_sion_flags_entry *entry)
{
  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  entry->key = NULL;
  entry->val = NULL;
  entry->next = NULL;

  DPRINTFP((2, DFUNCTION, -1, "leave\n"));
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_create_entry"
_sion_flags_entry *_lsion_flags_create_entry(void)
{
  _sion_flags_entry *new_entry = NULL;

  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  new_entry = malloc(sizeof(_sion_flags_entry));
  _lsion_flags_init_entry(new_entry);

  DPRINTFP((2, DFUNCTION, -1, "leave\n"));

  return new_entry;
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_destroy_entry"
void _lsion_flags_destroy_entry(_sion_flags_entry **entry)
{
  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  free((*entry)->key);
  free((*entry)->val);
  free(*entry);
  *entry = NULL;

  DPRINTFP((2, DFUNCTION, -1, "leave\n"));
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_iter"
_sion_flags_entry *_lsion_flags_iter(const _sion_flags_store *store)
{
  DPRINTFP((2, DFUNCTION, -1, "enter\n"));
  DPRINTFP((2, DFUNCTION, -1, "leave\n"));

  return store->root;
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_init_store"
void _lsion_flags_init_store(_sion_flags_store *store)
{
  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  store->root = _lsion_flags_create_entry();
  store->current = store->root;

  DPRINTFP((2, DFUNCTION, -1, "leave\n"));
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_create_store"
_sion_flags_store *_lsion_flags_create_store(void)
{
  _sion_flags_store *new_store = NULL;

  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  new_store = malloc(sizeof(_sion_flags_store));
  _lsion_flags_init_store(new_store);

  DPRINTFP((2, DFUNCTION, -1, "leave\n"));

  return new_store;
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_destroy_store"
void _lsion_flags_destroy_store(_sion_flags_store **store)
{
  _sion_flags_entry *entry = NULL;
  _sion_flags_entry *next = NULL;

  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  entry = _lsion_flags_iter(*store);

  while (entry != NULL) {
    next = entry->next;
    _lsion_flags_destroy_entry(&entry);
    entry = next;
  }

  free(*store);
  *store = NULL;

  DPRINTFP((2, DFUNCTION, -1, "leave\n"));
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_add_entry"
_sion_flags_entry *_lsion_flags_add_entry(_sion_flags_entry *entry, const char *key, const char *val)
{
  _sion_flags_entry *new_entry = NULL;

  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  /* include terminating null */
  entry->key = malloc(strlen(key) + 1);
  entry->val = malloc(strlen(val) + 1);
  new_entry = _lsion_flags_create_entry();
  strcpy(entry->key, key);
  strcpy(entry->val, val);
  entry->next = new_entry;

  DPRINTFP((2, DFUNCTION, -1, "leave (entry->key = %s, entry->val = %s\n", entry->key, entry->val));

  return new_entry;
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_add"
void _lsion_flags_add(_sion_flags_store *store, const char *key, const char *val)
{
  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  store->current = _lsion_flags_add_entry(store->current, key, val);

  DPRINTFP((2, DFUNCTION, -1, "leave\n"));
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_get"
_sion_flags_entry *_lsion_flags_get(const _sion_flags_store *store, const char *key)
{
  _sion_flags_entry *iter = NULL;

  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  for (iter = _lsion_flags_iter(store); iter->next != NULL; iter = iter->next) {
    if (!strcmp(iter->key, key)) {
      DPRINTFP((2, DFUNCTION, -1, "leave (key in store)\n"));
      return iter;
    }
  }

  DPRINTFP((2, DFUNCTION, -1, "leave (key not in store)\n"));

  return NULL;
}
#undef DFUNCTION

#define DFUNCTION "_lsion_flags_update_mask"
int64_t _lsion_flags_update_mask(_sion_flags_store *store)
{
  _sion_flags_entry *iter = NULL;

  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  store->mask = 0;
  store->mask |= _SION_FMODE_ANSI;

  for (iter = _lsion_flags_iter(store); iter->next != NULL; iter = iter->next) {
    if ((!strcmp(iter->key, "w")) || (!strcmp(iter->key, "wb")) || (!strcmp(iter->key, "bw"))) {
      store->mask |= _SION_FMODE_WRITE;
    } else if ((!strcmp(iter->key, "r")) || (!strcmp(iter->key, "rb")) || (!strcmp(iter->key, "br"))) {
      store->mask |= _SION_FMODE_READ;
    } else if (!strcmp(iter->key, "buffered")) {
      store->mask |= _SION_FMODE_BUFFERED;
    } else if (!strcmp(iter->key, "buddy")) {
      store->mask |= _SION_FMODE_BUDDY;
    } else if (!strcmp(iter->key, "compress")) {
      store->mask |= _SION_FMODE_COMPRESS;
    } else if (!strcmp(iter->key, "collective")) {
      store->mask |= _SION_FMODE_COLLECTIVE;
    } else if ((!strcmp(iter->key, "collectivemerge")) || (!strcmp(iter->key, "cmerge"))) {
      store->mask |= _SION_FMODE_COLLECTIVE_MERGE;
    } else if (!strcmp(iter->key, "keyval")) {
      if ((!strcmp(iter->val, "default")) || (!strcmp(iter->val, "inline")) || (!strcmp(iter->val, ""))) {
        store->mask |= _SION_FMODE_KEYVAL_INLINE;
      } else if (!strcmp(iter->key, "meta")) {
        store->mask |= _SION_FMODE_KEYVAL_META;
      } else if (!strcmp(iter->key, "hash")) {
        store->mask |= _SION_FMODE_KEYVAL_HASH;
      } else if (!strcmp(iter->key, "none")) {
        store->mask |= _SION_FMODE_KEYVAL_NONE;
      } else if (!strcmp(iter->key, "unknown")) {
        store->mask |= _SION_FMODE_KEYVAL_UNKNOWN;
      }
    } else if (!strcmp(iter->key, "endianness")) {
      store->mask |= _SION_FMODE_ENDIANNESS_SET;
      if (!strcmp(iter->val, "big")) {
        store->mask |= _SION_FMODE_ENDIANNESS_BIG;
      }
    } else if (!strcmp(iter->key, "posix")) {
      store->mask |= _SION_FMODE_POSIX;
      store->mask ^= store->mask & _SION_FMODE_ANSI;
    } else if (!strcmp(iter->key, "ansi")) {
      store->mask |= _SION_FMODE_ANSI;
      store->mask ^= store->mask & _SION_FMODE_POSIX;
    }
  }

  DPRINTFP((2, DFUNCTION, -1, "leave (mask = %llx)\n", store->mask));

  return store->mask;
}
#undef DFUNCTION

#define DFUNCTION "_lsion_parse_flags"
_sion_flags_store *_lsion_parse_flags(const char *flags)
{
  char *tmps = NULL;
  char *key = NULL;
  char *val = NULL;
  char *pch = NULL;
  char *saveptr = NULL;
  const char *delim_tok = ",";
  const char *delim_kv = "=";
  int span = 0;
  /* include terminating null */
  const int len = strlen(flags) + 1;
  _sion_flags_store *store = NULL;

  DPRINTFP((2, DFUNCTION, -1, "enter\n"));

  store = _lsion_flags_create_store();

  tmps = malloc(len);
  key = malloc(len);
  val = malloc(len);
  strcpy(tmps, flags);

  DPRINTF((32, DFUNCTION, -1, "tmps = '%s'\n", tmps));
  DPRINTF((32, DFUNCTION, -1, "delim_tok = '%s'\n", delim_tok));
  DPRINTF((32, DFUNCTION, -1, "delim_kv = '%s'\n", delim_kv));
  pch = strtok_r(tmps, delim_tok, &saveptr);
  while (pch != NULL) {
    span = strcspn(pch, delim_kv);
    strncpy(key, pch, span);
    key[span] = '\0';
    if (span < strlen(pch)) {
      strcpy(val, &pch[span + 1]);
    } else {
      val[0] = '\0';
    }
    DPRINTF((32, DFUNCTION, -1, "key = '%s'  |  val = '%s'\n", key, val));
    _lsion_flags_add(store, key, val);
    pch = strtok_r(NULL, delim_tok, &saveptr);
  }

  free(tmps);
  free(key);
  free(val);

  _lsion_flags_update_mask(store);

  DPRINTFP((2, DFUNCTION, -1, "leave\n"));

  return store;
}
#undef DFUNCTION
