#pragma hdrfile "jsdb.csm"
#include "rslib.h"
#include "jsdb.h"
#include "rs/wrap_jsdb.h"
#pragma hdrstop
#include "js/jsobj.h"

void Record_JSFinalize(JSContext *cx, JSObject *obj)
{
 JSPointer<TParameterList> * t =
   (JSPointer<TParameterList>*)JS_GetPrivate(cx,obj);
 if (t) delete t;

 JS_SetPrivate(cx,obj,NULL);
}

JSBool
Record_Record(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
    jsval *rval)
{
 GETENV;
 if (!Env) return JS_FALSE;

 TParameterList * dt = NULL;
 JSBool b;

 try {
 if (argc == 1 && ISSTR(0))
  dt =  new TParameterList(UTF8STR(0),',');
 else if (argc == 2 && ISSTR(0) && ISSTR(1))
  dt =  new TParameterList(UTF8STR(0),UTF8STR(1)[0]);
 else if (argc == 1 && ISBOOL(0))
  dt = new TParameterList(b);
 else if (argc == 1 && JSVAL_IS_OBJECT(argv[0]))
 {
  dt = new TParameterList;
  JSObject *o = JSVAL_TO_OBJECT(argv[0]);
  JSIdArray * props = JS_Enumerate(cx,o);
  if (props)
  {
   jsint index;
   jsint max = props->length;
   jsval np;
   jsval vp;
   for (index=0 ; index< max ; index++)
   {
     jsid id = props->vector[index];
     if (!JS_IdToValue(cx, id, &np))
       continue;
     JSString * name = JS_ValueToString(cx,np);

     TStr n( name ? JS_GetStringChars(name) : (jschar*)0,
             name ? JS_GetStringLength(name) : 0);

     if (OBJ_GET_PROPERTY(cx,o,id,&vp))
     {
      JSString * value = JS_ValueToString(cx,vp);
      TStr v( (value) ? JS_GetStringChars(value) : (jschar*)0,
             (value) ? JS_GetStringLength(value) : 0);
      dt->Set(n,v);
     }
     else
      dt->Set(n);
   }
   JS_DestroyIdArray(cx,props);
  }
 }
 else if (argc == 0)
  dt = new TParameterList;
 } catch (...) {dt = NULL;}

 if (!dt)
 {
  if (Env->errorOnFailure) return JS_FALSE;
  ERR_MSG(Record,"Out of memory","");
 }

 if (dt)
 SETPRIVATE(obj,TParameterList,dt,true,NULL);
// JS_SetPrivate(cx,obj,dt);

 return JS_TRUE;
}
/*
JSBool Record_JSEnumerate(JSContext *cx, JSObject *obj,
                                     JSIterateOp enum_op,
                                     jsval *statep, jsid *idp)
{
 int32 * x, u;
 GETOBJ(TParameterList,t);

 switch (enum_op)
 {
  case JSENUMERATE_INIT:
    x = new int32;
    //allocate a new integer, because PRIVATE_TO_JSVAL drops the last bit
    *x = 0;
    *statep = PRIVATE_TO_JSVAL(x);
    if (idp) *idp = INT_TO_JSVAL(t->Count());
    break;
  case JSENUMERATE_NEXT:
    x = (int32*)JSVAL_TO_PRIVATE(*statep);
    u = *x;
    if (u < t->Count())
    {
     if (idp) *idp = INT_TO_JSVAL(u);
     *x = u + 1;
     break;
    }
    //else done -- cleanup.
  case JSENUMERATE_DESTROY:
    x = (int32*)JSVAL_TO_PRIVATE(*statep);
    delete x;
    *statep = JSVAL_NULL;
 };
 return JS_TRUE;
}
*/

