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

void Stream_JSFinalize(JSContext *cx, JSObject *obj)
{
 DELPRIVATE(Stream);
}

WRAP(Stream,Close)
{
 CLOSEPRIVATE(Stream);
 RETBOOL(true);
}

static void GetProxy(TStr& x,const char* address)
{
#ifdef XP_WIN
 TStr s;
 RegGetKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
           "ProxyEnable",s,HKEY_CURRENT_USER);
 if (!*s) return;
 if (!atoi(s)) return;

 TStr override;
 RegGetKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
           "ProxyOverride",override,HKEY_CURRENT_USER);

 if (stristr(override, "<local>") && !strchr(address,'.'))
   return;

 if (!strncasecmp(address,"http://",7)) address += 7;

 TStringList list(override,";");

 FOREACH(const char*c, list)
  if (!*c) continue;
  if (stristr(address,c) == 0) return;
 DONEFOREACH

 RegGetKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
           "ProxyServer",s,HKEY_CURRENT_USER);

 x = TStr("http://",s,"/");
#endif
}

class InetCache: public MemoryStream
{
  public:
   TStr name;
   const char * filename() {return name;}
   InetCache() :MemoryStream() {}
   ~InetCache() {}
};

JSBool
Stream_Stream(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
    jsval *rval)
{
 GETENV;
 if (!Env) return JS_FALSE;
 bool loadStatus = true;
 Stream * dt = NULL;

 GETUTF8(0);

 if ((argc == 1 || argc == 2) && ISSTR(0))
  {
   xdb err;
   if (Env->SafeMode)
    {//only web and memory streams are allowed
     if (strncasecmp(s0,"http://",7) && strncasecmp(s0,"inet://",7) && strncasecmp(s0,"net://",6))
     {
     if (Env->errorOnFailure)
      return JS_FALSE;
     ERR_MSG(Stream,"Safe mode denied access",s0);
    }
    }
   else
   // stdin and stdout are already mapped.
   if (*s0 == 0 || !strcasecmp(s0,"stdin") ||
       !strcasecmp(s0,"stdout") || !strcasecmp(s0,"stderr"))
    ERR_MSG(Stream,"Invalid filename",s0);

   if (!strncasecmp(s0,"http://",7))
    {//opens HTTP and reads the reply
     if (argc == 2 && ISINT(1) && INT(1) == 0)
     {
      dt = new InternetStream(s0);
     }
     else
     {
     try {
      TStr proxy;
      GetProxy(proxy,s0);
      TParameterList Headers;
      InternetStream * is = new InternetStream(TStr(proxy,s0));
      int status = is->GetHeaders(Headers);
      *rval = INT_TO_JSVAL(status);
      JS_SetProperty(cx, obj,"status",rval);
      const char* chunked = Headers("Transfer-Encoding");
      if (!strcasecmp(chunked,"chunked")) //chunked always comes first
      {
       InetCache*ic = new InetCache();
       ic->name = is->filename();
       dt = ic;
       if (strchr(chunked,';')) //other encodings
          Headers.Set("Transfer-Encoding",strchr(chunked,';') + 1);
       else
          Headers.Unset("Transfer-Encoding");

       dt->WriteMIME(Headers);

       TStr line;
       while (is->readline(line))
       {
        Replace(line,'\r',0);
        if (!*line) continue;
        unsigned count = strtoul(line,0,16);
        if (!count) break;
        dt->Append(*is,count);
       }
       delete is;
       dt->rewind();
      }
      else
      {
       is->start = new MemoryStream;
       is->start->WriteMIME(Headers);
       is->start->rewind();
       dt = is;
      }

      if (dt->error) {delete dt; dt=0;}
     } catch(...) {dt=0;}
     loadStatus = false;
     }
    }
#ifndef XP_UNIX
   else if (Env->AllowExec && !strncasecmp(s0,"exec://",7))
   {
     try {
     bool detached = false;
     if (ISSTR(1)) if (toupper(*STR(1))=='D')
      detached = true;
      dt = new PipeStream(s0+7,detached);
      if (dt->error) {delete dt; dt=0;}
     }
     catch (...) {ERR_MSG(Stream,"Could not open the pipe",s0);}
   }
#endif

   if (!dt)
    dt = OpenStream(s0,&err);
   //OpenStream returns NULL if the string doesn't have a ://

   if (dt) if (dt->error)
   {
     delete dt;
     if (Env->errorOnFailure)
       return JS_FALSE;

     ERR_MSG(Stream,"Could not open the stream",s0)
    // else
    //  return JS_FALSE; //

   }

   if (dt && (!strncasecmp(s0,"inet://",7) || !strncasecmp(s0,"net://",6)))
   {
     InternetStream* is = TYPESAFE_DOWNCAST(dt,InternetStream);
     if (is && argc > 1 && ISINT(1) && INT(1) == 1)
       is->NoDelay();
   }

   if (dt && loadStatus)
   {
    if (!strncasecmp(s0,"http://",7))
    {//load the HTTP header
     TStr status;
     dt->readline(status);

     *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,(char*)status));
     JS_SetProperty(cx, obj,"status",rval);
    }
   }
   else if (!dt && s0)
    if (*s0)
    {
     Stream::TType Type = Stream::IORead;
     Stream::TOpenMode Mode = Stream::OMText;
     if (argc == 2 && ISSTR(1))
      {
       const char * mode = STR(1);
       if (strchr(mode,'a')) Type = Stream::IOAppend;
       //else if (strchr(mode,'c')) Type = Stream::IOCreate;
       else if (strchr(mode,'r'))
       {
        if (strchr(mode,'w')) Type = Stream::ReadWrite;
        else Type = Stream::IORead;
       }
       else if (strchr(mode,'w')) Type = Stream::IOCreate;

       if (strchr(mode,'b')) Mode = Stream::OMBinary;
       if (strchr(mode,'+')) Mode = (Stream::TOpenMode)((int)Mode | (int)Stream::OMUnbuffered);
      }

     try {
      dt = new FileStream(s0,Mode,Type);
     } catch(...)
     {
      dt = NULL;
     }
   }
 }

 /*else if (argc == 1 && ISINT(0))
 {
  try {
  dt = new MemoryStream(INT(1));
  }catch (...) {dt = NULL;}
 }    */
 else if (argc == 0)
 {
  try{
  dt = new MemoryStream;
  }  catch (...) {dt = NULL;}
 }

 if (!dt)
  {
   if (Env->errorOnFailure)
   return JS_FALSE; //

   ERR_MSG(Stream,"Could not open the stream",s0)
  }
 //if (!dt) return JS_FALSE;

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

 return JS_TRUE;
}


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

 int x = JSVAL_TO_INT(id);

 if (JSVAL_IS_INT(id))
 {
  InternetStream* s = 0;
#ifdef XP_WIN
  PipeStream* p =0;
#endif
  switch (x)
  {
   case 0: RETSTR(t->filename());
   case 1: RETINT(t->size());
   case 2:
   case 3: RETINT(t->pos());
   case 4: RETBOOL(t->eof());
   case 5:
   case 6: s = TYPESAFE_DOWNCAST(t,InternetStream);
           break;
   case 7: RETBOOL(t->canread());
   case 8: RETBOOL(t->canwrite());
   case 9: RETSTR("Stream");
#ifdef XP_WIN
   case 10: p=TYPESAFE_DOWNCAST(t,PipeStream);
#endif
  }
  switch(x)
  {
   case 5: if (s) RETSTR(s->hostinfo);
           RETSTR("");
   case 6: if (s) RETINT(s->hostaddr);
           RETINT(0);
#ifdef XP_WIN
   case 10: if (p && p->StdErr) RETOBJ(Stream_Object(cx,p->StdErr,false,GETPOINTER));
           RETOBJ(0);
#endif
  }
 }

 return JS_FALSE;
}

