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

#ifdef XP_WIN
#include <oleauto.h>
#include "js/jsatom.h"
#include "js/jsfun.h"

//dword-word-word-byte[8]
//{85BBD920-42A0-1069-A2E4-08002B30309D}
//HRESULT CLSIDFromString(LPOLESTR lpsz,LPCLSID pclsid);
//S_OK == StringFromCLSID(REFCLSID rclsid,LPOLESTR * ppsz);

#ifndef DISPID_PROPERTYPUT
#define DISPID_PROPERTYPUT (-3)
#endif

class ActiveX
{public:
 IDispatch * dispatch;
 IUnknown * unknown;
 ITypeInfo * typeinfo;

 struct PropInfo
  {
   WStr name;
   bool Get;
   bool Put;
   bool PutRef;
   PropInfo(const uint16* n): name(n) {Get = Put = PutRef = false;}
  };

 TList<PropInfo> Properties;

 ActiveX();
 ActiveX(CLSID& clsid);
 ActiveX(IUnknown *obj);
 ActiveX(IDispatch *obj);
 TIntList properties;

 ~ActiveX();

 PropInfo* Find(const uint16* n);

// bool Id(size_t x,DISPID &dispid);

 bool Id(const uint16* name,DISPID &dispid);

 bool Set(DISPID dispid,JSContext *cx, uintN argc, jsval *argv, jsval* rval,bool ref);

 bool Get(DISPID dispid,JSContext *cx, uintN argc, jsval *argv, jsval* rval);

 //throws an xdb exception on error
 bool Invoke(DISPID dispid, JSContext *cx, uintN argc, jsval *argv, jsval* rval);

 bool SetupValue(VARIANTARG& arg, JSContext* cx, jsval* rval);

 void CheckReturn(JSContext* cx, jsval *rval);

 bool RetrieveValue(VARIANTARG& arg, JSContext* cx, jsval* rval);

 bool SetupMembers(JSContext* cx, JSObject* obj);
};

ActiveX::PropInfo* ActiveX::Find(const uint16* n)
{
 FOREACH(PropInfo*p,Properties)
  if (!ucscmp(p->name,n)) return p;
 DONEFOREACH
 return 0;
}

JSClass* ActiveX_Class();

JSObject* ActiveX_Object(JSContext *cx, ActiveX* t,bool autodelete,JSPointerBase* Parent);

extern "C" JSObject *
js_NewDateObject(JSContext* cx, int year, int mon, int mday,
                 int hour, int min, int sec);

