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

#include "rs/tbl_sql.h"

I_WRAP_S(DataTable,FindColumn)
//S_WRAP_V(DataTable,GetLastError)
S_WRAP_I(DataTable,ColumnTitle)
B_WRAP_IS(DataTable,SetTitle)
I_WRAP_I(DataTable,GetColWidth)
I_WRAP_S(DataTable,AddCol)
B_WRAP_I(DataTable,DelCol)

static JSBool
Table_GetRow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc == 1)
  {
   int32 v0;
   if (!TOINT(0,v0)) ERR_TYPE(Table,getRow,1,integer);
   GETOBJ(DataTable,t);
   TParameterList * r1 = new TParameterList;
   if (!t->GetRow(v0,*r1))
   {
    delete r1;
    ERR_MSG(Table,"Row not available",itos(v0));
   }
   RETOBJ(Record_Object(cx,r1,true,NULL));
  }
 else if (argc == 2)
  {
   int32 v0;
   TParameterList * r1;
   if (!TOINT(0,v0)) ERR_TYPE(Table,getRow,1,integer);
   GETREC(1,r1); if (!r1) ERR_TYPE(Table,getRow,2,record);
   GETOBJ(DataTable,t);
   if (!t->GetRow(v0,*r1))
   {
    ERR_MSG(Table,"Row not available",itos(v0));
   }
  }
 else
  ERR_COUNT(Table,getRow);
 RETOBJ(NULL);
}

B_WRAP_IR(DataTable,SetRow)
I_WRAP_R(DataTable,AddRow)

static JSBool
Table_FindRow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 int32 v1 = 0, v2=1;
 TParameterList * r0;

 if (argc == 0 || argc > 3) ERR_COUNT(Table,FindRow)

 GETREC(0,r0);
 if (!r0) ERR_TYPE(Table,FindRow,1,record);

 if (argc == 2)
 if (!TOINT(1,v1)) ERR_TYPE(Table,FindRow,2,integer);

 if (argc == 3)
 {
  if (!TOINT(2,v2)) ERR_TYPE(Table,FindRow,3,integer);
 }

 if (v2 != 1 && v2 != -1) v2 = 1;

 GETOBJ(DataTable,t);
 int32 row = t->FindRow(*r0,v1,v2);
 if (row)
 {
  *rval = INT_TO_JSVAL(v2);
  JS_SetProperty(cx, obj,"searchDirection",rval);
 }

 *rval = INT_TO_JSVAL(row);

 JS_SetProperty(cx, obj,"lastFind",rval);

 RETINT(row);
}

static JSBool
Table_FindNext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc != 1) ERR_COUNT(Table,FindNext);

 TParameterList* r0;
 GETREC(0,r0);
 if (!r0) ERR_TYPE(Table,FindNext,1,record);

 if (!JS_GetProperty(cx,obj,"lastFind",rval))
   ERR_MSG(Table,FindNext,"you must call find() before findNext");

 int32 v1 = JSVAL_TO_INT(*rval);
 if (!v1) RETBOOL(false);
 v1++;

 if (!JS_GetProperty(cx,obj,"searchDirection",rval))
   ERR_MSG(Table,FindNext,"you must call find() before findNext");
 int32 v2 = JSVAL_TO_INT(*rval);

 if (v2 != 1 && v2 != -1) v2 = 1;

 GETOBJ(DataTable,t);

 int32 row = t->FindRow(*r0,v1,v2);
 if (row)
 {
  *rval = INT_TO_JSVAL(v2);
  JS_SetProperty(cx, obj,"searchDirection",rval);
 }

 *rval = INT_TO_JSVAL(row);

 JS_SetProperty(cx, obj,"lastFind",rval);

 RETINT(row);
}

WRAP_HELP(DataTable,
 "data(row,col/name) or get(row,col/name)\n"
 "set(row,col/name,text)\nsetN(row,col/name,number)\n"
 "save() or save(filename) memory and ASCII tables may be saved with a new name.\n"
 "column(name)\n"
 "title(col)\n"
 "type(index/name,index/name,...) may return an array of values\n"
 "title(index)\n"
 "setTitle(index,name)\n"
 "width(n)\n"
 "addColumn(name)\n"
 "deleteColumn(index)\n"
 "getRow(index,[record])\n"
 "setRow(index,record)\n"
 "find(record,[start],[direction])\n"
 "findNext(record)\n"
 "add(record)\n"
 "addRow(record)\n"
 "delete(row) do not delete a row while looping over rowCount\n"
 "toString(true) converts the table to tab-delimited text\n"
 "getText(index) for mail databases only\n"
 "getHTML(index) for mail databases only\n"
 "index(fieldname,fieldname,...) returns an object with a find() function\n"
 "help()\n")


static JSBool
DataTable_DelRow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc == 0) ERR_COUNT(Table,DelRow);
 GETOBJ(DataTable,t);

 char c[2];
 c[1]=0;

 for (int i =0 ; i < argc; i++)
  {
   int32 row;
   if (!TOINT(i,row))
//   if (ISINT(i)) row = INT(i);
//   else
    ERR_TYPE(Table,delete,i,integer);

   if (!t->SetDataC(row,0,"Delete")) RETBOOL(false);
  }

 RETBOOL(true);
}