JSBool
Record_JSGet(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
{
 GETOBJ(TParameterList,t);

 int x = JSVAL_TO_INT(id);

 if (JSVAL_IS_INT(id))
  switch (x)
  {
   case 0: RETBOOL(t->CaseSensitive);
   case 1: RETINT(t->Count());
   case 2: RETSTR("Record");
   default: return JS_FALSE;
  }
 else return JS_FALSE;
}

JSBool
Record_JSSet(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
 GETOBJ(TParameterList,t);
 JSBool b;
 int x = JSVAL_TO_INT(id);
 if (JSVAL_IS_INT(id))
  switch (x)
  {
   case 0: if (!JS_ValueToBoolean(cx,*vp,&b)) return JS_FALSE;
           t->CaseSensitive = b;
           return JS_TRUE;
   default: return JS_FALSE;
  }
 else return JS_FALSE;
}

V_WRAP_V(TParameterList,Clear)
I_WRAP_SS(TParameterList,ReadINIFileSection)
V_WRAP_SS(TParameterList,WriteINIFileSection)
B_WRAP_S(TParameterList,Has)
V_WRAP_S(TParameterList,Unset)
S_WRAP_I(TParameterList,Name)
S_WRAP_I(TParameterList,Value)
WRAP_HELP(Record,
 "append(Record)\nclear()\nread(text,delim)\nwrite(text,delim)\n"
 "readINI(file,section)\nwriteINI(file,section)\nset(text,value)\n"
 "unSet(text)\nhas(text)\nname(int)\nvalue(int)\ncount()\n"
 "help()\n")


static JSBool
TParameterList_Read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 const char * s0 = NULL, *s1 = NULL;
 JSString* j0, *j1;
 if (argc == 0 || argc > 2) ERR_COUNT(Record,Read);

 GETOBJ(TParameterList,t);

 GETSTRING(0);
 if (argc == 2) GETSTRING(1);

 RETINT(t->Read(s0, s1?s1[0] : '&'));
}


static JSBool
TParameterList_Write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 const char * s0 = NULL;
 JSString* j0;
 if (argc > 1) ERR_COUNT(Record,Read);

 GETOBJ(TParameterList,t);

 j0 = JS_ValueToString(cx,argv[0]);
  TStr r;
 if (j0)
 {
  TStr s0(JS_GetStringChars(j0));
  t->Write(r,s0);
 }
 else
  t->Write(r,"&");

 RETSTRW(WStr(r));
}


static JSBool
TParameterList_Get(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(TParameterList,t);

 if (argc == 0 || argc > 2) ERR_COUNT(Record,Get);

 GETUTF8(0);

 if (argc == 2)
 {
  GETUTF8(1);
  RETSTRW(WStr(t->Get(s0,s1)));
 }
 else
 RETSTRW(WStr(t->Get(s0)));
}



static JSBool
TParameterList_GetI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(TParameterList,t);

 if (argc == 0 || argc > 2) ERR_COUNT(Record,Get);

 GETUTF8(0);
 int32 def = 0;
 if (argc == 2 && JS_ValueToInt32(cx, argv[1], &def))
 {
  RETINT(t->GetInt(s0,def));
 }
 else
 RETINT(t->GetInt(s0));
}

static JSBool
TParameterList_GetN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(TParameterList,t);

 if (argc == 0 || argc > 2) ERR_COUNT(Record,Get);

 GETUTF8(0);
 double def = 0;
 if (argc == 2 && JS_ValueToNumber(cx, argv[1], &def))
 {
  RETINT(t->GetDouble(s0,def));
 }
 else
 RETINT(t->GetDouble(s0));
}

#ifndef XP_WIN
#define GetGValue(rgb)   ((uint8) (((uint16) (rgb)) >> 8))
#define GetRValue(rgb)   ((uint8) (rgb))
#define GetBValue(rgb)   ((uint8) ((rgb) >> 16))
#endif

static JSBool
TParameterList_GetColor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(TParameterList,t);
 if (argc == 0 || argc > 1) ERR_COUNT(Record,GetColor);

 GETUTF8(0);
 int32 c = t->GetInt(s0); // COLORREFINT(0);
 jsval arr[3];

 arr[0] = INT_TO_JSVAL(GetRValue(c));
 arr[1] = INT_TO_JSVAL(GetGValue(c));
 arr[2] = INT_TO_JSVAL(GetBValue(c));

 JSObject * ret = JS_NewArrayObject(cx,3,arr);
  RETOBJ(ret);
}

static JSBool
TParameterList_Set(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(TParameterList,t);

 if (argc != 2) ERR_COUNT(Record,Set);

 if (ISINT(0))
 {
  TNameValuePair*x = (*t)[INT(0)];
  GETUTF8(1);
  if (x)
   x->Value =  s1;
  RETBOOL(true);
 }
 else
 {
  GETUTF8(0);
  GETUTF8(1);
  t->Set(s0,s1);
  RETBOOL(true);
 }
}

static JSBool
TParameterList_ToObject(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 ENTERNATIVE(cx);
 GETOBJ(TParameterList,t);
 JSObject * o = ROOT(JS_NewObject(cx, NULL, NULL, NULL));
 FOREACH(TNameValuePair*p, *t)
  WStr n(p->Name);
  WStr v(p->Value);
  jsval ptr = STRING_TO_JSVAL(
               JS_NewUCStringCopyN(cx,(uint16*)v,v.bytes()/sizeof(uint16))
               );
  JS_SetUCProperty(cx, o,(uint16*)n,n.bytes()/sizeof(uint16),&ptr);
 DONEFOREACH

 RETOBJ(o);
}