static JSPropertySpec Stream_properties[] = {
    {"name",      0,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"size",  1,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"position",  2,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"pos",  3,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"eof",  4,   JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"hostName", 5, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"hostAddress", 6, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"canRead", 7, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"canWrite", 8, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
    {"className", 9, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
#ifdef XP_WIN
    {"stderr", 10, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Stream_JSGet},
#endif
    {0}
};

WRAP(Stream,SetEndOfFile)
{
 if (argc != 1) ERR_COUNT(Stream,read);
 int32 len;
 if (!TOINT(0,len)) ERR_TYPE(Stream,SetEndOfFile,1,integer);
 GETOBJ(Stream,t);
 {
 FileStream* f;
 f = TYPESAFE_DOWNCAST(t,FileStream);
 if (f)
 {
  f->SetEndOfFile(len);
  RETBOOL(true);
 }
 }
   {
 MemoryStream* f;
 f = TYPESAFE_DOWNCAST(t,MemoryStream);
 if (f)
 {
  f->Maxsize = min(f->Maxsize,len);
  RETBOOL(true);
 }
  }
  {
 ByteStream* f;
 f = TYPESAFE_DOWNCAST(t,ByteStream);
 if (f)
 {
  f->Maxsize = min(f->Maxsize,len);
  RETBOOL(true);
 }
 }

 RETBOOL(false);
}


WRAP(Stream,Clear)
{
 if (argc > 1) ERR_COUNT(Stream,Clear);
 int32 size = 0;
 if (argc == 1 && !TOINT(0,size)) ERR_TYPE(Stream,Clear,1,integer);
 GETOBJ(Stream,t);
 MemoryStream * m;
 m = TYPESAFE_DOWNCAST(t,MemoryStream);
 if (!m) RETBOOL(false);

 m->Clear(size);
 RETBOOL(true);
}

WRAP(Stream,Resize)
{
 if (argc != 1) ERR_COUNT(Stream,Resize);
 int32 size = 0;
 if (!TOINT(0,size)) ERR_TYPE(Stream,Resize,1,integer);
 GETOBJ(Stream,t);
 MemoryStream * m;
 m = TYPESAFE_DOWNCAST(t,MemoryStream);
 if (m)
  {
   m->Resize(size);
   RETBOOL(true);
  }
 FileStream * f = TYPESAFE_DOWNCAST(t,FileStream);
 if (f)
  {
   f->SetEndOfFile(size);
   RETBOOL(true);
  }
 RETBOOL(true);
}

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

 const char* s = t->filename();
 if (s) RETSTR(s);

 MemoryStream * m;
 ByteStream * b;
 m = TYPESAFE_DOWNCAST(t,MemoryStream);
 if (m) RETSTR((char*)*m);
 b = TYPESAFE_DOWNCAST(t,ByteStream);
 if (b) RETSTRN(b->Buf,b->Maxsize);
 RETSTR("");
}

I_WRAP_I(Stream,seek)
I_WRAP_I(Stream,goforward)
I_WRAP_I(Stream,putback)
C_WRAP_V(Stream,peek)
C_WRAP_V(Stream,get)
V_WRAP_C(Stream,put)
V_WRAP_V(Stream,rewind)
I_WRAP_R(Stream,Write)
I_WRAP_R(Stream,Read)
I_WRAP_R(Stream,WriteMIME)
//I_WRAP_R(Stream,ReadMIME)

WRAP(Stream,ReadMIME)
{
 TParameterList * r1;
 GETOBJ(Stream,t);
 if (argc)
 {
  GETREC(0,r1); if (!r1) ERR_TYPE(Stream,ReadMIME,1,record);
  RETINT(t->ReadMIME(*r1));
 }
 else
 {
  r1 = new TParameterList;
  t->ReadMIME(*r1);
  RETOBJ(Record_Object(cx,r1,true,0));
 }
}

WRAP(Stream,ReadTag)
{
 GETOBJ(Stream,t);

 bool EndTag=false;
 TStr type;
 MemoryStream text;
 TParameterList* p=NULL;
 TStr allowedtags;

 if (argc > 0) GETREC(0,p);
 if (argc > 1 && ISSTR(1))
  {
   allowedtags = STR(1);// ![CDATA[ is always an allowed tag
  }

 int lastc = t->StartTag(type,&text,&EndTag,allowedtags[0]?(char*)allowedtags:(char*)0);

 if (!strncasecmp(type, "![CDATA[",8))
  {
   size_t len = strlen(type);
   if (lastc == '>' && len > 8 && type[len-1] == ']' && type[len-2] == ']')
    {
     type[len-2] = 0;
     text.writestr(((char*)type)+8);
    }
   else
    {
     text.writestr(((char*)type)+8);
     if (len > 8 || !IsSpace(lastc)) text.put(lastc);
     t->ReadUntilWord("]]>",&text);
    }
   *rval = BOOLEAN_TO_JSVAL(false);
   JS_SetProperty(cx, obj,"hasChildren",rval);
   *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,text));
   JS_SetProperty(cx, obj,"tagText",rval);
   RETSTR("![CDATA[");
  }
 else if (t->FinishTag(lastc,p,&EndTag))
 {
  *rval = BOOLEAN_TO_JSVAL((EndTag==false));
  JS_SetProperty(cx, obj,"hasChildren",rval);
  char * c = text;

  if (IsWhitespace(text,strlen(text)))
   c = "";
  else
  {
   c = StripCharsFB(c,"\r\n\f\v");
   //don't strip trailing spaces or tabs

   if (strchr(" \t",c[0]) && strchr(" \t",c[1]))
   {
    while (c[0] && strchr(" \t",c[0])) c++;
    //but do strip leading ones, except for the first space
   }
  }
  *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,c));
  JS_SetProperty(cx, obj,"tagText",rval);
  RETSTR(type);
 }
 else
 {
  *rval = BOOLEAN_TO_JSVAL(false);
  JS_SetProperty(cx, obj,"hasChildren",rval);
  *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,text));
  JS_SetProperty(cx, obj,"tagText",rval);
  RETSTR("");
 }
}

