#include <stdarg.h>
#include <assert.h>

#include "types.h"
#include "mpool.h"
#include "tktree.h"
#include "clstree.h"
#include "sha1.h"
#include "listen.h"
#include "rovm.h"

#include "thread.h"
#include "thread_mutex.h"
#include "thread_cond.h"

#include "mpm_worker_fdqueue.h"
#include "mpm_worker_pod.h"
#include "mpm_worker.h"

#include "connection.h"
#include "request.h"
#include "proc_rc.h"

#include "ticket.h"
#include "file.h"

#include "el.h"
#include "gc.h"

/**
   FDS 에 정의되어 있는 field 들을 클래스 CLS 의 하위 field 로 정의한다.

   @param cls   현재 Class 를 정의할 클래스 변수 포인터
   @param fds   Field 가 선언되어 있는 구조체 포인터.
   @return      실행 중 오류가 발생할 경우, -1 을, 그렇지 않을 경우 0 을 반환하게
                된다.
 */
int
RvDefineFields (RvClass *cls, RvFieldDef *fds)
{
  RvFieldDef *fd = fds;

  while (fd->name)
    {
      RvField *f;

      if (rc_find_efield (cls, (u1 *) fd->name, (u1 *) fd->type))
        return 0;

      f = init_efield (cls, (u1 *) fd->name, (u1 *) fd->type);
      if (!f)
        return -1;

      FIELD_NEXT (f) = CLASS_FIELD (cls);
      CLASS_FIELD (cls) = f;

      fd++;
    }

  return 0;
}

/**
   CLS 에 MDS 를 읽어서 해당 Method 를 정의들을 CLS 내부에 정의한다.

   @param cls   현재 Class 를 정의할 클래스 변수 포인터
   @param mds   Method 가 선언되어 있는 변수.
   @return      실행 중 오류가 발생할 경우, -1 을, 그렇지 않을 경우 0 을 반환하게
                된다.
 */
int
RvDefineMethods (RvClass *cls, RvMethodDef *mds)
{
  RvMethodDef *md = mds;

  while (md->name)
    {
      RvMethod * m;

      if (rc_find_emethod (cls, (u1 *) md->name, (u1 *) md->type))
        return 0;

      /* Native Function 을 호출할 수 있도록 method 값들을 설정합니다.  */
      m = init_emethod ((u1 *) md->name, (u1 *) md->type);
      if (!m)
        return -1;

      METHOD_FLAG (m) |= ACCESS_NATIVE;
      METHOD_NATIVE (m) = md->func;
      METHOD_NEXT (m) = CLASS_METHOD (cls);
      
      CLASS_METHOD (cls) = m;

      md++;
    }

  return 0;
}

/**
   ObjectRef 에 사용자가 데이타를 설정할 때 사용됩니다.

   @param self  현재 ObjectRef
   @param idx   설정할 사용자 index 번호 (반드시 OBJECT_MAX_USERDATA 보가
                값이 작아야 합니다.
   @param value 설정할 값.
*/
int
RvSetUserData (RvObject *self, int idx, void *value)
{
  if (idx >= OBJECT_MAX_USERDATA)
    return -1;

  OBJECT_USERDATA_IDX (self, idx) = (unsigned long) value;

  return 0;
}

/**
   ObjectRef 에 사용자가 설정한 데이타를 얻어올 때, 사용한다.

   (외부 공개 함수)

   @param self  현재 ObjectRef
   @param idx   사용자 데이타의 index 번호
   @return      사용자가 설정한 값을 반환합니다.
*/
void *
RvGetUserData (RvObject *self, int idx)
{
  if (!self || idx >= OBJECT_MAX_USERDATA || idx < 0)
    return NULL;

  return (void *) OBJECT_USERDATA_IDX (self, idx);
}