#define FETCH(x) (ref? * (var.p ## x) : var.x)
bool ActiveX::RetrieveValue(VARIANTARG& var, JSContext* cx, jsval* rval)
{
 ENTERNATIVE(cx);
 bool ref = false;
 int type = var.vt;
 if (type & VT_BYREF) {ref = true; type &= ~VT_BYREF;}
 ActiveX* x;
try {
 switch (type)
 {
  case VT_EMPTY: *rval = OBJECT_TO_JSVAL(NULL); break;
  case VT_I1: *rval = INT_TO_JSVAL(FETCH(cVal)); break;
  case VT_I2: *rval = INT_TO_JSVAL(FETCH(iVal)); break;
  case VT_INT:
  case VT_I4: if (INT_FITS_IN_JSVAL(FETCH(lVal)))
                  *rval = INT_TO_JSVAL(FETCH(lVal));
               else
                  *rval = DOUBLE_TO_JSVAL(ROOT(JS_NewDouble(cx,FETCH(lVal))));
               break;

  case VT_R4: *rval = DOUBLE_TO_JSVAL(ROOT(JS_NewDouble(cx,FETCH(fltVal)))); break;
  case VT_R8: *rval = DOUBLE_TO_JSVAL(ROOT(JS_NewDouble(cx,FETCH(dblVal)))); break;

  case VT_BOOL: *rval = BOOLEAN_TO_JSVAL(FETCH(boolVal)); break;

  case VT_UI1: *rval = INT_TO_JSVAL(FETCH(bVal)); break;
  case VT_UI2: *rval = INT_TO_JSVAL(FETCH(uiVal)); break;
  case VT_UINT:
  case VT_UI4: if (FETCH(ulVal) <= JSVAL_INT_MAX)
                  *rval = INT_TO_JSVAL(FETCH(ulVal));
               else
                  *rval = DOUBLE_TO_JSVAL(ROOT(JS_NewDouble(cx,FETCH(ulVal))));
               break;

  case VT_BSTR: *rval = STRING_TO_JSVAL(ROOT(JS_NewUCStringCopyN(cx,(jschar *)FETCH(bstrVal),SysStringLen(FETCH(bstrVal)))));
              SysFreeString(FETCH(bstrVal));
              break;

  case VT_DATE:
  {
   DATE d = FETCH(date);
   SYSTEMTIME time;
   VariantTimeToSystemTime(d,&time);
   *rval = OBJECT_TO_JSVAL(ROOT(js_NewDateObject(cx,time.wYear,time.wMonth-1,time.wDay,
                time.wHour,time.wMinute, time.wSecond)));
   break;
  }

  case VT_UNKNOWN:
   if (!FETCH(punkVal)) {*rval = OBJECT_TO_JSVAL(0); break;}
   x = new ActiveX(FETCH(punkVal));
   if (!x->unknown && !x->dispatch) {delete x; return false;}
   *rval = OBJECT_TO_JSVAL(ActiveX_Object(cx, x,true,NULL));
   break;

  case VT_DISPATCH:
   if (!FETCH(pdispVal)) {*rval = OBJECT_TO_JSVAL(0); break;}
   x = new ActiveX(FETCH(pdispVal));
   if (!x->unknown && !x->dispatch) {delete x; return false;}
   *rval = OBJECT_TO_JSVAL(ActiveX_Object(cx, x,true,NULL));
   break;

  case VT_VARIANT:
   if (ref)
   {
    VARIANTARG* v = var.pvarVal;
    if (v)
      return RetrieveValue(*v,cx,rval);
   }
   break;

  default: return false;
 }
 } catch(...)
  {
   return false;
  }
 return true;
}
#undef FETCH

void ActiveX::CheckReturn(JSContext* cx, jsval *rval)
{
 if (JSVAL_IS_OBJECT(*rval))
 {
  HRESULT hresult;
  JSObject* j0 = JSVAL_TO_OBJECT(*rval);
  if (j0 && JS_InstanceOf(cx, j0, ActiveX_Class(), 0))
   {
    ActiveX* x = GETPRIVATE(ActiveX,j0);
    if (x->unknown && !x->dispatch)
    {
     hresult = unknown->QueryInterface(IID_IDispatch, (void * *)&x->dispatch);
     if (SUCCEEDED(hresult))
     {
       x->SetupMembers(cx, j0);
     }
     else
     {x->dispatch = 0; }
    }
   }
  }
}

bool ActiveX::SetupValue(VARIANTARG& arg, JSContext* cx, jsval *rval)
{
 if (JSVAL_IS_OBJECT(*rval))
 {
   JSObject* j0 = JSVAL_TO_OBJECT(*rval);
   if (j0 && JS_InstanceOf(cx, j0, ActiveX_Class(), 0))
   {
    ActiveX* x = GETPRIVATE(ActiveX,j0);
    if (x->dispatch)
     {
      arg.vt = VT_DISPATCH;
      arg.pdispVal = x->dispatch;
      return true;
     }
    else if (x->unknown)
     {
      arg.vt = VT_UNKNOWN;
      arg.punkVal = x->unknown;
      return true;
     }
    else
     {
      arg.vt = VT_BYREF|VT_UNKNOWN;
      arg.ppunkVal = &x->unknown;
      return true;
     }
   }
 }

 if (JSVAL_IS_BOOLEAN(*rval))
 {
  arg.vt = VT_BOOL;
  arg.boolVal = JSVAL_TO_BOOLEAN(*rval);
  return true;
 }

 if (JSVAL_IS_INT(*rval))
 {
  arg.vt = VT_I4;
  arg.lVal = JSVAL_TO_INT(*rval);
  return true;
 }

 if (JSVAL_IS_DOUBLE(*rval))
 {
  arg.vt = VT_R8;
  arg.dblVal = *JSVAL_TO_DOUBLE(*rval);
  return true;
 }

 if (JSVAL_IS_NULL(*rval))
 {
  arg.vt = VT_EMPTY;
  arg.scode = 0;
  return true;
 }

 if (JSVAL_IS_STRING(*rval))
 {
  arg.vt = VT_BSTR;
  arg.bstrVal = SysAllocString((WCHAR*)JS_GetStringChars(JSVAL_TO_STRING(*rval)));
  return true;
 }

 return false;
}