WRAP(Stream,WritePaired)
{
 GETOBJ(Stream,t);
 if (argc == 0 || argc > 3) ERR_COUNT(Stream,WritePaired);
 TParameterList* p;
 GETREC(0,p);
 if (!p) ERR_TYPE(Stream,WritePaired,0,record);
 const char * delim = 0, *eq = 0;
 if (argc >  1) delim = STR(1);
 if (argc == 3) eq = STR(2);
 RETINT(t->WritePaired(*p,delim,eq));
}

//readpaired(record, delimiter, equals sign)
WRAP(Stream,ReadPaired)
{
 GETOBJ(Stream,t);
 if (argc == 0 || argc > 3) ERR_COUNT(Stream,ReadPaired);
 TParameterList* p;
 GETREC(0,p);
 if (!p) ERR_TYPE(Stream,ReadPaired,0,record);
 const char * delim = 0, *eq = 0;
 if (argc >  1) delim = STR(1);
 if (argc == 3) eq = STR(2);
 if (!delim || !*delim) delim = "\n";
 if (!eq || !*eq) eq = "=";
 RETINT(t->ReadPaired(*p,*delim,*eq));
}

static JSBool Stream_ReadUntilWord(JSContext *cx,
 JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc == 0 || argc > 2) ERR_COUNT(Stream,ReadUntilWord);
 if (!ISSTR(0)) ERR_TYPE(Stream,ReadUntilWord,1,string);
 Stream* out = 0;
 if (argc == 2) GETFILE(1,out);
 GETOBJ(Stream,t);
 GETUTF8(0);
 RETBOOL(t->ReadUntilWord(s0,out));
}