/**
   ARGS 를 FMT 과 비교하여 해당 변수에 올바른 값을 설정해 주는 역활을 하는
   함수입니다.

   (외부 공개 함수)

   @param args  RvValue 구조체입니다.  이것은 Stack Slot 에 대한 포인터를
                가지게 됩니다.
   @param fmt   해석할 구문입니다.
   @return      성공일 경우 0 을, 그렇지 않을 경우 -1 을 반환한다.
*/
int
RvParseArg (RvObject *self, RvValue *args, const char *fmt, ...)
{
  va_list ap;
  int idx;
  char *p = (char *) fmt;

  va_start (ap, fmt);

  idx = 0;
  while (*p)
    {
      RvValue *col = args + idx;

      switch (*p)
        {
        case 'x':
          {
            RvValue **t = va_arg (ap, RvValue **);

            *t = col;
            break;
          }
        case '[':
          {
            RvObject **t = va_arg (ap, RvObject **);

            if (STACK_TYPE (col) != STACK_TYPE_ARRAYREF)
              goto error;

            *t = GET_STACK_VALUE (ARRAYREF, col);
            break;
          }
        case 't':
          {
            RvObject **t = va_arg (ap, RvObject **);

            if (STACK_TYPE (col) != STACK_TYPE_OBJREF)
              goto error;

            *t = GET_STACK_VALUE (OBJREF, col);
            break;
          }
        case 'S':
          {
            RvObject **t = va_arg (ap, RvObject **);

            if (STACK_TYPE (col) != STACK_TYPE_STRINGREF)
              goto error;

            *t = GET_STACK_VALUE (STRINGREF, col);
            break;
          }
        case 's':
          {
            char **s = va_arg (ap, char **);
            RvValue *value;
            RvObject *strobj;

            if (STACK_TYPE (col) != STACK_TYPE_STRINGREF)
              goto error;

            strobj = GET_STACK_V_STRINGREF (col);
            value = RvGetFieldValue (strobj, "value", "[");
            if (!value)
              goto error;
            *s = RvValue2Str (strobj, value);

            if (p[1] == '#')
              {
                int *len = va_arg (ap, int *);

                p++;
                if (RvGetField (strobj, "len", "I", len))
                  goto error;
              }
            break;
          }
        case 'b':
          {
            unsigned char *c = va_arg (ap, unsigned char *);

            if (STACK_TYPE (col) != STACK_TYPE_BOOLEAN)
              goto error;
           
            *c = GET_STACK_VALUE (BOOLEAN, col);
            break;
          }
        case 'c':
          {
            char *c = va_arg (ap, char *);

            if (STACK_TYPE (col) != STACK_TYPE_CHAR)
              goto error;
           
            *c = GET_STACK_VALUE (CHAR, col);
            break;
          }
        case 'h':
          {
            short *h = va_arg (ap, short *);

            if (STACK_TYPE (col) != STACK_TYPE_SHORT)
              goto error;
           
            *h = GET_STACK_VALUE (SHORT, col);
            break;
          }
        case 'i':
          {
            int *i = va_arg (ap, int *);
           
            if (STACK_TYPE (col) != STACK_TYPE_INT)
              goto error;

            *i = GET_STACK_VALUE (INT, col);
            break;
          }
        case 'f':
          {
            float *f = va_arg (ap, float *);
           
            if (STACK_TYPE (col) != STACK_TYPE_FLOAT)
              goto error;

            *f = GET_STACK_VALUE (FLOAT, col);
            break;
          }
        case 'd':
          {
            double *d = va_arg (ap, double *);
           
            if (STACK_TYPE (col) != STACK_TYPE_DOUBLE)
              goto error;

            *d = GET_STACK_VALUE (DOUBLE, col);
            break;
          }
        default:
          goto error;
          break;
        }

      idx++;
      p++;
    }

  va_end (ap);

  return 0;

 error:
  va_end (ap);

  return -1;
}

/**
   Field 값을 설정하는 함수입니다.

   @param self  SELF ObjectRef
   @param name  Field 이름
   @param type  Field 타입
 */
int
RvSetField (RvObject *self, const char *name, const char *type, ...)
{
  va_list ap;
  RvValue *val;
  void *ptr;

  va_start (ap, type);

  val = rc_getfieldvalue (self, (u1 *) name, (u1 *) type);
  if (!val)
    goto error;

  switch (type[0])
    {
    case 'B':
      SET_STACK_VALUE (val, BOOLEAN, (unsigned char) va_arg (ap, int));
      break;
    case 'C':
      SET_STACK_VALUE (val, CHAR, (char) va_arg (ap, int));
      break;
    case 'H':
      SET_STACK_VALUE (val, SHORT, (short) va_arg (ap, int));
      break;
    case 'I':
      SET_STACK_VALUE (val, INT, (int) va_arg (ap, int));
      break;
    case 'F':
      SET_STACK_VALUE (val, FLOAT, (float) va_arg (ap, double));
      break;
    case 'D':
      SET_STACK_VALUE (val, DOUBLE, (double) va_arg (ap, double));
      break;
    case 'T':
      ptr = (void *) va_arg (ap, RvObject *);
      SET_STACK_VALUE (val, OBJREF, ptr);
      break;
    case 'S':
      ptr = (void *) va_arg (ap, RvObject *);
      SET_STACK_VALUE (val, STRINGREF, ptr);
      break;
    case '[':
      ptr = (void *) va_arg (ap, RvObject *);
      SET_STACK_VALUE (val, ARRAYREF, ptr);
      break;
    default:
      goto error;
    }

  va_end (ap);
  return 0;

 error:
  va_end (ap);
  return -1;
}

char *
RvArray2Str (RvObject *self, RvObject *array)
{
  RvValue *val;

  if (!self || !array)
    return "(error)";

  if (!OBJECT_IS_ARRAY (array))
    return "(error)";

  val = rc_getfieldvalue (array, "value", "[");
  if (!val)
    return "(error)";

  if (STACK_IS_STRINGVALUE (val))
    {
      char *p = (char *) GET_STACK_VALUE (ADDR, val);
      RvValue *v2;

      v2 = RvGetFieldValue (array, "len", "I");
      if (!v2)
        return "(broken_string)";
      
      return gc_strndup (OBJECTL_ROVM (self), OBJECT_TICKET (self), p, GET_STACK_VALUE (INT, v2));
    }
  else
    return "(can't process)";

  return "(error)";
}

/**
   RvValue 를 문자열로 변환하여 반환합니다.
 */
char *
RvValue2Str (RvObject *self, RvValue *val)
{
  if (!self || !val)
    return NULL;

  switch (STACK_TYPE (val))
    {
    case STACK_TYPE_VOID:
      return "(void)";
    case STACK_TYPE_BOOLEAN:
      return "(boolean)";
    case STACK_TYPE_CHAR:
      return "(char)";
    case STACK_TYPE_SHORT:
      return "(short)";
    case STACK_TYPE_INT:
      return "(int)";
    case STACK_TYPE_FLOAT:
      return "(float)";
    case STACK_TYPE_DOUBLE:
      return "(double)";
    case STACK_TYPE_OBJREF:
      return "(objref)";
    case STACK_TYPE_STRINGREF:
      return "(stringref)";
    case STACK_TYPE_ARRAYREF:
      {
        if (STACK_IS_STRINGVALUE (val))
          {
            char *p = (char *) GET_STACK_VALUE (ADDR, val);
            RvValue *v2;

            if (!OBJECT_IS_STRING (self))
              return "(invalid StringRef)";

            v2 = RvGetFieldValue (self, "len", "I");
            if (!v2)
              return "(broken_string)";
            
            return gc_strndup (OBJECTL_ROVM (self), OBJECT_TICKET (self), p, GET_STACK_VALUE (INT, v2));
          }
        else
          return "(can't process)";
        break;
      }
    default:
      break;
    }

  return "(unknown)";
}

