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

#ifdef XP_UNIX

#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
#endif

#ifdef XP_WIN
BOOL CALLBACK closewindowsbypid_callback(HWND hwnd, LPARAM match)
{
   DWORD pid;
   GetWindowThreadProcessId(hwnd,&pid);
   if (pid==(DWORD)match)
      PostMessage(hwnd,WM_CLOSE,0,0);
   return 1;
}

class Process
{public:
 STARTUPINFO si;
 PROCESS_INFORMATION pi;
 HANDLE handle;
 DWORD PID;

 bool isActive()
 {
  DWORD exitcode;
  return GetExitCodeProcess(handle,&exitcode) ? (exitcode==STILL_ACTIVE) : false;
 }

 void Close();
 Process(char* cmd, char* args);
 ~Process();
};

Process::Process(char* cmd, char* args)
 {
  handle = INVALID_HANDLE_VALUE;
  PID = 0;

   memset(&si,0,sizeof(STARTUPINFO));
   si.cb=sizeof(STARTUPINFO);
   si.dwFlags=STARTF_USESHOWWINDOW;
   si.wShowWindow=SW_SHOWNORMAL;
   if (CreateProcess(cmd,args,NULL,NULL,
                      FALSE,DETACHED_PROCESS,NULL,NULL,&si,&pi))
   {  /* WARNING, if an error occurs, clean this up */
       handle = pi.hProcess;
      PID = pi.dwProcessId;
       CloseHandle(pi.hThread);
   }
 }

 void Process::Close()
 {
  if (isActive() && PID)
      EnumWindows(closewindowsbypid_callback,(LPARAM)PID);
 }

 Process::~Process()
 {
  CloseHandle(handle);
 }

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

 JS_SetPrivate(cx,obj,NULL);
}

WRAP(Process,Close)
{
 {
  GETOBJ(Process,t);
  if (t) t->Close();
 }
 CLOSEPRIVATE(Process);
 RETBOOL(true);
}

JSBool
Process_Process(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
    jsval *rval)
{
 if (argc == 0)
   ERR_COUNT(Process,Process);

 GETENV;
 if (!Env) return JS_FALSE;

 if (Env->SafeMode) return JS_FALSE;

 GETUTF8(0);
 GETUTF8(1);
 Process* dt = 0;
 if (!s0)
  ERR_COUNT(Process,Process)

  if (*s1)
   dt = new Process(s0,s1);
  else
   dt = new Process(NULL,s0);

  if (!dt || dt->handle == INVALID_HANDLE_VALUE && *s1)
  {
   if (dt) delete dt;
   dt = 0;
//   TStr t0(s0);
//   TStr t1(s1);
    dt = new Process(NULL,TStr(s0," ",s1));
  }
  if (!dt || dt->handle == INVALID_HANDLE_VALUE)
  {
   if (dt) delete dt;
   dt = 0;
   char s[MAXPATH];
   if ((int)FindExecutable(s0,0,s) > 32)
    dt = new Process(s,s0);
  }
  if (!dt || dt->handle == INVALID_HANDLE_VALUE)
  {
   if (dt) delete dt;
   dt = 0;
   TStr s;
   if (strstr(s0,"://"))
   {
    TStr url(s0);
    *strstr(url,"://") = 0;
    RegGetKey(TStr(url,"\\shell\\open\\command"),"",s,HKEY_CLASSES_ROOT);

    if (*s)
    {
     s.replace("%1",s0);
      dt = new Process(NULL,s);
    }
   }
  }
  if (!dt || dt->handle == INVALID_HANDLE_VALUE)
  {
   if (dt) delete dt;
   dt = 0;
  }

 if (dt)
  SETPRIVATE(obj,Process,dt,true,NULL);

 return JS_TRUE;
}