static JSBool Stream_ReadUntilBytes(JSContext *cx,
 JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc == 0 || argc > 2) ERR_COUNT(Stream,ReadUntilBytes);
 if (!ISSTR(0)) ERR_TYPE(Stream,ReadUntilBytes,1,string);
 JSString* s = JS_ValueToString(cx,argv[0]);
 if (!s) RETBOOL(false);
 char* c = JS_GetStringBytes(s);
 size_t length = JS_GetStringLength(s);
 Stream* out = 0;
 if (argc == 2) GETFILE(1,out);
 GETOBJ(Stream,t);
 RETBOOL(t->ReadUntilBytes(c, length, out));
}

C_WRAP_S(Stream,EatChars)

WRAP(Stream,ReadFile)
{
 if (argc > 2) ERR_COUNT(Stream,ReadFile);
 GETOBJ(Stream,t);
 MemoryStream dest;
 Stream * x;
 int size = LONG_MAX;
 int local = 1;

 if (ISINT(0))      size = INT(0);
 else if (ISINT(1)) size = INT(1);

 GETFILE(0,x);
 if (!x) GETFILE(1,x);

 if (!x) x = &dest;
 else local = 0;

 size = x->Append(*t, size);

 if (local) RETSTRN(dest,dest.size());

 RETINT(size);
}