/**
   RvValue 를 정수로 변환하여 반환합니다.
 */
static int
RvValue2Int (RvObject *self, RvValue *val)
{
  if (!self || !val)
    return 0;

  switch (STACK_TYPE (val))
    {
    case STACK_TYPE_BOOLEAN:
    case STACK_TYPE_SHORT:
    case STACK_TYPE_CHAR:
    case STACK_TYPE_INT:
      return GET_STACK_VALUE (INT, val);
    default:
      break;
    }

  return 0;
}

/**
   타입을 FTYPE 으로 가지는 필드 FNAME 을 SELF 에서 찾아 해당 값이 저장되는
   공간 RvValue 구조체를 반환하게 되니다.

   @param self  SELF 구조체.
   @param fname 필드 이름
   @param ftype 필드 타입
   @return      SELF 에서 존재하는 필드를 위한 공간을 반환합니다.
 */
RvValue *
RvGetFieldValue (RvObject *self, const char *fname, const char *ftype)
{
  RvValue *val;

  if (!self || !fname || !ftype)
    return NULL_VALUE;

  val = rc_getfieldvalue (self, fname, ftype);
  if (!val)
    return NULL_VALUE;

  return val;
}

/**
   Field 값을 얻어오는 함수입니다.  Type 에 따라서 ... 가 적용되는 것이
   특징입니다.

   @param self  SELF ObjectRef
   @param name  Field 이름
   @param type  Field 타입
 */
int
RvGetField (RvObject *self, const char *fname, const char *ftype, ...)
{
  va_list ap;
  RvValue * val;

  if (!self || !fname || !ftype)
    return -1;

  val = rc_getfieldvalue (self, fname, ftype);
  if (!val)
    return -1;

  va_start (ap, ftype);

  switch (ftype[0])
    {
    case '[':
      {
        RvObject **o = va_arg (ap, RvObject **);

        *o = GET_STACK_VALUE (ARRAYREF, val);
        break;
      }
    case 'S':
      {
        RvObject **o = va_arg (ap, RvObject **);

        *o = GET_STACK_VALUE (STRINGREF, val);
        break;
      }
    case 'I':
      {
        int *i = va_arg (ap, int *);

        *i = RvValue2Int (self, val);
        break;
      }
    default:
      va_end (ap);
      return -1;
    }

  va_end (ap);
  return 0;
}

/**
   TYPE 인 배열을 생성하여 해당 포인터를 반환합니다.
 */
RvObject *
RvArrayNew (RvObject *self, int len)
{
  if (!self)
    return NULL_OBJECT;

  return NewRvArray (OBJECTL_ROVM (self), OBJECT_TICKET (self), len);
}

/**
   배열 객체 ARRAY 에서 실제 값이 저장되는 공간인 필드 VALUE 의 주소 포인터를
   반환하게 됩니다.

   배열 객체와 문자열 객체의 필드 VALUE 는 둘다 모두 특별하게 취급해줘야하는
   부분이 존재합니다.

   @param array 배열 객체
   @return      필드 VALUE 의 주소 포인터
 */
RvValue *
RvArrayGetEntry (RvObject *array)
{
  RvValue *val;

  if (!array || (array && !OBJECT_IS_ARRAY (array)))
    return NULL_VALUE;

  val = rc_getfieldvalue (array, "value", "[");
  if (!val)
    return NULL_VALUE;

  return (RvValue *) GET_STACK_VALUE (ADDR, val);
}

/**
   값 ENTRY 가 RvValue 구조체로 이루어진 배열이라고 가정을 하고, 해당 IDX 에
   맞는 포인터값을 반환하게 됩니다.

   @param entry RvValue 구조체 포인터
   @param idx   배열의 index 번호
   @return      해당 값 포인터
 */
RvValue *
RvValueEntryItem (RvValue *entry, int idx)
{
  return entry + idx;
}

/**
   배열 객체 ARRAY 를 길이 TOTAL 로 크기를 재조정합니다.  이에 대한 결과값으로
   배열 객체의 필드 LEN 값이 변경되게 됩니다.

   @param array 배열 객체
   @param total 배열의 총 길이
   @return      오류 발생시 -1, 정상시 0 을 반환합니다.
 */
int
RvArrayResize (RvObject *array, int total)
{
  int diff, len = RvArrayLength (array);

  diff = total - len;
  if (diff <= 0)
    return 0;

  if (rc_expandarray (array, diff))
    return -1;

  if (RvSetField (array, "len", "I", total))
    return -1;

  return 0;
}

/**
   배열 ARRAY 에 타입이 TYPE 인 항목을 새롭게 추가할 때 사용됩니다.

   @param array ArraryRef 포인터
   @param type  새롭게 추가되는 항목의 타입 문자열
   @return      성공시 0, 실패시 -1 을 반환합니다.
 */
