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

#ifdef USE_NLLIBC
#include <nllibc.h>
#endif

#include "config.h"
#include "lib.h"
#include "objlist.h"
#include "type.h"
#include "symbol.h"
#include "struct.h"
#include "union.h"
#include "enum.h"
#include "typedef.h"
#include "defines.h"

/*****************************************************************
 * syntax_t
 */

defines_t defines_destroy(defines_t defines)
{
  if (defines != NULL) {
    objlist_done(&defines->symbol_list);
    objlist_done(&defines->struct_list);
    objlist_done(&defines->union_list);
    objlist_done(&defines->enum_list);
    objlist_done(&defines->typedef_list);

    objentry_set_free_func(&defines->entry, NULL);
    if (objentry_done(&defines->entry) != NULL) {
      /* fail to done */
    }
    memset(defines, 0, sizeof(*defines));
    free(defines);
  }

  return NULL;
}

static void defines_free(void *p)
{
  defines_destroy((defines_t)p);
}

defines_t defines_create(void)
{
  defines_t defines;

  defines = malloc(sizeof(*defines));
  if (defines == NULL)
    goto err;
  memset(defines, 0, sizeof(*defines));

  if (objentry_init(&defines->entry, defines, 0, defines_free) == NULL)
    goto err;

  if (objlist_init(&defines->symbol_list, NULL) == NULL)
    goto err;
  if (objlist_init(&defines->struct_list, NULL) == NULL)
    goto err;
  if (objlist_init(&defines->union_list, NULL) == NULL)
    goto err;
  if (objlist_init(&defines->enum_list, NULL) == NULL)
    goto err;
  if (objlist_init(&defines->typedef_list, NULL) == NULL)
    goto err;

  return defines;

err:
  if (defines != NULL)
    defines_destroy(defines);
  return NULL;
}

/*****************************************************************
 * library
 */

/* search */

symbol_t defines_list_search_symbol_name(objlist_t defines_list, char *name)
{
  objentry_t entry;
  defines_t defines;
  symbol_t symbol;

  for (entry = objlist_get_head(defines_list);
       !objlist_is_end(defines_list, entry);
       entry = objentry_get_next(entry)) {
    defines = objentry_get_obj(entry);
    symbol = symbol_list_search(&defines->symbol_list, name);
    if (symbol != NULL)
      return symbol;
  }

  return NULL;
}

struct_t defines_list_search_struct_name(objlist_t defines_list, char *name)
{
  objentry_t entry;
  defines_t defines;
  struct_t _struct;

  for (entry = objlist_get_head(defines_list);
       !objlist_is_end(defines_list, entry);
       entry = objentry_get_next(entry)) {
    defines = objentry_get_obj(entry);
    _struct = struct_list_search(&defines->struct_list, name);
    if (_struct != NULL)
      return _struct;
  }

  return NULL;
}

union_t defines_list_search_union_name(objlist_t defines_list, char *name)
{
  objentry_t entry;
  defines_t defines;
  union_t _union;

  for (entry = objlist_get_head(defines_list);
       !objlist_is_end(defines_list, entry);
       entry = objentry_get_next(entry)) {
    defines = objentry_get_obj(entry);
    _union = union_list_search(&defines->union_list, name);
    if (_union != NULL)
      return _union;
  }

  return NULL;
}

enum_t defines_list_search_enum_name(objlist_t defines_list, char *name)
{
  objentry_t entry;
  defines_t defines;
  enum_t _enum;

  for (entry = objlist_get_head(defines_list);
       !objlist_is_end(defines_list, entry);
       entry = objentry_get_next(entry)) {
    defines = objentry_get_obj(entry);
    _enum = enum_list_search(&defines->enum_list, name);
    if (_enum != NULL)
      return _enum;
  }

  return NULL;
}

typedef_t defines_list_search_typedef_name(objlist_t defines_list, char *name)
{
  objentry_t entry;
  defines_t defines;
  typedef_t _typedef;

  for (entry = objlist_get_head(defines_list);
       !objlist_is_end(defines_list, entry);
       entry = objentry_get_next(entry)) {
    defines = objentry_get_obj(entry);
    _typedef = typedef_list_search(&defines->typedef_list, name);
    if (_typedef != NULL)
      return _typedef;
  }

  return NULL;
}