WRAP(Stream,ReadText)
{
 if (argc > 2) ERR_COUNT(Stream,ReadText);
 GETOBJ(Stream,t);
 MemoryStream dest;
 int32 size = LONG_MAX;
 if (argc > 1) TOINT(0,size);
 dest.AppendAsText(*t, size);
 RETSTRN(dest,dest.size());
}

WRAP(Stream,ReadLine)
{
 if (argc > 1) ERR_COUNT(Stream,ReadLine);
 GETOBJ(Stream,t);
 TStr dest;
 char* delim = 0;
 if (argc == 1)
  {
   if (!ISSTR(0)) ERR_TYPE(Stream,ReadLine,1,string);
   delim = STR(0);
  }
 if (!delim || !*delim) delim = "\n";
 t->readline(dest,delim[0]);
 RETSTR(dest);
}


WRAP(Stream,read)
{
 if (argc == 2 || argc == 0) return CALL(Stream,ReadFile);
 if (argc != 1) ERR_COUNT(Stream,read);
 int32 len;
 if (!TOINT(0,len)) ERR_TYPE(Stream,Read,1,integer);

 GETOBJ(Stream,t);

 if (!len) RETSTR("");
 TChars s(len);
 s[t->read(s,len)]=0;
 RETSTRN((char*)s,len);
}

WRAP(Stream,write)
{
 if (argc == 0) RETINT(0); //ERR_COUNT(Stream,write);
 GETOBJ(Stream,t);
 int ret = 0;

 for (int i = 0; i < argc; i++)
 {
   Stream* in;
   GETFILE(i,in);
   if (in)
     t->Append(*in);
   else
   {
      char * s;
   JSString* j = JS_ValueToString(cx,argv[i]);
   if (j)
   {
    s = JS_GetStringBytes(j);
    int len = JS_GetStringLength(j);
    ret += t->write(s,len);
   }
   }
 }
 RETINT(ret);
}

class ArgList: public TNameValueList
{
 public:
 const char ** args;
 size_t length;
 ArgList(const char**a, size_t l) : args(a), length(l) {};
 ~ArgList() {};

 size_t Count() {return length;}
 const char* Name (size_t i)
  {return i < length ? args[2 * i] : 0; }

 const char* Value (size_t i)
  {return i < length ? args[2 * i + 1]  : 0; }
};

WRAP(Stream,Format)
{
 if (argc  == 0) return JS_TRUE;
 if (argc == 1) CALL(Stream,write);
 GETOBJ(Stream,t);
 //size_t l=0;

 TParameterList* p=0;
 GETREC(1,p);

 Stream* in;
 GETFILE(0,in);

 if (!p) ERR_TYPE(Stream,Format,2,Record);

 const char* start = 0;
 const char* end = 0;

 if (argc == 3 && ISSTR(2)) start = end = STR(2);
 if (argc == 4 && ISSTR(2) && ISSTR(3))
  {
   start = STR(2);
   end = STR(3);
  }

 if (!start || !*start) start = "{";
 if (!end || !*end) end = "}";

 if (in)
 {
  FormatText(*p,*in,*t,start,end);
 }
 else
 {
   char * s;
   JSString* j = JS_ValueToString(cx,argv[0]);
   if (j)
   {
    s = JS_GetStringBytes(j);
    ByteStream src(s);
    FormatText(*p,src,*t,start,end);
   }
 }
 return JS_TRUE;
}