static JSBool
Table_FieldType(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc == 0) ERR_COUNT(Table,FieldType);
 ENTERNATIVE(cx);
 GETOBJ(DataTable,t);

 jsval * arr = new jsval[argc];

 char c[2];
 c[1]=0;

 for (int i =0 ; i < argc; i++)
  {
   int32 col;
   if (!TOINT(i,col)) //col = INT(i); else
     col = t->FindColumn(STR(i));
   c[0] = t->GetColStatus(col);
   if (!c[0]) c[0] = 'C';
   arr[i] = STRING_TO_JSVAL(ROOT(JS_NewStringCopyZ(cx,c)));
  }

 JSObject * ret = JS_NewArrayObject(cx,argc,arr);
 ROOT(ret);

 delete [] arr;
 RETOBJ(ret);
}

static JSBool
Table_SetDataC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc != 3) ERR_COUNT(Table,SetDataC);
 if (!ISINT(0)) ERR_TYPE(Table,set,1,integer);

 GETOBJ(DataTable,t);

 int32 j = 0;
 if (ISINT(1)) j = INT(1);
 else if (ISSTR(1)) j = t->FindColumn(STR(1));
 if (!j) ERR_TYPE(Table,set,2,column id);

 GETUTF8(2);

 bool ret = t->SetDataC(INT(0),j,s2);

 RETBOOL(ret);
}

static JSBool
Table_SetDataD(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc != 3) ERR_COUNT(Table,SetDataD);
 if (!ISINT(0)) ERR_TYPE(Table,SetDataD,1,Integer);
 jsdouble d;

 GETOBJ(DataTable,t);

 int32 j = 0;
 if (ISINT(1)) j = INT(1);
 else if (ISSTR(1)) j = t->FindColumn(STR(1));
 if (!j) return JS_FALSE;

 if (!TODBL(2,d)) return JS_FALSE;

 bool ret = t->SetDataD(INT(0),j,d);

 RETBOOL(ret);
}

static JSBool
Table_GetDataC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc != 2) ERR_COUNT(Table,GetDataC);
 if (!ISINT(0)) ERR_TYPE(Table,GetDataC,1,Integer);

 GETOBJ(DataTable,t);

 int32 j = 0;
 if (ISINT(1)) j = INT(1);
 else if (ISSTR(1)) j = t->FindColumn(STR(1));
 if (!j) ERR_MSG(Table,"Column unavailable","");

 const char * c = t->GetDataC(INT(0),j);

 if (c) RETSTR(c);
 ERR_MSG(Table,"Data unavailable","");
}

static JSBool
Table_GetMessage(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(DataTable,t);
 TMailList * m = TYPESAFE_DOWNCAST(t,TMailList);
 if (!m) RETSTR("");
 if (!argc) ERR_COUNT(Table,GetMessage);
 if (!ISINT(0)) ERR_TYPE(Table,GetMessage,1,Number);

 MailMessage * g = (*m)[INT(0)-1];
 if (!g) RETSTR("");
 RETSTR(g->MsgText());
}
static JSBool
Table_GetMessage2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(DataTable,t);
 TMailList * m = TYPESAFE_DOWNCAST(t,TMailList);
 if (!m) RETSTR("");
 if (!argc) ERR_COUNT(Table,GetMessage);
 if (!ISINT(0)) ERR_TYPE(Table,GetMessage,1,Number);

 MailMessage * g = (*m)[INT(0)-1];
 if (!g) RETSTR("");
 RETSTR(g->HTMLText());
}

static JSBool
Table_Save(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(DataTable,t);
 Stream* f;
 SpreadsheetTable * s = TYPESAFE_DOWNCAST(t,SpreadsheetTable);
 JSBool titles = true;
 if (argc > 2) TOBOOL(2,titles);

 if (s && argc >0 )
 {
  GETFILE(0,f);
  GETUTF8(1);
  if (f)
  {
   try
   {
          FileStream out(STR(0),Stream::OMText,Stream::IOWrite);
    RETBOOL(s->SaveToStream(out,s1,titles));
   } catch(...)
   {
    ERR_MSG(Table,"File write failure",STR(0));
   }
  }
  if (ISSTR(0))
  {
   try
   {
         FileStream out(STR(0),Stream::OMText,Stream::IOWrite);
    RETBOOL(s->SaveToStream(out,s1,titles));
   } catch(...)
   {
    ERR_MSG(Table,"File write failure",STR(0));
   }
  }
 }
 RETBOOL(t->Save());
}

