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

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

#include "config.h"
#include "lib.h"
#include "objlist.h"
#include "type.h"
#include "syntax.h"
#include "area.h"
#include "struct.h"

/*****************************************************************
 * struct_t
 */

struct_t struct_destroy(struct_t _struct)
{
  if (_struct != NULL) {
    if (_struct->name != NULL)
      free(_struct->name);
    if (_struct->members != NULL)
      area_destroy(_struct->members);

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

  return NULL;
}

static void struct_free(void *p)
{
  struct_destroy((struct_t)p);
}

struct_t struct_create(char *name)
{
  struct_t _struct;
  char n[32];
  static int id = 0;

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

  if (objentry_init(&_struct->entry, _struct, 0, struct_free) == NULL)
    goto err;

  if (name == NULL) {
    sprintf(n, "struct.%d", id);
    name = n;
  }

  _struct->name = strdup(name);
  if (_struct->name == NULL)
    goto err;

  _struct->members = area_create(C_TYPE_STRUCT);
  if (_struct->members == NULL)
    goto err;

  _struct->id = id++;

  return _struct;

err:
  if (_struct != NULL)
    struct_destroy(_struct);
  return NULL;
}

void struct_print(struct_t _struct, int indent)
{
  indent_print(indent);
  printf("STRUCT: %s (id:0x%x)\n", _struct->name, _struct->id);
  indent++;
  area_print(_struct->members, indent);
}

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

struct_t struct_list_search(objlist_t list, char *name)
{
  objentry_t entry;
  struct_t _struct;

  for (entry = objlist_get_head(list);
       !objlist_is_end(list, entry);
       entry = objentry_get_next(entry)) {
    _struct = objentry_get_obj(entry);
    if ((_struct->name != NULL) && !strcmp(name, _struct->name))
      return _struct;
  }

  return NULL;
}

static int set_members(struct_t _struct, objlist_t symbols)
{
  objentry_t entry;
  symbol_t symbol;

  while ((entry = objlist_extract_head(symbols)) != NULL) {
    symbol = objentry_get_obj(entry);
    if (area_insert_symbol(_struct->members, symbol) == NULL)
      return -1;
  }

  return _struct->members->size;
}

static objlist_t read_members(objlist_t syntax_list,
			      objlist_t defines_list, objlist_t symbols,
			      area_t static_area, area_t stack_area)
{
  objlist_t list, words;
  objentry_t entry;
  syntax_t syntax;
  symbol_t symbol;

  list = objlist_create(NULL);

  while ((words = symbol_define_read(syntax_list, NULL, defines_list, symbols,
				     static_area, stack_area)) != NULL) {
    while ((entry = objlist_extract_head(words)) != NULL) {
      symbol = objentry_get_obj(entry);
      if (symbol_list_search(list, symbol->name) != NULL)
	ERREXIT(1);
      objlist_insert_tail(list, entry);
    }
    objlist_destroy(words);
  }

  if ((syntax = objlist_extract_syntax_head(syntax_list)) != NULL)
    ERREXITAT(syntax->line, 1);

  return list;
}

int struct_normalize(syntax_t syntax, objlist_t syntax_list,
		     objlist_t defines_list)
{
  objentry_t entry;
  syntax_t s;
  struct_t _struct = NULL;
  objlist_t symbols;

  saveline(syntax->line);

  entry = objlist_get_head(syntax_list);
  if (objlist_is_end(syntax_list, entry))
    ERREXIT(1);
  s = objentry_get_syntax(entry);

  if (s->type == C_TYPE_WORD) {
    _struct = defines_list_search_struct_name(defines_list, s->obj.word);
    if (_struct == NULL) {
      _struct = struct_create(s->obj.word);
      defines_list_insert_struct(defines_list, _struct);
    }

    objlist_extract(syntax_list, &s->entry);
    syntax_destroy(s);

    entry = objlist_get_head(syntax_list);
    if (objlist_is_end(syntax_list, entry))
      s = NULL;
    else
      s = objentry_get_syntax(entry);
  }

  if ((s != NULL) && (s->type == C_TYPE_MBRACKET_IN)) {
    if (_struct == NULL) {
      _struct = struct_create(NULL);
      defines_list_insert_struct(defines_list, _struct);
    }
    symbols = read_members(s->obj.words, defines_list, NULL, NULL, NULL);
    s->obj.words = NULL;

    objlist_extract(syntax_list, &s->entry);
    syntax_destroy(s);

    if (set_members(_struct, symbols) < 0)
      ERREXIT(1);
  }

  if (_struct)
    syntax->obj.custom._struct = _struct;

  return 0;
}