JSBool
ActiveX_ActiveX(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
    jsval *rval)
{
 GETENV;
 if (Env->SafeMode) return JS_FALSE;

 if (argc)
   if (!ISSTR(0)) ERR_TYPE(ActiveX,ActiveX,1,String);
 ENTERNATIVE(cx);

 CLSID clsid;
 HRESULT hresult;

 jschar * name = JS_GetStringChars(JSVAL_TO_STRING(argv[0]));
 if (name[0] == L'{')
   hresult = CLSIDFromString((WCHAR*)name,&clsid);
 else
   hresult = CLSIDFromProgID((WCHAR*)name,&clsid);

 if (!SUCCEEDED(hresult))
 {
  ERR_MSG(ActiveX,ActiveX,"invalid CLSID");
 }

 ActiveX* t = NULL;

 if (argc == 0)
 {
  t = new ActiveX();

 }
 else if (argc == 1)
 {
  IUnknown* unk = NULL ;
  hresult = GetActiveObject(clsid,NULL,&unk);
  if (SUCCEEDED(hresult) && unk)
  {
    t = new ActiveX(unk);
    if (!t->unknown)
    {
     delete t;
     t=0;
     ERR_MSG(ActiveX,"Can't create ActiveX object",TStr(name));
    }
  }
 }

 if (!t)
 {
   t = new ActiveX(clsid);
   if (!t->unknown)
   {
    delete t;
    ERR_MSG(ActiveX,"Can't create ActiveX object",TStr(name));
   }
 }

// if (!t) return JS_FALSE;
 if (t)
 {
  SETPRIVATE(obj,ActiveX,t,true,NULL);

  t->SetupMembers(cx,obj);
 }
 return JS_TRUE;
}