WRAP(Stream,writeln)
{
 if (argc) CALL(Stream,write);
 GETOBJ(Stream,t);
 t->write("\n",1);
 return JS_TRUE;
}

#define SWAP32(i) ( ((i << 24)& 0xff000000) | ((i << 8) & 0x00ff0000) | ((i >> 8) & 0x0000ff00) | ((i >> 24) & 0x000000ff))
WRAP(Stream,ReadInt)
{
 GETOBJ(Stream,t);
 int32 i;
 t->read(&i,sizeof(i));
 if (argc > 1 && JSVAL_TO_BOOLEAN(argv[1]))
   i=SWAP32(i);
 RETINT(i);
}

WRAP(Stream,WriteInt)
{
 if (argc == 0) ERR_COUNT(Stream,WriteInt);
 GETOBJ(Stream,t);
 int32 i;
 TOINT(0,i);
 if (argc > 1 && JSVAL_TO_BOOLEAN(argv[1]))
   i=SWAP32(i);
 t->write(&i,sizeof(i));
 RETBOOL(true);
}

#define SWAP16(i) ( ((i >> 8) & 0x00ff) | ((i << 8) & 0xff00) )
WRAP(Stream,ReadInt16)
{
 GETOBJ(Stream,t);
 int16 i;
 t->read(&i,sizeof(i));
 if (argc > 1 && JSVAL_TO_BOOLEAN(argv[1]))
   i=SWAP16(i);
 RETINT(i);
}

WRAP(Stream,WriteInt16)
{
 if (argc == 0) ERR_COUNT(Stream,WriteInt);
 GETOBJ(Stream,t);
 int32 x;
 TOINT(0,x);
 int16 i= (int16)x;
 if (argc > 1 && JSVAL_TO_BOOLEAN(argv[1]))
   i=SWAP16(i);
 t->write(&i,sizeof(i));
 RETBOOL(true);
}
WRAP(Stream,ReadUInt16)
{
 GETOBJ(Stream,t);
 uint16 i;
 t->read(&i,sizeof(i));
 if (argc > 1 && JSVAL_TO_BOOLEAN(argv[1]))
   i=SWAP16(i);
 RETINT(i);
}

WRAP(Stream,ReadInt8)
{
 GETOBJ(Stream,t);
 int8 i;
 t->read(&i,sizeof(i));
 RETINT(i);
}

WRAP(Stream,WriteInt8)
{
 if (argc == 0) ERR_COUNT(Stream,WriteInt);
 GETOBJ(Stream,t);
 int32 x;
 TOINT(0,x);
 int8 i= (int8)x;
 t->write(&i,sizeof(i));
 RETBOOL(true);
}

WRAP(Stream,ReadByte)
{
 GETOBJ(Stream,t);
 uint8 i;
 t->read(&i,sizeof(i));
 RETINT(i);
}

WRAP(Stream,WriteByte)
{
 if (argc == 0) ERR_COUNT(Stream,WriteInt);
 GETOBJ(Stream,t);
 int32 x;
 TOINT(0,x);
 uint8 i= (uint8)x;
 t->write(&i,sizeof(i));
 RETBOOL(true);
}


WRAP(Stream,Append)
{
 if (argc == 0) ERR_COUNT(Stream,Append);
 GETOBJ(Stream,t);
 int32 maxcopy = LONG_MAX;
 Stream* in;
 GETFILE(0,in);
 if (!in) ERR_TYPE(Stream,Append,1,Stream);
 if (argc > 1 && ISINT(1)) maxcopy = INT(1);
 RETINT(t->Append(*in,maxcopy));
}