int
RvArrayPush (RvObject *array, const char *type, ...)
{
  int arrlen;
  RvValue *entry;
  va_list ap;

  if (!array || !type)
    return -1;

  va_start (ap, type);

  if (rc_expandarray (array, 1))
    {
      va_end (ap);
      return -1;
    }

  if (rc_getinfo_array (array, &entry, &arrlen))
    {
      va_end (ap);
      return -1;
    }

  entry = entry + arrlen;

  switch (type[0])
    {
    case 'S':
      {
        RvObject *o = va_arg (ap, RvObject *);

        GET_STACK_VALUE (ADDR, entry) = (void *) o;
        STACK_TYPE (entry) = STACK_TYPE_STRINGREF;
        break;
      }
    default:
      va_end (ap);
      return -1;
    }

  if (RvSetField (array, "len", "I", arrlen + 1))
    return -1;

  return 0;
}

/**
   배열 ARRAY 에서 가장 위에 있는 entry 를 제거합니다.

   @param array         ArrayRef 포인터
   @param poped_value   이 함수 실행으로 poped 되는 RvValue 구조체에 대한 정보를
                        기록해 줍니다.
   @return      성공시 0, 실패시 -1 을 반환합니다.
 */
int
RvArrayPop (RvObject *array, RvValue **poped_value)
{
  int len;

  len = RvArrayLength (array);
  if (len < 0)
    return -1;

  if (poped_value)
    *poped_value = RvValueCopy (array, RvArrayGetItem (array, len - 1));

  if (RvSetField (array, "len", "I", len - 1))
    return -1;  

  return 0;
}

static RvObject *
list_slice (RvObject *a, int ilow, int ihigh)
{
  RvValue *entry, *src, *dest;
  RvObject *np;
  int i, len, arrlen;

  if (rc_getinfo_array (a, &entry, &arrlen))
    return NULL_OBJECT;

  if (ilow < 0)
    ilow = 0;
  else if (ilow > arrlen)
    ilow = arrlen;

  if (ihigh < ilow)
    ihigh = ilow;
  else if (ihigh > arrlen)
    ihigh = arrlen;

  len = ihigh - ilow;
  np = (RvObject *) RvArrayNew (a, len);
  if (np == NULL)
    return NULL_OBJECT;
  
  src = entry + ilow;
  dest = RvArrayGetEntry (np);
  for (i = 0; i < len; i++)
    {
      RvValue v = src[i];
      dest[i] = v;
    }

  return (RvObject *) np;
}

/* a[ilow:ihigh] = v if v != NULL.
 * del a[ilow:ihigh] if v == NULL.
 *
 * Special speed gimmick:  when v is NULL and ihigh - ilow <= 8, it's
 * guaranteed the call cannot fail.
 */
static int
list_ass_slice (RvObject *a, int ilow, int ihigh, RvObject *v)
{
  int n; /* # of elements in replacement list */
  int norig; /* # of elements in list getting replaced */
  int d; /* Change in size */
  int result = -1;	/* guilty until proved innocent */
  int k;
  RvValue *item, *vitem = NULL_VALUE;

#define b       ((RvObject *) v)

  if (v == NULL_OBJECT)
    n = 0;
  else
    {
      if (a == b)
        {
          /* Special case "a[i:j] = a" -- copy b first */
          v = list_slice (b, 0, RvArrayLength (b));
          if (v == NULL_OBJECT)
            return result;

          return list_ass_slice (a, ilow, ihigh, v);
        }

      n = RvArrayLength (v);
      vitem = RvArrayGetEntry (v);
    }

  if (ilow < 0)
    ilow = 0;
  else if (ilow > RvArrayLength (a))
    ilow = RvArrayLength (a);

  if (ihigh < ilow)
    ihigh = ilow;
  else if (ihigh > RvArrayLength (a))
    ihigh = RvArrayLength (a);

  norig = ihigh - ilow;
  d = n - norig;
  if (RvArrayLength (a) + d == 0)
    {
      if (RvSetField (a, "len", "I", 0))
        return -1;
      
      return 0;
    }

  item = RvArrayGetEntry (a);

  if (d < 0) 
    { /* Delete -d items */
      memmove (&item[ihigh+d], &item[ihigh], (RvArrayLength (a) - ihigh) * sizeof(RvValue));
      
      if (RvArrayResize (a, RvArrayLength (a) + d))
        return -1;

      item = RvArrayGetEntry (a);
    }
  else if (d > 0)
    { /* Insert d items */
      k = RvArrayLength (a);
      if (RvArrayResize (a, k + d))
        return -1;

      item = RvArrayGetEntry (a);
      memmove (&item[ihigh+d], &item[ihigh], (k - ihigh) * sizeof(RvValue));
    }

  if (vitem)
    {
      for (k = 0; k < n; k++, ilow++)
        {
          RvValue w = vitem[k];
          item[ilow] = w;
        }
    }
  
#undef b  
  return 0;
}

/**
   배열 객체 ARRAY 에서 인덱스 I 의 값을 POP 하고 그 POP 된 값을 POPED_VALUE
   에 설정하게 됩니다.  만약 배열의 중간 부분이 POP 될 경우, 자동으로 그 공간이
   줄어들게 됩니다.

   @param array 배열 객체
   @param i     POP 할 항목의 인덱스
   @param poped_value POP 된 항목에 들어있던 값
   @return      오류 발생시 -1, 정상시 0 값을 반환합니다.
 */