//shadow property 0 to 255
JSBool
ActiveX_JSGet(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
{
 if (!JSVAL_IS_INT(id)) return JS_FALSE;
 int x = JSVAL_TO_INT(id);

 switch (x)
  {
   case 255: RETSTR("ActiveX");
  }

 GETOBJ(ActiveX,t);

 ActiveX::PropInfo * p = t->Properties[x];
 DISPID dispid=0;
 if (p)
  if (t->Id((uint16*)p->name,dispid))
   return t->Get(dispid,cx,0,0,rval);

 return JS_FALSE;
}

JSBool
ActiveX_JSSet(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
{
 if (!JSVAL_IS_INT(id)) return JS_FALSE;
 int x = JSVAL_TO_INT(id);

 GETOBJ(ActiveX,t);

// DISPID dispid=0;
// if (t->Id(id,dispid))

 ActiveX::PropInfo * p = t->Properties[x];
 DISPID dispid=0;
 if (p)
  if (t->Id(p->name,dispid))
   return t->Set(dispid,cx,0,0,rval,p->PutRef);

 return JS_FALSE;
}

char * DeflateString(const jschar*chars, size_t length)
{
    size_t i, size;
    char *bytes;

    size = (length + 1) * sizeof(char);
    bytes = (char *) malloc(size);
    if (!bytes) return NULL;
    for (i = 0; i < length; i++)
        bytes[i] = (char) chars[i];
    bytes[i] = 0;
    return bytes;
}

void ActiveXError(HRESULT hresult, EXCEPINFO& exception, UINT& argerr, JSContext* cx)
{
  char errmsg[1024];
//#define ReportError1(msg,arg)
// sprintf(errmsg,"ActiveX:  msg,arg);
// JS_ReportError(cx,STRING_TO_JSVAL(JS_NewStringCopyZ(cx,errmsg )));

#define ReportError1(msg,arg) \
 {sprintf(errmsg,msg,arg); \
 JS_SetPendingException(cx,STRING_TO_JSVAL(JS_NewStringCopyZ(cx,TStr("ActiveX:", errmsg))));}

#define ReportError(msg) \
 JS_SetPendingException(cx,STRING_TO_JSVAL(JS_NewStringCopyZ(cx,TStr("ActiveX:", msg))))

//  #define ReportError1(msg,arg) JS_ReportError(cx,"ActiveX: " msg,arg);
//#define ReportError(msg) JS_ReportError(cx,"ActiveX: " msg);
// JS_SetPendingException(cx,STRING_TO_JSVAL(JS_NewStringCopyZ(cx,errmsg )));

  switch(hresult)
  {
  case DISP_E_BADPARAMCOUNT: ReportError("Wrong number of parameters"); break;
  case DISP_E_BADVARTYPE: ReportError1("Bad variable type %d",argerr); break;
  case DISP_E_EXCEPTION: if (exception.bstrDescription)
                         {
                          WStr w1((uint16*)exception.bstrDescription,SysStringLen(exception.bstrDescription));
//                          TStr d(w1);

                          WStr w2((uint16*)exception.bstrSource,SysStringLen(exception.bstrSource));
//                          TStr s(w2);
                          WStr w(w1.length() + w2.length() + 20);
                          swprintf(w,L"ActiveX: (%s) %s",(wchar_t*)w2,(wchar_t*)w1);
//                          TStr err(w);
//                          printf("%s\n",(char*)err);

                          JS_SetPendingException(cx,STRING_TO_JSVAL(JS_NewUCStringCopyN(cx,w,w.length())));
//                          char* err = DeflateString((jschar*)exception.bstrDescription,SysStringLen(exception.bstrDescription));
//                          ReportError(err);
//                          ReportError(cx,"%s",TStr("Activex: ",err));
//                          JS_ReportError(cx,"%s",(char*)TStr(err));
//                          free(err);
                         }
                         else
                         {
                           ReportError1("Error code %d",exception.scode);
                         }
                         SysFreeString(exception.bstrSource);
                         SysFreeString(exception.bstrDescription);
                         SysFreeString(exception.bstrHelpFile);
                         break;
  case DISP_E_MEMBERNOTFOUND: ReportError("Function not found"); break;
  case DISP_E_OVERFLOW: ReportError1("Can not convert variable %d",argerr); break;
  case DISP_E_PARAMNOTFOUND: ReportError1("Parameter %d not found",argerr); break;
  case DISP_E_TYPEMISMATCH: ReportError1("Parameter %d type mismatch",argerr); break;
  case DISP_E_UNKNOWNINTERFACE: ReportError("Unknown interface"); break;
  case DISP_E_UNKNOWNLCID: ReportError("Unknown LCID"); break;
  case DISP_E_PARAMNOTOPTIONAL: ReportError1("Parameter %d is required",argerr);
  }
}

bool ActiveX::Id(const uint16 * name,DISPID& dispid)
{
 if (!dispatch) return false;

 dispid = NULL;
 HRESULT hresult;

 if (name) if (name[0] == L'0') {dispid=0; return true;}

 hresult = dispatch->GetIDsOfNames(IID_NULL, (WCHAR**)&name, 1, LOCALE_USER_DEFAULT, &dispid);

 if (!SUCCEEDED(hresult)) return false;

 return true;
}

bool ActiveX::Invoke(DISPID dispid, JSContext *cx, uintN argc, jsval *argv, jsval* rval)
{
 if (!dispatch) return false;

 VARIANT VarResult;
 VARIANTARG * args;
 DISPPARAMS dispparams = {NULL,NULL,0,0};
 HRESULT hresult;
 EXCEPINFO exception={0};
 UINT argerr=0;

 if (argc)
 {
   args = new VARIANTARG[argc];
   dispparams.rgvarg = args;
   dispparams.cArgs = argc;
   for (size_t i=0; i<argc; i++)
   {
    if (!SetupValue(args[argc-i-1],cx,argv+i))
     {
      args[argc-i-1].vt=VT_ERROR;
      args[argc-i-1].scode = 0;
     }
   }
 }

 hresult = dispatch->Invoke(
      dispid,
      IID_NULL,
      LOCALE_USER_DEFAULT,
      DISPATCH_METHOD,
      &dispparams, &VarResult, &exception, &argerr);

 CheckReturn(cx,rval);
 for (size_t i=0; i<argc; i++)
    CheckReturn(cx,argv+i);

 if (argc) delete[] args;

 if (!SUCCEEDED(hresult))
 {
  *rval = NULL;
  ActiveXError(hresult,exception,argerr,cx);
  return false;
 }

 if (!RetrieveValue(VarResult, cx, rval))
  {
   *rval = NULL;
  }

 return true;
}

bool ActiveX::Set(DISPID dispid, JSContext *cx, uintN argc, jsval *argv, jsval *rval,bool byref)
{
 VARIANTARG * args = new VARIANTARG[argc+1];
 DISPID dispput = DISPID_PROPERTYPUT;
 DISPPARAMS dispparams = {args,&dispput,argc+1,1};
 HRESULT hresult;
 EXCEPINFO exception={0};
 UINT argerr=0;

 //the set value
 if (!SetupValue(args[0],cx,rval)) return false;

 //the index values, in reverse order
 if (argc)
 {
//   dispparams.rgvarg = args;
//   dispparams.cArgs = argc+1;
   for (size_t i=0; i<argc; i++)
   {
    if (!SetupValue(args[argc-i],cx,argv+i))
     {
      args[argc-i].vt=VT_ERROR;
      args[argc-i].scode = 0;
     }
   }
 }

 DWORD flag = DISPATCH_PROPERTYPUT;
 if (byref && (args[0].vt & VT_DISPATCH || args[0].vt & VT_UNKNOWN))
 {//must be passed by name
  flag = DISPATCH_PROPERTYPUTREF;
 }

 hresult = dispatch->Invoke(
      dispid,
      IID_NULL,
      LOCALE_USER_DEFAULT,
      flag,
      &dispparams, NULL, &exception, &argerr);

 CheckReturn(cx,rval);
 for (size_t i=0; i<argc; i++)
    CheckReturn(cx,argv+i);

 delete [] args;

  if (!SUCCEEDED(hresult))
  {
     ActiveXError(hresult,exception,argerr,cx);
     return false;
  }

  return true;
}

bool ActiveX::Get(DISPID dispid, JSContext *cx, uintN argc, jsval *argv,  jsval *rval)
{
 if (!dispatch) return false;
 VARIANT VarResult;
 VARIANTARG * args;
 DISPPARAMS dispparams = {NULL,NULL,0,0};
 HRESULT hresult;
 EXCEPINFO exception={0};
 UINT argerr=0;

 if (argc)
 {
   args = new VARIANTARG[argc];
   dispparams.rgvarg = args;
   dispparams.cArgs = argc;
   for (size_t i=0; i<argc; i++)
   {
    if (!SetupValue(args[argc-i-1],cx,argv+i))
     {
      args[argc-i-1].vt=VT_ERROR;
      args[argc-i-1].scode = 0;
     }
   }
 }

 hresult = dispatch->Invoke(
      dispid,
      IID_NULL,
      LOCALE_USER_DEFAULT,
      DISPATCH_PROPERTYGET,
      &dispparams, &VarResult, &exception, &argerr);

 CheckReturn(cx,rval);
 for (size_t i=0; i<argc; i++)
    CheckReturn(cx,argv+i);

 if (argc) delete[] args;

 if (!SUCCEEDED(hresult))
 {
  *rval = NULL;
  ActiveXError(hresult,exception,argerr,cx);
  return false;
 }
 else if (!RetrieveValue(VarResult, cx, rval))
  {
   *rval = NULL;
   JS_ReportError(cx,"Invalid return type %d",VarResult.vt);
  }
 return true;
}

ActiveX::ActiveX()
{
 unknown = NULL;
 typeinfo = NULL;
 dispatch = NULL;
}

ActiveX::ActiveX(IDispatch *obj)
{
 unknown = NULL;
 typeinfo = NULL;

 dispatch = obj;
}

ActiveX::ActiveX(IUnknown* obj)
{
 dispatch = NULL;
 typeinfo = NULL;

 unknown = obj;

 HRESULT hresult;

 hresult = unknown->QueryInterface(IID_IDispatch, (void * *)&dispatch);

 if (!SUCCEEDED(hresult)) {dispatch = 0; }
}

ActiveX::ActiveX(CLSID& clsid)
{
 HRESULT hresult;
 unknown = NULL;
 dispatch = NULL;
 typeinfo = NULL;

 hresult = CoCreateInstance(clsid, NULL, CLSCTX_SERVER|CLSCTX_INPROC_HANDLER,
            IID_IUnknown, (void **)&unknown);

 if (!SUCCEEDED(hresult)) {unknown = 0; return;} //throw xdb("CoCreateInstance Failure");

 hresult = unknown->QueryInterface(IID_IDispatch, (void * *)&dispatch);

 //maybe I don't know what to do with it, but it might get passed to
 //another COM function
 if (!SUCCEEDED(hresult))
 {dispatch = NULL;
  //unknown->Release();
  //unknown=NULL;
  //throw xdb("IDispatch interface not found");
 }
}

ActiveX::~ActiveX()
{
 if (dispatch) dispatch->Release();
 if (unknown) unknown->Release();
 if (typeinfo) typeinfo->Release();
 CoFreeUnusedLibraries();
}

static JSBool
ActiveX_Run(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(ActiveX,t);
 JSString* s = JS_GetFunctionId(JS_ValueToFunction(cx, argv[-2]));
 if (s)
  { jschar* name = JS_GetStringChars(s);
    if (!name)
    {
     ERR_MSG(ActiveX,Exec,"No function name");
    }

    DISPID dispid;
    if (!t->Id((uint16*)name,dispid))
    {
      ERR_MSG(ActiveX,"This object does not have that function",TStr(name));
    }
    if (!t->Invoke(dispid, cx, argc, argv, rval))
    {
     return JS_FALSE;
//      RETOBJ(0);
      //JavaScript handles the exception with SetPendingException
//      ERR_MSG(ActiveX,"IDispatch->Invoke failed",TStr((WCHAR*)name));
    }
  }
  return JS_TRUE;
}


static JSBool
ActiveX_Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(ActiveX,t);
 if (argc < 1) ERR_COUNT(ActiveX,Exec);
 if (!ISSTR(0)) ERR_TYPE(ActiveX,Exec,1,string);
 JSString* s = JSVAL_TO_STRING(argv[0]);
 if (s)
  { jschar* name = JS_GetStringChars(s);
    if (!name)
    {
      ERR_MSG(ActiveX,Exec,"No function name");
    }
    DISPID dispid;
    if (!t->Id(name,dispid))
    {
      ERR_MSG(ActiveX,"This object does not have that function",TStr(name));
    }
    if (!t->Invoke(dispid, cx, argc-1, argv+1, rval))
    {
      return JS_FALSE;
//      ERR_MSG(ActiveX,"IDispatch->Invoke failed",TStr(name));
    }
  }
  return JS_TRUE;
}

///Get("property","index","index")
static JSBool
ActiveX_ToString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 GETOBJ(ActiveX,t);

 DISPID dispid = 0;

 if (t->Id((uint16*)L"toString",dispid))
 {
    t->Invoke(dispid, cx, argc, argv, rval);
    return JS_TRUE;
 }

 if (!t->Get(dispid, cx, 0, 0, rval))
    RETSTR("");

 return JS_TRUE;
}