WRAP(Stream,AppendAsText)
{
 if (argc == 0) ERR_COUNT(Stream,Append);
 GETOBJ(Stream,t);
 int32 maxcopy = LONG_MAX;
 Stream* in;
 GETFILE(0,in);
 if (!in) ERR_TYPE(Stream,AppendAsText,1,Stream);
 if (argc > 1 && ISINT(1)) maxcopy = INT(1);
 RETINT(t->AppendAsText(*in,maxcopy));
}
//I_WRAP_F(Stream,Append);
//I_WRAP_F(Stream,AppendAsText);

static JSFunctionSpec Stream_functions[] = {
    {"read",     Stream_read,      1},
    {"clear",    Stream_Clear, 0},
    {"resize",    Stream_Resize, 1},
    {"write",     Stream_write,      8},
    {"writeln",     Stream_writeln,     8},
    {"seek",     Stream_seek,      3},
    {"goForward",     Stream_goforward,      3},
    {"goBack",    Stream_putback,          0},
    {"peek",    Stream_peek,          0},
    {"get",  Stream_get,    1},
    {"put",  Stream_put,    0},
    {"putBack",  Stream_putback,    1},
    {"rewind",  Stream_rewind,    2},
    {"writeListB",  Stream_Write,    1},
    {"readListB",  Stream_Read,    1},
    {"writeList",  Stream_WritePaired,    1},
    {"readList",Stream_ReadPaired ,1},
    {"writeMIME",Stream_WriteMIME,1},
    {"readMIME",Stream_ReadMIME,2},
    {"readUntil",Stream_ReadUntilWord,2},
    {"readUntilBytes",Stream_ReadUntilBytes,2},
    {"eatChars",Stream_EatChars,1},
    {"readln",Stream_ReadLine,0},
//    {"writeln",Stream_WriteLine,1},
    {"readLine",Stream_ReadLine,0},
    {"readFile",Stream_ReadFile,1},
    {"readText",Stream_ReadText,1},
    {"readTag",Stream_ReadTag,0},
    {"writeLine",Stream_writeln,1},
    {"readInt",Stream_ReadInt,1},
    {"writeInt",Stream_WriteInt,1},
    {"readInt32",Stream_ReadInt,1},
    {"writeInt32",Stream_WriteInt,1},
    {"readInt16",Stream_ReadInt16,1},
    {"writeInt16",Stream_WriteInt16,1},
    {"readByte",Stream_ReadByte,1},
    {"writeByte",Stream_WriteByte,1},
    {"readInt8",Stream_ReadInt8,1},
    {"writeInt8",Stream_WriteInt8,1},
    {"readUInt16",Stream_ReadUInt16,1},
    {"writeUInt16",Stream_WriteInt16,1}, //no difference in writing
    {"readUInt8",Stream_ReadByte,1},
    {"writeUInt8",Stream_WriteByte,1},
    {"append",Stream_Append,1},
    {"appendText",Stream_AppendAsText,1},
    {"toString",Stream_ToString,0},
    {"close",Stream_Close,0},
    {"format",Stream_Format,16},
    {"setEndOfFile",Stream_SetEndOfFile,1},
    {0}
};

WRAP(Stream,HELP)
{
 MemoryStream msg;
 for (int i = 0;Stream_functions[i].name; i++)
 {
  msg << Stream_functions[i].name << "(";
  if (Stream_functions[i].nargs) msg  << Stream_functions[i].nargs;
  msg << ")\n";
 }
 RETSTR((char *)msg);
}

static JSFunctionSpec Stream_fnstatic[] = {
    {"help",  Stream_HELP,    0},
    {0}
};

static JSClass Stream_class = {
    "Stream", JSCLASS_HAS_PRIVATE,         //Stream_JSGet
    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub,   JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,Stream_JSFinalize
};

JSObject*
Stream_Object(JSContext *cx, Stream* t,bool autodelete,JSPointerBase* Parent)
{
 JSObject* obj;
 GETENV;
 MAKENEW(Stream);
 SETPRIVATE(obj,Stream,t,autodelete,Parent);
 return obj;
}

void Stream_InitClass(JSContext *cx, JSObject *obj)
{
 GETENV;
 INITCLASS(Stream);
}

JSClass* Stream_Class() {return &Stream_class;}