int
RvArrayPopWithIndex (RvObject *array, int i, RvValue **poped_value)
{
  int arrlen;
  RvValue *entry;

  if (rc_getinfo_array (array, &entry, &arrlen))
    return -1;

  if (i < 0)
    i += arrlen;
  if (i < 0 || i >= arrlen) 
    {
      RvLogError (array, ERRLOG_ERR, ERRLOG_MARK, "pop index out of range");
      return -1;
    }

  if (poped_value)
    *poped_value = RvValueCopy (array, entry + i);

  if (i == arrlen - 1)
    {
      if (RvArrayResize (array, arrlen - 1))
        return -1;

      return 0;
    }

  return list_ass_slice (array, i, i+1, NULL_OBJECT);
}

/**
   문자열이 STR 이고 길이가 LEN 인 StringRef 를 반환한다.
 */
RvObject *
RvStrNew (RvObject *self, char *str, size_t len)
{
  return NewRvStringObject (OBJECTL_ROVM (self), OBJECT_TICKET (self),
                            str, len);
}

/**
   문자열을 잘라서 그에 대한 StringRef 를 반환한다.
 */
RvObject *
RvStrSubstr (RvObject *self, char *str, int offset, size_t len)
{
  return NewRvStringObject (OBJECTL_ROVM (self), OBJECT_TICKET (self),
                            str + offset, len);
}

/**
   Return 값을 TYPE 에 따라 설정합니다.
 */
int
RvSetReturn (RvObject *self, RvValue *ret, const char *type, ...)
{
  va_list ap;
  void *ptr;

  if (!self || !ret || !type)
    return -1;

  va_start (ap, type);

  switch (type[0])
    {
    case 'V':
      SET_STACK_TYPE (ret, STACK_TYPE_VOID);
      break;
    case 'B':
      SET_STACK_VALUE (ret, BOOLEAN, (unsigned char) va_arg (ap, int));
      break;
    case 'C':
      SET_STACK_VALUE (ret, CHAR, (char) va_arg (ap, int));
      break;
    case 'H':
      SET_STACK_VALUE (ret, SHORT, (short) va_arg (ap, int));
      break;
    case 'I':
      SET_STACK_VALUE (ret, INT, (int) va_arg (ap, int));
      break;
    case 'F':
      SET_STACK_VALUE (ret, FLOAT, (float) va_arg (ap, double));
      break;
    case 'D':
      SET_STACK_VALUE (ret, DOUBLE, (double) va_arg (ap, double));
      break;
    case 'T':
      ptr = (void *) va_arg (ap, RvObject *);
      if (!ptr)
        goto error;
      SET_STACK_VALUE (ret, OBJREF, ptr);
      break;
    case 'S':
      ptr = (void *) va_arg (ap, RvObject *);
      if (!ptr)
        goto error;
      SET_STACK_VALUE (ret, STRINGREF, ptr);
      break;
    case '[':
      ptr = (void *) va_arg (ap, RvObject *);
      if (!ptr)
        goto error;
      SET_STACK_VALUE (ret, ARRAYREF, ptr);
      break;
    default:
      break;
    }

  va_end (ap);
  return 0;

 error:
  va_end (ap);
  return -1;
}

/**
   문자열 객체 STR 의 길이를 계산하여 반환합니다.

   @param str   문자열 객체
   @return      문자열의 길이
 */
int
RvStrSize (RvObject *str)
{
  int len;

  if (RvGetField (str, "len", "I", &len))
    return -1;
  
  return len;
}

/**
   StringRef 에서 문자열과 길이를 각각 PTR 과 LEN 에 담아 반환합니다.

   @param str   StringRef 포인터
   @param ptr   문자열을 저장할 문자열 포인터
   @param len   문자열의 길이를 저장할 int 포인터
   @return      성공시 0, 실패시 -1 을 반환합니다.
 */
int
RvStrGetInfo (RvObject *str, char **ptr, int *len)
{
  RvValue *value;

  /* StringRef 와 ArrayRef 의 value 는 특별하게 취급해 줘야 한다.  */
  value = RvGetFieldValue (str, "value", "[");
  if (!value)
    return -1;
  *ptr = RvValue2Str (str, value);

  if (RvGetField (str, "len", "I", len))
    return -1;

  return 0;
}

/**
   배열 ARRAY 의 현재 길이를 계산하여 반환합니다.

   @param array ArrayRef 포인터
   @return      현재 배열의 크기
*/
int
RvArrayLength (RvObject *array)
{
  int len;

  if (RvGetField (array, "len", "I", &len))
    return -1;
  
  return len;
}

/**
   배열 객체 ARRAY 의 index IDX 에 존재하는 값 구조체를 반환합니다.

   @param array 배열 객체
   @param idx   찾을 index 번호
   @return      찾은 값 구조체를 반환
 */
RvValue *
RvArrayGetItem (RvObject *array, size_t idx)
{
  int len;
  RvValue *value, *entry;

  len = RvArrayLength (array);
  if (idx >= len)
    return NULL_VALUE;

  /* StringRef 와 ArrayRef 의 value 는 특별하게 취급해 줘야 한다.  */
  value = RvGetFieldValue (array, "value", "[");
  if (!value)
    return NULL_VALUE;

  if (!STACK_IS_ARRAYVALUE (value))
    return NULL_VALUE;

  entry = (RvValue *) GET_STACK_VALUE (ADDR, value);

  return &entry[idx];
}

/**
   배열 객체 ARRAY 에서 가장 끝에 존재하는 항목에 대한 값을 반환합니다.

   @param array 배열 객체
   @return      가장 끝에 존재하는 값 공간
 */
RvValue *
RvArrayLastValue (RvObject *array)
{
  int len;

  len = RvArrayLength (array);
  if (len <= 0)
    return NULL_VALUE;

  return RvArrayGetItem (array, len - 1);
}