static JSBool
Process_JSGet(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
{
 GETOBJ(Process,t);

 int x = JSVAL_TO_INT(id);

 if (JSVAL_IS_INT(id))
  switch (x)
  {
   case 0: RETBOOL(t->isActive());
   case 3: RETSTR("Process");
   default: return JS_FALSE;
  }
 else return JS_FALSE;
}

static JSPropertySpec Process_properties[] = {
    {"active",0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Process_JSGet},
    {"className",3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Process_JSGet},
    {0}
};

static JSFunctionSpec Process_fnstatic[] = {
    {0}
};

static JSFunctionSpec Process_functions[] = {
    {"close", Process_Close,0},
    {0}
};

static JSClass Process_class = {
    "Process", JSCLASS_HAS_PRIVATE,         //Process_JSGet
    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub,   JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,Process_JSFinalize
};

JSObject*
Process_Object(JSContext *cx, Process* t,bool autodelete,JSPointerBase* Parent)
{
 JSObject* obj;
 GETENV;
 ENTERNATIVE(cx);
 MAKENEW(Process);
 SETPRIVATE(obj,Process,t,autodelete,Parent);

 return obj;
}

JSClass* Process_Class() {return &Process_class;}

void Process_InitClass(JSContext *cx, JSObject *obj)
{
 GETENV;
 INITCLASS(Process);
}

#endif

#ifdef XP_UNIX
class Process
{public:
 int RET;

 Process(const char* cmd);
 int pfork();
 int pwait();
 int pgetpid();
 ~Process();
};

Process::Process(const char* cmd)
 {
  RET = 0;
  if(strlen(cmd)>0) {
    RET=system(cmd);
  } else {
       RET=0;
  }
}

int Process::pfork()
 {
 RET=fork();
  return RET;
 }

int Process::pwait()
 {
  return wait(0);
 }

int Process::pgetpid()
 {
  return getpid();
 }

 Process::~Process()
 {

 }

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

 JS_SetPrivate(cx,obj,NULL);
}

WRAP(Process,Close)
{
// GETOBJ(Process,t);
 CLOSEPRIVATE(Process);
 RETBOOL(true);
}

JSBool
Process_Process(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
    jsval *rval)
{

 GETENV;
 if (!Env) return JS_FALSE;

 if (Env->SafeMode) return JS_FALSE;
 GETUTF8(0);

 if (argc > 0) {
    if (!ISSTR(0)) ERR_TYPE(js,Process,1,String);
 }
   //ERR_COUNT(Process,Process);


 Process* dt = 0;
   if (argc>0)
   {
    dt = new Process(s0);
    return JS_FALSE;
   } else {
    dt = new Process("");
   }

  if (dt<0)
  {
    return JS_FALSE;
  }
  else {
  SETPRIVATE(obj,Process,dt,true,NULL);
  return JS_TRUE;
  }
}

JSBool
Process_pfork(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
    jsval *rval)
{
// GETENV;
 GETOBJ(Process,p);
 RETINT(p->pfork());
}

JSBool
Process_pwait(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
    jsval *rval)
{
 GETOBJ(Process,p);
 if(p->pwait()==-1) { perror("WAIT");  RETBOOL(false); }
 else RETBOOL(true);
}

JSBool
Process_pgetpid(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
    jsval *rval)
{
 GETOBJ(Process,p);
 RETINT(p->pgetpid());
}

static JSBool
Process_JSGet(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
{
 GETOBJ(Process,t);

 int x = JSVAL_TO_INT(id);

 if (JSVAL_IS_INT(id))
  switch (x)
  {
   case 3: RETSTR("Process");
   default: return JS_FALSE;
  }
 else return JS_FALSE;
}

static JSPropertySpec Process_properties[] = {
    {"className",3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT,Process_JSGet},
    {0}
};

static JSFunctionSpec Process_fnstatic[] = {
    {0}
};

static JSFunctionSpec Process_functions[] = {
    {"close", Process_Close,0},
    {"fork",     Process_pfork,      0},
    {"wait",     Process_pwait,      0},
    {"getpid",     Process_pgetpid,      0},
    {0}
};

static JSClass Process_class = {
    "Process", JSCLASS_HAS_PRIVATE,         //Process_JSGet
    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub,   JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,Process_JSFinalize
};

JSObject*
Process_Object(JSContext *cx, Process* t,bool autodelete,JSPointerBase* Parent)
{
 JSObject* obj;
 GETENV;
 ENTERNATIVE(cx);
 MAKENEW(Process);
 SETPRIVATE(obj,Process,t,autodelete,Parent);

 return obj;
}

JSClass* Process_Class() {return &Process_class;}

void Process_InitClass(JSContext *cx, JSObject *obj)
{
 GETENV;
 INITCLASS(Process);
}
#endif