static JSBool
ActiveX_Get(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc == 0) ERR_COUNT(ActiveX,Get);
 if (!ISSTR(0) && !ISINT(0)) ERR_TYPE(ActiveX,Get,1,String);

 GETOBJ(ActiveX,t);

 DISPID dispid = 0;
 if (ISSTR(0))
  {
  JSString* s = JSVAL_TO_STRING(argv[0]);

 if (s)
  { jschar* name = JS_GetStringChars(s);
    if (!name)
    {
      ERR_MSG(ActiveX,Exec,"No property name");
    }
    if (!t->Id(name,dispid))
    {
      ERR_MSG(ActiveX,"This object does not have that property",TStr(name));
  } }
  }

  if (!t->Get(dispid, cx, argc-1, argv+1, rval))
  {
  //    ERR_MSG(ActiveX,"IDispatch->Invoke failed","");
    return JS_FALSE;
  }
  return JS_TRUE;
}

///Set("property","index","index","value")
static JSBool
ActiveX_Set(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
 if (argc < 2) ERR_COUNT(ActiveX,Set);
 if (!ISSTR(0)) ERR_TYPE(ActiveX,Set,1,String);

 GETOBJ(ActiveX,t);

 JSString* s = JSVAL_TO_STRING(argv[0]);
 if (s)
  { jschar* name = JS_GetStringChars(s);
    if (!name)
    {
      ERR_MSG(ActiveX,Exec,"No property name");
    }

    DISPID dispid;
    if (!t->Id(name,dispid))
    {
      ERR_MSG(ActiveX,"This object does not have that property",TStr(name));
    }

    ActiveX::PropInfo *p = t->Find(name);
    RETBOOL(t->Set(dispid, cx, argc-2, argv+1, argv+argc-1, p?p->PutRef:false));
  }
 RETOBJ(0);
}