/**
   배열 객체 ARRAY 의 index IDX 에 값 VAL 를 설정하는 함수입니다.

   @param array 배열 객체
   @param idx   값을 갱신할 index 번호
   @param val   설정할 값
   @return      오류 발생시 -1, 정상시 0
 */
int
RvArraySetItem (RvObject *array, size_t idx, RvValue *val)
{
  RvValue *entry;

  entry = RvArrayGetItem (array, idx);
  if (!entry)
    return -1;

  *entry = *val;

  return 0;
}

/* Reverse a slice of a list in place, from lo up to (exclusive) hi. */
static void
reverse_slice (RvValue *lo, RvValue *hi)
{
  assert (lo && hi);

  --hi;
  while (lo < hi)
    {
      RvValue t = *lo;
      *lo = *hi;
      *hi = t;
      ++lo;
      --hi;
    }
}

/**
   배열 객체 ARRAY 의 순서를 반대로 만듭니다.

   @param array 배열 객체
   @return      오류 발생시 -1, 정상시 0 을 반환
 */
int
RvArrayReverse (RvObject *array)
{
  int arrlen;
  RvValue *entry;

  if (rc_getinfo_array (array, &entry, &arrlen))
    return -1;

  reverse_slice (entry, entry + arrlen);
  
  return 0;
}

/**
   배열 객체 ARRAY 의 index WHERE 에 값 FMT 을 넣게 됩니다.  FMT 을 바로
   넣는 것이 아니라 ... 에 맞게 해석한 뒤 알맞게 들어가게 됩니다.

   @param array 배열 객체
   @param where 어디에 넣을 것인가를 결정하는 index 번호
   @param fmt   문자열 포맷
   @return      만약 중간에 오류가 발견되었을 경우 -1, 그렇지 않을 경우
                0 을 반환하게 됩니다.
 */
int
RvArrayInsert (RvObject *array, int where, const char *fmt, ...)
{
  int i, n, unused;
  void *ptr;
  va_list ap;
  RvValue *entry, *e;

  n = RvArrayLength (array);
  if (n == INT_MAX)
    {
      RvLogError (array, ERRLOG_ERR, ERRLOG_MARK, "cannot add more objects to list");
      return -1;
    }

  if (RvArrayResize (array, n + 1))
    return -1;

  /* RvArrayResize () 함수가 실행되면 내부 value 변수의 주소값이 달리지므로
     주의해야 한다.  */
  if (rc_getinfo_array (array, &entry, &unused))
    return -1;

  if (where < 0)
    {
      where += n;
      if (where < 0)
        where = 0;
    }
  if (where > n)
    where = n;
  
  for (i = n; --i >= where; )
    entry[i+1] = entry[i];

  va_start (ap, fmt);

  e = &(entry[where]);
  switch (*fmt)
    {
    case 'V':
      SET_STACK_TYPE (e, STACK_TYPE_VOID);
      break;
    case 'B':
      SET_STACK_VALUE (e, BOOLEAN, (unsigned char) va_arg (ap, int));
      break;
    case 'C':
      SET_STACK_VALUE (e, CHAR, (char) va_arg (ap, int));
      break;
    case 'H':
      SET_STACK_VALUE (e, SHORT, (short) va_arg (ap, int));
      break;
    case 'I':
      SET_STACK_VALUE (e, INT, (int) va_arg (ap, int));
      break;
    case 'F':
      SET_STACK_VALUE (e, FLOAT, (float) va_arg (ap, double));
      break;
    case 'D':
      SET_STACK_VALUE (e, DOUBLE, (double) va_arg (ap, double));
      break;
    case 'T':
      ptr = (void *) va_arg (ap, RvObject *);
      if (!ptr)
        goto error;
      SET_STACK_VALUE (e, OBJREF, ptr);
      break;
    case 'S':
      ptr = (void *) va_arg (ap, RvObject *);
      if (!ptr)
        goto error;
      SET_STACK_VALUE (e, STRINGREF, ptr);
      break;
    case '[':
      ptr = (void *) va_arg (ap, RvObject *);
      if (!ptr)
        goto error;
      SET_STACK_VALUE (e, ARRAYREF, ptr);
      break;
    default:
      goto error;
    }

  va_end (ap);
  return 0;
 error:
  va_end (ap);
  return -1;
}

/**
   값 VALUE 가 StringRef 인지를 반환합니다.

   @param value 값을 담는 구조체
   @return      만약 true 일 경우 1 을, 그렇지 않다면 0 을 반환합니다.
 */
int
RvValueIsStringRef (RvValue *value)
{
  if (STACK_TYPE (value) == STACK_TYPE_STRINGREF)
    return TRUE;

  return FALSE;
}

/**
   값 VALUE 가 ArrayRef 일 경우 TRUE 를 반환하고 그렇지 않을 경우 FALSE 를
   반환합니다.
 */
int
RvValueIsArrayRef (RvValue *value)
{
  if (STACK_TYPE (value) == STACK_TYPE_ARRAYREF)
    return TRUE;

  return FALSE;
}

/**
   값 VALUE 에서 StringRef 를 추출하여 반환합니다.

   @return      만약 VALUE 가 StringRef 일 경우, 그에 해당하는 포인터를 반환하고
                그렇지 않다면 NULL_OBJECT 를 반환합니다.
 */
RvObject *
RvValueStringRef (RvValue *value)
{
  if (!RvValueIsStringRef (value))
    return NULL_OBJECT;

  return GET_STACK_V_STRINGREF (value);
}