/* get */

objlist_t defines_list_get_symbol_list(objlist_t defines_list)
{
  objentry_t entry;
  defines_t defines;

  entry = objlist_get_head(defines_list);
  if (objlist_is_end(defines_list, entry))
    return NULL;
  defines = objentry_get_obj(entry);

  return &defines->symbol_list;
}

objlist_t defines_list_get_struct_list(objlist_t defines_list)
{
  objentry_t entry;
  defines_t defines;

  entry = objlist_get_head(defines_list);
  if (objlist_is_end(defines_list, entry))
    return NULL;
  defines = objentry_get_obj(entry);

  return &defines->struct_list;
}

objlist_t defines_list_get_union_list(objlist_t defines_list)
{
  objentry_t entry;
  defines_t defines;

  entry = objlist_get_head(defines_list);
  if (objlist_is_end(defines_list, entry))
    return NULL;
  defines = objentry_get_obj(entry);

  return &defines->union_list;
}

objlist_t defines_list_get_enum_list(objlist_t defines_list)
{
  objentry_t entry;
  defines_t defines;

  entry = objlist_get_head(defines_list);
  if (objlist_is_end(defines_list, entry))
    return NULL;
  defines = objentry_get_obj(entry);

  return &defines->enum_list;
}

objlist_t defines_list_get_typedef_list(objlist_t defines_list)
{
  objentry_t entry;
  defines_t defines;

  entry = objlist_get_head(defines_list);
  if (objlist_is_end(defines_list, entry))
    return NULL;
  defines = objentry_get_obj(entry);

  return &defines->typedef_list;
}

/* insert */

symbol_t defines_list_insert_symbol(objlist_t defines_list, symbol_t symbol)
{
  objlist_t list;
  list = defines_list_get_symbol_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_insert_tail(list, &symbol->entry);
  return symbol;
}

struct_t defines_list_insert_struct(objlist_t defines_list, struct_t _struct)
{
  objlist_t list;
  list = defines_list_get_struct_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_insert_tail(list, &_struct->entry);
  return _struct;
}

union_t defines_list_insert_union(objlist_t defines_list, union_t _union)
{
  objlist_t list;
  list = defines_list_get_union_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_insert_tail(list, &_union->entry);
  return _union;
}

enum_t defines_list_insert_enum(objlist_t defines_list, enum_t _enum)
{
  objlist_t list;
  list = defines_list_get_enum_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_insert_tail(list, &_enum->entry);
  return _enum;
}

typedef_t defines_list_insert_typedef(objlist_t defines_list, typedef_t _typedef)
{
  objlist_t list;
  list = defines_list_get_typedef_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_insert_tail(list, &_typedef->entry);
  return _typedef;
}

/* delete */

symbol_t defines_list_delete_symbol(objlist_t defines_list, symbol_t symbol)
{
  objlist_t list;
  list = defines_list_get_symbol_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_extract(list, &symbol->entry);
  return symbol;
}

struct_t defines_list_delete_struct(objlist_t defines_list, struct_t _struct)
{
  objlist_t list;
  list = defines_list_get_struct_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_extract(list, &_struct->entry);
  return _struct;
}

union_t defines_list_delete_union(objlist_t defines_list, union_t _union)
{
  objlist_t list;
  list = defines_list_get_union_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_extract(list, &_union->entry);
  return _union;
}

enum_t defines_list_delete_enum(objlist_t defines_list, enum_t _enum)
{
  objlist_t list;
  list = defines_list_get_enum_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_extract(list, &_enum->entry);
  return _enum;
}

typedef_t defines_list_delete_typedef(objlist_t defines_list, typedef_t _typedef)
{
  objlist_t list;
  list = defines_list_get_typedef_list(defines_list);
  if (list == NULL)
    return NULL;
  objlist_extract(list, &_typedef->entry);
  return _typedef;
}