bool ActiveX::SetupMembers(JSContext* cx, JSObject* obj)
{
 if (!dispatch) return false;
 HRESULT hresult;
 ENTERNATIVE(cx);

 JSObject * doc = ROOT(JS_NewObject(cx,NULL,NULL,obj));
 JS_DefineProperty(cx, obj,"members",OBJECT_TO_JSVAL(doc),0,0,JSPROP_ENUMERATE);

 if (!typeinfo)
 {
  unsigned ctinfo;
  hresult = dispatch->GetTypeInfoCount(&ctinfo);
  if (SUCCEEDED(hresult))
   if (ctinfo)
    dispatch->GetTypeInfo(0,0,&typeinfo);
 }

 if (!typeinfo) return false;

 size_t i;
 VARDESC * vardesc;
 for (i=0; typeinfo->GetVarDesc(i, &vardesc) == S_OK && i < 255; i++)
 {
  BSTR name = NULL;
  BSTR desc = NULL;
  if (typeinfo->GetDocumentation(vardesc->memid, &name, &desc, NULL, NULL) == S_OK)
   {
    PropInfo * p = Find((uint16*)name);
    if (!p) p = new ActiveX::PropInfo((uint16*)name);
    p->Get = p->Put = true;
    unsigned prop = Properties.Add(p);

    JS_DefineUCPropertyWithTinyId(cx,obj,(jschar *)name,
            SysStringLen(name), (int8)(prop), NULL,
            ActiveX_JSGet, ActiveX_JSSet, JSPROP_ENUMERATE);

    if (doc)
     {
      jsval d = desc ? STRING_TO_JSVAL(ROOT(JS_NewUCStringCopyN(cx,(jschar*)desc, SysStringLen(desc)))) : NULL;
      JS_DefineUCProperty(cx, doc, (jschar*)name, SysStringLen(name),d,NULL,NULL,JSPROP_ENUMERATE);
     }
    SysFreeString(name);
    SysFreeString(desc);
   }
  typeinfo->ReleaseVarDesc(vardesc);
 }

 FUNCDESC * funcdesc;
 for (i=0; typeinfo->GetFuncDesc(i, &funcdesc) == S_OK; i++)
 {
  BSTR name = NULL;
  BSTR desc = NULL;

  if (typeinfo->GetDocumentation(funcdesc->memid, &name, &desc, NULL, NULL) == S_OK)
   {
    char* fname = DeflateString((jschar*)name,SysStringLen(name));

    if (funcdesc->invkind == INVOKE_FUNC)
    {
       JS_DefineFunction(cx,obj,fname,*ActiveX_Run,funcdesc->cParams,0);
    }
    else
    {
      PropInfo * p = Find((uint16*)name);

      if (!p)
      {
       p = new PropInfo((uint16*)name);
       unsigned prop = Properties.Add(p);
       JS_DefineUCPropertyWithTinyId(cx,obj,(jschar *)name,
                                     SysStringLen(name), (int8)(prop), NULL,
                                     ActiveX_JSGet, ActiveX_JSSet, JSPROP_ENUMERATE);
      }

      if (funcdesc->invkind & INVOKE_PROPERTYGET)
        p->Get = true;
      if (funcdesc->invkind & INVOKE_PROPERTYPUT)
        p->Put = true;
      if (funcdesc->invkind & INVOKE_PROPERTYPUTREF)
        p->PutRef = true;
    }
    free(fname);

    if (doc)
     {
      jsval d = desc ? STRING_TO_JSVAL(ROOT(JS_NewUCStringCopyN(cx,(jschar*)desc, SysStringLen(desc)))) : NULL;
      JS_DefineUCProperty(cx, doc, (jschar*)name, SysStringLen(name),
       d,NULL,NULL,JSPROP_ENUMERATE);
     }

    SysFreeString(name);
    SysFreeString(desc);
   }
  typeinfo->ReleaseFuncDesc(funcdesc);
 }
 return true;
}

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