/**
   값 VALUE 가 ArrayRef 일 경우, 값에 저장되어 있는 ArrayRef 포인터를 
   반환합니다.

   @param value 값을 담는 공간인 RvValue 구조체
   @return      만약 ArrayRef 일 경우, 해당 ArrayRef 포인터를, 그렇지
                않을 경우 NULL_OBJECT 가 반환됩니다.
 */
RvObject *
RvValueArrayRef (RvValue *value)
{
  if (!RvValueIsArrayRef (value))
    return NULL_OBJECT;

  return GET_STACK_V_ARRAYREF (value);
}

/**
   문자열 객체 STR 에서 실제 값이 저장되어 있는 공간인 value 의 실제 포인터
   값을 반환합니다.

   @param str   문자열 객체
   @return      실제 char * 포인터.
 */
char *
RvStrGetPointer (RvObject *str)
{
  RvValue *value;

  /* StringRef 와 ArrayRef 의 value 는 특별하게 취급해 줘야 한다.  */
  value = RvGetFieldValue (str, "value", "[");
  if (!value)
    return NULL;
  
  if (!STACK_IS_STRINGVALUE (value))
    return NULL;

  return (char *) GET_STACK_VALUE (ADDR, value);
}

/**
   GC 메모리 공간을 할당할 때 사용되는 함수입니다.  아래에서 할당되는 메모리는
   자동으로 Delayed Stack Slot 에 등록되게 됩니다.

   @param self  ROVM 구조체와 TICKET 구조체의 포인터를 얻기 위해 사용되는
                객체
   @param size  할당할 메모리 크기
   @return      할당된 메모리 포인터
 */
void *
RvMemMalloc (RvObject *self, size_t size)
{
  return gc_alloc (OBJECTL_ROVM (self), OBJECT_TICKET (self), size);
}

/**
   값 VALUE 의 복사본을 만들어 그 값을 반환하게 됩니다.  이 함수가 존재하는 이유는
   확장 모듈 작성시 GC 메모리 포인터를 스택상에 보관하고 있을 그 순간에 GC 가 
   실행될 경우, 해당 객체가 증발해 버리는 사고를 방지하기 위해서 존재합니다.

   아래의 RvMemMalloc () 에서 할당되는 메모리는 Delayed Stack Slot 에 자동으로
   등록되게 됩니다.

   @param self  변수 참조를 위해 사용되는 Object 객체
   @param value 복사할 값
   @return      복사된 값
 */
RvValue *
RvValueCopy (RvObject *self, RvValue *value)
{
  RvValue *new;

  new = (RvValue *) RvMemMalloc (self, sizeof (RvValue));
  if (!new)
    return NULL_VALUE;

  *new = *value;

  return new;
}

extern const char *rovm_stack_name[MAX_STACK_TYPE];

/**
   VAL 의 타입 이름을 반환합니다.

   @param val   알고자 하는 값
   @return      해당 타입의 이름
 */
const char *
RvGetTypeName (RvValue *val)
{
  return rovm_stack_name[STACK_TYPE (val)];
}

/**
   값을 담는 공간인 RET 에 값 VALUE 를 복사하는데 사용되는 함수로써 반환값을
   RvValue 그대로 복사하고 싶을 때 사용됩니다.

   @param ret   확장 모듈 메쏘드 호출시 3 번째 인자가 되는 값입니다.
   @param value 설정할 값
   @return      오류 발생시 -1, 그렇지 않을 경우 0 을 반환
 */
int
RvSetReturnValue (RvValue *ret, RvValue *value)
{
  if (!ret || !value)
    return -1;

  *ret = *value;

  return 0;
}

/**
   값을 담는 공간인 O1 과 O2 를 비교하여 만약 같다면 1 을 반환하고 그렇지
   않다면 0 을 반환합니다.  만약 중간에 오류가 발생하였다면 -1 을 반환하게
   됩니다.  만약 배열 비교일 경우, 내부 항목들을 재귀 호출로써 비교를 하게
   되며, 문자열 객체일 경우, 문자열의 길이 및 내용을 비교하게 됩니다. 
   만약 ObjectRef 일 경우는 주소값을 기준으로 비교를 하게 됩니다.  그 외의
   값은 실제 해당 값을 비교함으로써 이루어지게 됩니다.

   @param o1    비교할 대상값 1 번
   @param o2    비교할 대상값 2 번
   @return      만약 같다면 1 을 반환하고 그렇지 않다면 0 을 반환합니다.
                만약 중간에 오류가 발생하였다면 -1 을 반환
 */