static JSBool
TParameterList_ToString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(TParameterList,t);
 TStr out;
 t->Write(out,"\n");
 RETSTR(out);
}


static JSBool
TParameterList_Append(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

static JSPropertySpec Record_properties[] = {
    {"caseSensitive",      0,   JSPROP_ENUMERATE|JSPROP_PERMANENT,Record_JSGet,Record_JSSet},
    {"count",      1,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Record_JSGet},
    {"length",      1,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Record_JSGet},
    {"className",2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Record_JSGet},
        {0}
};

static JSFunctionSpec Record_functions[] = {
    {"append",     TParameterList_Append,     1},
    {"clear",     TParameterList_Clear,       0},
    {"read",     TParameterList_Read,          1},
    {"write",    TParameterList_Write,         1},
    {"readINI",    TParameterList_ReadINIFileSection,         2},
    {"writeINI",    TParameterList_WriteINIFileSection,          2},
    {"set",  TParameterList_Set,    2},
    {"unSet",  TParameterList_Unset,    1},
    {"unset",  TParameterList_Unset,    1},
    {"get",  TParameterList_Get,    2},
    {"getN",  TParameterList_GetN,    2},
    {"getI",  TParameterList_GetI,    2},
    {"getColor",  TParameterList_GetColor,    1},
    {"has",  TParameterList_Has,    1},
    {"name",  TParameterList_Name,    1},
    {"value",  TParameterList_Value,    1},
    {"toString", TParameterList_ToString,0},
    {"toObject", TParameterList_ToObject,0},
    {0}
};

static JSFunctionSpec Record_fnstatic[] = {
    {"help",  Record_HELP,    0},
    {0}
};

static JSBool
TParameterList_Append(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETENV;
 GETOBJ(TParameterList,t);

 for (uintN i = 0; i < argc; i++)
 {
  if (!JSVAL_IS_OBJECT(argv[i]))
    ERR_TYPE(Record,Append,i,Record);

  JSObject* j0 = JSVAL_TO_OBJECT(argv[i]);
  if (!j0) return false;

  TParameterList* a;
  GETREC(0,a);

  if (a)
   {
    t->Append(*a);
   }
  else
   {
    jsval last;
    int32 max;
    jsval n,v;
    TChars c(256);

    if (!JS_GetProperty(cx,j0,"count",&last)) goto error;

    if (JSVAL_IS_VOID(last) || JSVAL_IS_NULL(last)) goto error;

    if (!JS_ValueToInt32(cx,last,&max)) goto error;

    for (int32 i=0; i<max; i++)
        {
         JSErrorReporter older = JS_SetErrorReporter(cx,NULL);

         sprintf(c,"name(%d)",i);
         if (!JS_EvaluateScript(cx,j0,c,strlen(c),0,0,&n))
          goto error;

         sprintf(c,"value(%d)",i);
         if (!JS_EvaluateScript(cx,j0,c,strlen(c),0,0,&v))
          goto error;

         JS_SetErrorReporter(cx,older);

         JSString *s0, *s1;
         s0 = JS_ValueToString(cx,n);
         s1 = JS_ValueToString(cx,v);

         if (!s1 || !s0) goto error;

         t->Set(JS_GetStringBytes(s0),JS_GetStringBytes(s1));
        }
   }
 }
 RETBOOL(true);

 error:
 JS_ReportError(cx,"Arguments to record.append() must have a "
 "count property and name() and value() functions.");
 return JS_FALSE;
}

JSObject*
Record_Object(JSContext *cx, TParameterList* t,bool autodelete,JSPointerBase* Parent)
{
 JSObject* obj;
 GETENV;
 MAKENEW(Record);
 //JSPointer<TParameterList> * p =new JSPointer<TParameterList>(t,Parent == NULL);
 //JS_SetPrivate(cx,obj,p);
 SETPRIVATE(obj,TParameterList,t,autodelete,Parent);
 return obj;
}

void
Record_InitClass(JSContext *cx, JSObject *obj)
{
 GETENV;
 INITCLASS(Record);
}


static JSClass Record_class = {
    "Record", JSCLASS_HAS_PRIVATE ,         //Table_JSGet
    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub,   JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,Record_JSFinalize
};

JSClass* Record_Class() {return &Record_class;}