void ActiveX_JSFinalize(JSContext *cx, JSObject *obj)
{
 DELPRIVATE(ActiveX);
}

WRAP_HELP(ActiveX,
 "name(index)\nextract(index)\nextract(index,string)\nsize(index)\n"
 "close()\n")

static JSPropertySpec ActiveX_properties[] = {
    {"className",255, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,ActiveX_JSGet},
    {0}
};

static JSFunctionSpec ActiveX_functions[] = {
    {"get",     ActiveX_Get,      1},
    {"set",    ActiveX_Set, 2},
    {"exec",    ActiveX_Exec, 2},
    {"at",    ActiveX_Exec, 2},
    {"close",ActiveX_Close,0},
    {"toString",ActiveX_ToString,0},
    {0}
};

static JSFunctionSpec ActiveX_fnstatic[] = {
    {"help",  ActiveX_HELP,    0},
    {0}
};

static JSClass ActiveX_class = {
    "ActiveX", JSCLASS_HAS_PRIVATE,         //ActiveX_JSGet
    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub,   JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ActiveX_JSFinalize
};

JSObject*
ActiveX_Object(JSContext *cx, ActiveX* t,bool autodelete,JSPointerBase* Parent)
{
 JSObject* obj;
 GETENV;
 ENTERNATIVE(cx);
 MAKENEW(ActiveX);
 /*obj = JS_NewObject(cx, ActiveX_Class(),Env->ActiveX, NULL);
 JS_DefineFunctions(cx,obj,ActiveX_functions);
 JS_DefineProperties(cx,obj,ActiveX_properties);   */
 if (t)
 {
  SETPRIVATE(obj,ActiveX,t,autodelete,Parent);
  t->SetupMembers(cx,obj);
 }
 return obj;
}

JSClass* ActiveX_Class() {return &ActiveX_class;}

void ActiveX_InitClass(JSContext *cx, JSObject *obj)
{
 GETENV;
 INITCLASS(ActiveX);
}

/*
CoInitialize(NULL);
ActiveX_InitClass(cx,obj);
CoFreeUnusedLibraries();
CoUninitialize();
*/

#endif
