#include <stdlib.h>

#include "luadebug.h"
#include "table.h"
#include "mem.h"
#include "func.h"
#include "opcode.h"


static TFunc *function_root = NULL;
static LocVar *currvars = NULL;
static int numcurrvars = 0;
static int maxcurrvars = 0;


/*
** Initialize TFunc struct
*/
void luaI_initTFunc (TFunc *f)
{
  f->next = NULL;
  f->marked = 0;
  f->size = 0;
  f->code = NULL;
  f->lineDefined = 0;
  f->fileName = NULL;
  f->locvars = NULL;
}

/*
** Insert function in list for GC
*/
void luaI_insertfunction (TFunc *f)
{
  lua_pack();
  f->next = function_root;
  function_root = f;
  f->marked = 0;
}


/*
** Free function
*/
void luaI_freefunc (TFunc *f)
{
  luaI_free (f->code);
  luaI_free (f->locvars);
  luaI_free (f);
}

/*
** Garbage collection function.
** This function traverse the function list freeing unindexed functions
*/
Long luaI_funccollector (void)
{
  TFunc *curr = function_root;
  TFunc *prev = NULL;
  Long counter = 0;
  while (curr)
  {
    TFunc *next = curr->next;
    if (!curr->marked)
    {
      if (prev == NULL)
        function_root = next;
      else
        prev->next = next;
      luaI_freefunc (curr);
      ++counter;
    }
    else
    {
      curr->marked = 0;
      prev = curr;
    } 
    curr = next;
  }
  return counter;
}


void lua_funcinfo (lua_Object func, char **filename, int *linedefined)
{
  Object *f = luaI_Address(func);
  if (f->tag == LUA_T_MARK || f->tag == LUA_T_FUNCTION)
  {
    *filename = f->value.tf->fileName;
    *linedefined = f->value.tf->lineDefined;
  }
  else if (f->tag == LUA_T_CMARK || f->tag == LUA_T_CFUNCTION)
  {
    *filename = "(C)";
    *linedefined = -1;
  }
}

/*
** Stores information to know that variable has been declared in given line
*/
void luaI_registerlocalvar (TaggedString *varname, int line)
{
  if (numcurrvars >= maxcurrvars)
    maxcurrvars = growvector(&currvars, maxcurrvars, LocVar, "", MAX_WORD);
  currvars[numcurrvars].varname = varname;
  currvars[numcurrvars].line = line;
  numcurrvars++;
}

/*
** Stores information to know that variable has been out of scope in given line
*/
void luaI_unregisterlocalvar (int line)
{
  luaI_registerlocalvar(NULL, line);
}

/*
** Copies "currvars" into a new area and store it in function header.
** The values (varname = NULL, line = -1) signal the end of vector.
*/
void luaI_closelocalvars (TFunc *func)
{
  func->locvars = newvector (numcurrvars+1, LocVar);
  memcpy (func->locvars, currvars, numcurrvars*sizeof(LocVar));
  func->locvars[numcurrvars].varname = NULL;
  func->locvars[numcurrvars].line = -1;
  numcurrvars = 0;  /* prepares for next function */
}

/*
** Look for n-esim local variable at line "line" in function "func".
** Returns NULL if not found.
*/
char *luaI_getlocalname (TFunc *func, int local_number, int line)
{
  int count = 0;
  char *varname = NULL;
  LocVar *lv = func->locvars;
  if (lv == NULL)
    return NULL;
  for (; lv->line != -1 && lv->line < line; lv++)
  {
    if (lv->varname)               /* register */
    {
      if (++count == local_number)
        varname = lv->varname->str;
    }
    else                           /* unregister */
      if (--count < local_number)
        varname = NULL;
  }
  return varname;
}