int
RvValueRichCompare (RvValue *o1, RvValue *o2)
{
  if (!o1 || !o2)
    return -1;

  if (STACK_TYPE (o1) != STACK_TYPE (o2))
    return FALSE;

#define RICHCOMPARECASE(TYPE)                 \
  case STACK_TYPE_##TYPE:                       \
    {                                           \
      if (GET_STACK_VALUE (TYPE, o1) != GET_STACK_VALUE (TYPE, o2))     \
        return FALSE;                                                   \
      break;                                                            \
    }
  
  switch (STACK_TYPE (o1))
    {
      RICHCOMPARECASE (BOOLEAN);
      RICHCOMPARECASE (CHAR);
      RICHCOMPARECASE (SHORT);
      RICHCOMPARECASE (INT);
      RICHCOMPARECASE (FLOAT);
      RICHCOMPARECASE (DOUBLE);
      RICHCOMPARECASE (OBJREF);
    case STACK_TYPE_STRINGREF:
      {
        int str1len, str2len;
        char *str1, *str2;
        RvObject *s1, *s2;

        s1 = GET_STACK_VALUE (STRINGREF, o1);
        s2 = GET_STACK_VALUE (STRINGREF, o2);
        if (!s1 || !s2)
          return -1;
        if (RvStrGetInfo (s1, &str1, &str1len))
          return -1;
        if (RvStrGetInfo (s2, &str2, &str2len))
          return -1;

        if (str1len != str2len)
          return FALSE;
        if (memcmp (str1, str2, str1len))
          return FALSE;
        break;
      }
    case STACK_TYPE_ARRAYREF:
      {
        int i, e1len, e2len;
        RvValue *e1, *e2;

        e1len = RvArrayLength (GET_STACK_VALUE (ARRAYREF, o1));
        e2len = RvArrayLength (GET_STACK_VALUE (ARRAYREF, o2));
        if (e1len != e2len)
          return FALSE;

        e1 = RvArrayGetEntry (GET_STACK_VALUE (ARRAYREF, o1));
        e2 = RvArrayGetEntry (GET_STACK_VALUE (ARRAYREF, o2));
        if (!e1 || !e2)
          return -1;

        for (i=0; i<e1len; i++)
          {
            int ret = RvValueRichCompare (e1 + i, e2 + i);
            if (ret == FALSE)
              return FALSE;
            else if (ret == -1)
              return -1;
          }
        break;
      }
    default:
      return -1;
    }

  return TRUE;
}

/**
   배열 객체 ARRAY 에서 LO 와 HI 값을 처리할 때 사용됩니다.  만약 V 가 NULL
   일 경우는 del ARRAY[LO:HI] 와 같은 의미가 되며, 그렇지 않을 경우 
   ARRAY[LO:HI] = v 의 의미가 됩니다.

   파이썬의 코드가 매우 많이 참조되었습니다. ^^ 같은 기능을 다시 머리로 생각해서
   만드는 것을 세상에서 가장 싫어합니다.

   @param array 배열 객체
   @param lo    배열에서의 lowest index
   @param hi    배열에서의 highest index
   @param v     삽입할 경우, 그에 해당하는 값
   @return      만약 실행 중 오류가 발생했었다면 -1 을 그렇지 않다면 0 을 
                반환합니다.
 */
int
RvArraySliceAdvanced (RvObject *array, int lo, int hi, RvObject *v)
{
  return list_ass_slice (array, lo, hi, v);
}

/**
   Array 관련 함수로써 SELF 와 B 는 모두 ArrayRef 객체입니다.  이 함수는
   B 에 들어 있는 항목들을 모두 SELF 에 추가하는 일을 합니다.

   @param self  배열 객체 1 번
   @param b     배열 객체 2 번
   @return      배열 객체 2 번이 배열 객체 1 번에 모두 삽입된 결과값을
                만드는데, 과정에서 오류가 발생하였을 경우 -1 을 그렇지
                않을 경우 0 을 반환합니다.
 */
int
RvArrayExtend (self, b)
     RvObject *self, *b;
{
  int i, m, n, mn;
  RvValue *src, *dest;

  if (self == b)
    {
      n = RvArrayLength (b);
      if (n == 0)
        return 0;
      m = RvArrayLength (self);

      if (RvArrayResize (self, m + n))
        return -1;

      src = RvArrayGetEntry (b);
      dest = RvArrayGetEntry (self) + m;
      for (i = 0; i < n; i++) 
        {
          RvValue o = src[i];
          dest[i] = o;
        }
      return 0;
    }

  src = RvArrayGetEntry (b);
  dest = RvArrayGetEntry (self);

  n = RvArrayLength (b);
  if (n < 0)
    return -1;

  m = RvArrayLength (self);
  mn = m + n;
  if (mn >= m)
    {
      /* Make room. */
      if (RvArrayResize (self, mn))
        return -1;
    }

  for (i = 0; i < n; i++)
    {
      if (RvArraySetItem (self, m + i, src + i))
        return -1;
    }

  return 0;
}

/**
   ROVM Request 에서 발생하는 모든 메세지를 기록하기 위해서 사용하는 logger 함수.

   @param self	SELF 객체
   @param level 현재 로그의 위험도 등급
   @param file	메세지가 발생한 파일 이름
   @param line	메세지가 발생한 파일의 라인 번호
   @param fmt	vprintf 관련 포맷
 */
int
RvLogError (RvObject *self,
            int level,
            const char *file, 
            int line, 
            const char *fmt, ...)
{
  va_list args;

  va_start (args, fmt);
  if (request_add_error_core (OBJECT_REQUEST (self), level, file, line, fmt, args))
    {
      va_end (args);
      return -1;
    }

  va_end (args);
  return 0;
}

RvObject *
RvObjectNew (RvObject *self, 
             int iptype,
             char *hostname, 
             unsigned short port,
             char *path)
{
  RvClass *cls;
  RvObject *obj;

  cls = rc_getclassbyhost (OBJECT_REQUEST (self), iptype, hostname, port, path);
  if (cls == NULL_CLASS)
    {
      request_add_error (OBJECT_REQUEST (self), ERRLOG_ERR, ERRLOG_MARK, "Can't find class.");
      return NULL_OBJECT;
    }
  else if (cls == REMOTE_CLASS)
    {
      obj = NewRemoteRvObject (OBJECT_REQUEST (self), iptype, hostname, port, path);
      if (!obj)
        {
          request_add_error (OBJECT_REQUEST (self), ERRLOG_ERR, ERRLOG_MARK,
                             "NewRemoteRvObject is under construction.");
          return NULL_OBJECT;
        }
    }
  else
    obj = NewRvObject (OBJECTL_ROVM (self), OBJECT_TICKET (self), cls);
  
  return obj;
}