JSBool
Table_JSGet(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
{
 const char * c = 0;
 count_t i = 0;

 GETOBJ(DataTable,t);

 int x = JSVAL_TO_INT(id);

 if (JSVAL_IS_INT(id))
  switch (x)
  {
   case 0: c = t->Filename; break;
   case 1: i = t->RowCount(); break;
   case 2: i = t->ColumnCount(); break;
   case 3: RETSTR("Table");
   case 4: RETSTR(t->GetLastError());
   case 5:
   {
#ifndef TBL_NO_SQL
     ODBCTable * o = TYPESAFE_DOWNCAST(t,ODBCTable);
     if (o)
     {
      RETSTR(o->DriverName());
     }
     else
#endif
     {
      RETSTR("");
     }
   }
   default: return JS_FALSE;
  }
 else return JS_FALSE;

 if (c) RETSTR(c);
 RETINT(i);
}


static JSBool
DataTable_ToString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(DataTable,t);
 if (argc == 0) RETSTR(t->filename());

 MemoryStream out;

 for (count_t i = 0; i <= t->RowCount(); i++)
 {
  for (count_t j=1; j <= t->ColumnCount(); j++)
  {
   if (j > 1) out << "\t";
   out << t->GetDataC(i,j);
  }
  out << "\n";
 }
 RETSTR(out);
}

WRAP(Table,Index)
{
 if (argc == 0)
  ERR_COUNT(Table,Index);

 GETENV;
 if (!Env->oIndex)
  RETOBJ(0);

 GETOBJ(DataTable,t);

 DataIndex*d;
 if (argc == 1 && ISINT(0))
 {
  d = new DataIndex(*t,INT(0));
 }
 else
 {
  TStringList list;
  for (int i=0; i< argc; i++)
  {
   JSString* j = JS_ValueToString(cx,argv[i]);
   if (!j) continue;
   list.Add(JS_GetStringBytes(j));
  }
  d = new DataIndex(*t,list);
 }
 d->BuildIndex(0,0);
 RETOBJ(Index_Object(cx,d,true,GETPOINTER));
}

WRAP(Table,Close)
{
 CLOSEPRIVATE(DataTable);
 RETBOOL(true);
}

void Table_JSFinalize(JSContext *cx, JSObject *obj)
{
// if (JS_GetParent(cx,obj)) return;
 DELPRIVATE(DataTable);
// JS_SetPrivate(cx,obj,NULL);
}

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

 DataTable * dt = NULL;

 try {
 if (argc == 1 && ISSTR(0))
  dt = OpenTable(STR(0),Env->TableEnv);
 else if (argc == 0)
  dt = new SpreadsheetTable;
 } catch (...) {dt = NULL;}

 if (!dt)
 {
  if (Env->errorOnFailure)
    return JS_FALSE;
  ERR_MSG(Table,"Open table failed","");
 }

 if (dt)
 {
  SETPRIVATE(obj,DataTable,dt,true,NULL);
 }
// JS_SetPrivate(cx,obj,dt);

 return JS_TRUE;
}

static JSPropertySpec Table_properties[] = {
    {"name",      0,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Table_JSGet},
    {"rowCount",  1,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Table_JSGet},
    {"colCount",  2,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Table_JSGet},
    {"count",  1,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Table_JSGet},
    {"length",  1,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Table_JSGet},
    {"className",3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Table_JSGet},
    {"error",4,JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Table_JSGet},
    {"driverName",5,JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Table_JSGet},
    {0}
};

static JSFunctionSpec Table_functions[] = {
    {"data",     Table_GetDataC,      2},
    {"get",     Table_GetDataC,      2},
    {"set",     Table_SetDataC,      3},
    {"setN",     Table_SetDataD,      3},
    {"save",    Table_Save,          0},
    {"type",    Table_FieldType,         1},
    {"column",  DataTable_FindColumn,    1},
//    {"error",  DataTable_GetLastError,    0},
    {"title",  DataTable_ColumnTitle,    1},
    {"setTitle",  DataTable_SetTitle,    2},
    {"width",  DataTable_GetColWidth,    1},
    {"deleteColumn",  DataTable_DelCol,    1},
    {"addColumn",  DataTable_AddCol,    1},
    {"add",DataTable_AddRow ,1},
    {"addRow",DataTable_AddRow ,1},
    {"del",DataTable_DelRow,1},
    {"deleteRow",DataTable_DelRow,1},
    {"delRow",DataTable_DelRow,1},
    {"find",Table_FindRow,1},
    {"index",Table_Index,10},
    {"findNext",Table_FindNext,1},
    {"getRow",Table_GetRow,1},
    {"setRow",DataTable_SetRow,2},
    {"toString",DataTable_ToString,0},
    {"close",Table_Close,0},
    {"getMessage",Table_GetMessage,1},
    {"getHTML",Table_GetMessage2,1},
    {0}
};

static JSFunctionSpec Table_fnstatic[] = {
    {"help",  DataTable_HELP,    0},
    {0}
};

static JSClass Table_class = {
    "Table", JSCLASS_HAS_PRIVATE,         //Table_JSGet
    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub,   JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,Table_JSFinalize
};

JSObject*
Table_Object(JSContext *cx, DataTable* t,bool autodelete,JSPointerBase* Parent)
{
 JSObject* obj;
 GETENV;
 ENTERNATIVE(cx);
 MAKENEW(Table);
 SETPRIVATE(obj,DataTable,t,autodelete,Parent);

 return obj;
}

void Table_InitClass(JSContext *cx, JSObject *obj)
{
 GETENV;
 INITCLASS(Table);
}

JSClass* Table_Class() {return &Table_class;}

