//
// Copyright (C) 1999-2004 Toshikaz Hirabayashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// TOSHIKAZ HIRABAYASHI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Except as contained in this notice, the name of Toshikaz Hirabayashi shall
// not be used in advertising or otherwise to promote the sale, use or other
// dealings in this Software without prior written authorization from
// Toshikaz Hirabayashi.

//#include <x11/WSDxappDev.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <btron/bsocket.h>

#ifdef TE
#include <btron/unixemu.h>
#else
extern "C" {
extern int btron_prefork( void );
extern pid_t btron_forkexecvp( const char *name, char * const *argv );

};
#endif
#include <btron/proctask.h>
#include <btron/taskcomm.h>
#include <basic.h>

#include <WScom.h>
#include <WSDfile.h>
#include <btron/WSDbtronExecute.h>
#include <WSCbaseList.h>
#include <WSDfileSystem.h>
#include <WSClocaleSet.h>

//#define PDEBUG

WSMFclassInit(WSDbtronExecute,WSDexecute);

WSDexecute* _unix_execute_chandler(){
  return new WSDbtronExecute;
}
class _unix_execute_init {
  public: _unix_execute_init(){
    WSDexecute::setCreateInstanceHandler((void*)_unix_execute_chandler);
  };
};
static _unix_execute_init  _unix_execute_init_execute;

WSDbtronExecute::WSDbtronExecute(){
//  _xid = 0;
  _handle = (void*)NULL;
  _handle1 = (void*)-1;
  _handle2 = (void*)-1;
  _handle3 = (void*)-1;
  _id = 0;
  _sem = cre_sem(1,SEM_SYNC|DELEXIT );
}
WSDbtronExecute::~WSDbtronExecute(){
  if (_pid != 0){
    killProcess();
  }

  if (_handle != NULL){
    close((int)_handle2);
    _handle2 = (void*)-1;
    close((int)_handle3);
    _handle3 = (void*)-1;
    return;
  }
  if (_sem != NULL){
//printf("WSDbtronExecute::~WSDbtronExecute() _sem = NULL\n");
    del_sem(_sem);
    _sem = NULL;
  }

}
void WSDbtronExecute::_callback(WSDbtronExecute* obj){
//printf("WSDbtronExecute::_callback called\n");
  if (obj->_data_receive != NULL){
    obj->_data_receive(obj);
//printf("WSDbtronExecute::_callback unlock _sem = 0x%x\n",sem);
    WSGIappObjectList()->execUpdate();
    return;
  }
//printf("WSDbtronExecute::_callback _sem = 0x%x\n",sem);
}

#define READ_PORT 0
#define WRITE_PORT 1

long WSDbtronExecute::execute(char* exename,long mode){
  killProcess();

  if (_handle != (void*)NULL){
    _handle = (void*)NULL;
    if (_handle1 != (void*)-1){
      close((int)_handle1);
      _handle1 = (void*)-1;
    }
    if (_handle2 != (void*)-1){
      close((int)_handle2);
      _handle2 = (void*)-1;
    }
    if (_handle3 != (void*)-1){
      close((int)_handle3);
      _handle3 = (void*)-1;
    }
  }
//  if (_handle3 != -1){
//    close((int)_handle3);
//  }

  wai_sem(_sem,T_FOREVER);
  _string = "";
  _pos = 0; 
  sig_sem(_sem);

  if (exename == NULL){
    return WS_ERR;
  }
#ifdef PDEBUG
  printf("WSDbtronExecute::execute #%s#\n",exename);
#endif
  _exe_name = WSGIappFileSystem()->adjustFileName(exename);
  _exe_name.delHeadSpace();
  char* exe_name = _exe_name.getString(WSGIappLocaleSet()->getSystemLocaleEncoding());
  char* dir_name = _dir_name.getString(WSGIappLocaleSet()->getSystemLocaleEncoding());

#ifdef PDEBUG
  printf("WSDbtronExecute::execute chdir=#%s#\n",(char*)dir_name);
#endif
  if (strcmp((char*)dir_name,"")){
    int ret = chdir((char*)dir_name);
    if (ret != 0){
#ifdef PDEBUG
  printf("WSDbtronExecute::execute chdir err.\n");
#endif
      return WS_ERR;
    }
  }
//  if (mode == WS_EXEC_NONE){
//printf("WSDbtronExecute::execute start system.\n");
//    system(exe_name);
//printf("WSDbtronExecute::execute done system.\n");
//    return WS_NO_ERR;
//  }

  int pipes_stdin[2];
  int pipes_stdout[2];
  int pipes_stderr[2];

#ifdef PDEBUG
  printf("WSDbtronExecute::execute mode=%d\n",mode);
#endif

  if (mode == WS_EXEC_RDWR || mode == WS_EXEC_WR){
    if(pipe(pipes_stdin)){
      return WS_ERR;
    }
  }
  if (mode == WS_EXEC_RDWR || mode == WS_EXEC_RD){
    if(pipe(pipes_stdout)){
      return WS_ERR;
    }
    if(pipe(pipes_stderr)){
      return WS_ERR;
    }
  }

  WSCstring t1(exe_name);
  long num = _exe_name.getWords();
//printf("num=%d\n",num); 
  char** args = new char*[num+1];
  long i;
  for(i=0; i<num; i++){
    args[i] = WSGFstrdup((char*)t1.getWord(i));
//printf("args[%d]=%s\n",i,args[i]);
  }
  args[num] = NULL;
#if 0
  long fstat = WSGIappFileSystem()->check(args[0]);
  if (!(fstat & WS_FS_FILE)){
    return WS_ERR;
  }
#endif
  if (btron_prefork() < 0) {
#ifdef PDEBUG
  printf("btron_prefork() < 0..\n");
#endif
    return WS_ERR;
  }

  if (mode == WS_EXEC_RDWR || mode == WS_EXEC_WR){
    close(STDIN_FILENO);
    dup(pipes_stdin[READ_PORT]);
    close(pipes_stdin[READ_PORT]);
    close(pipes_stdin[WRITE_PORT]);
  }

  if (mode == WS_EXEC_RDWR || mode == WS_EXEC_RD){
    close(STDOUT_FILENO);
    dup(pipes_stdout[WRITE_PORT]);
    close(pipes_stdout[WRITE_PORT]);
    close(pipes_stdout[READ_PORT]);

    close(STDERR_FILENO);
    dup(pipes_stderr[WRITE_PORT]);
    close(pipes_stderr[WRITE_PORT]);
    close(pipes_stderr[READ_PORT]);
  }
#if 0
  close(STDIN_FILENO);
  dup(pipes_stdin[READ_PORT]);
  close(pipes_stdin[READ_PORT]);
  close(pipes_stdin[WRITE_PORT]);
#endif 

  int err = btron_forkexecvp( args[0], args);
//printf("cmd=%s child pid=%d\n",args[0],err); 
//printf("this=0x%x\n",this);

  for(i=0; i<num; i++){
    delete args[i];
//printf("args[%d]=%s\n",i,args[i]);
  }
  delete args;


  _mode = mode;
  if (err < 0) {
    printf("ERROR:btron_forkexecvp()=0x%08x, errno=%d\n", err, errno);
    return WS_ERR;
  }
  _pid = err;

//  int stat;
//  err = wait(&stat);
//  if (err == -1) {
//    printf( "ERROR:read(pipes_stdout)\n");
//    return WS_ERR;
//  }

  _handle = (void*)1;
  if (mode == WS_EXEC_RDWR || mode == WS_EXEC_WR){
    _handle1 = (void*)pipes_stdin[WRITE_PORT];
  }else{
    _handle1 = (void*)-1;
  }
  if (mode == WS_EXEC_RDWR || mode == WS_EXEC_RD ){
    _handle2 = (void*)pipes_stdout[READ_PORT];
    _handle3 = (void*)pipes_stderr[READ_PORT];
  }else{
    _handle2 = (void*)-1;
    _handle3 = (void*)-1;
  }

#ifdef PDEBUG
  printf("create here1 handle1=%d\n",_handle1);
  printf("create here1 handle2=%d\n",_handle2);
  printf("create here1 handle3=%d\n",_handle3);
#endif
  if (mode != WS_EXEC_NONE && mode != WS_EXEC_WR && _data_receive){
    int ret = cre_tsk(_watch_pid_,-1,(int)this);
    if (ret < 0){
//printf("return WS_ERR\n");
      return WS_ERR;
    }
    _tsk1 = ret;


//printf("WSDbtronExecute::execute tid=%d\n",get_tid());
    ret = cre_tsk(_read_thread_,-1,(int)this);
    if (ret < 0){
//printf("return WS_ERR\n");
      return WS_ERR;
    }
    _tsk2 = ret;
    ret = cre_tsk(_read_thread_2,-1,(int)this);
    if (ret < 0){
//printf("return WS_ERR\n");
      return WS_ERR;
    }
    _tsk3 = ret;
  }
//printf("return WS_NO_ERR\n");
  return WS_NO_ERR;
}
long WSDbtronExecute::killProcess(){
  if (_handle != (void*)NULL){
    _handle = (void*)NULL;
    if (_handle1 != (void*)-1){
      close((int)_handle1);
      _handle1 = (void*)-1;
    }
    if (_handle2 != (void*)-1){
      close((int)_handle2);
      _handle2 = (void*)-1;
    }
    if (_handle3 != (void*)-1){
      close((int)_handle3);
      _handle3 = (void*)-1;
    }
  }
  if (_pid == 0){
    _id++;
    return WS_NO_ERR;
  }
//TEST
//  kill(_pid,SIGKILL);
  if (_pid != 0){
    ter_prc(_pid,0,TERM_NRM);
    _pid = 0;
  }
  wai_sem(_sem,T_FOREVER);
  if (_tsk1 != 0){
    ter_tsk(_tsk1);
    _tsk1 = 0;
  }
  if (_tsk2 != 0){
    ter_tsk(_tsk2);
    _tsk2 = 0;
  }
  if (_tsk3 != 0){
    ter_tsk(_tsk3);
    _tsk3 = 0;
  }
  sig_sem(_sem);
  _pid = 0;
  _id++;

  return WS_NO_ERR;
}

long WSDbtronExecute::read(char* buf,long len){
//printf("WSDbtronExecute::read this=0x%x\n",this);
  if (len < 1){
    return 0;
  }
  if (_pid == 0){
    return -1;
  }
  if (_mode == WS_EXEC_RD || _mode == WS_EXEC_RDWR){
    wai_sem(_sem,T_FOREVER);
    char* str = _string.getString();
    long slen1 = strlen(str);
    long slen2 = _pos + len;
    if (slen1 < slen2){
      len = slen1 - _pos;
    }
    if (len < 1){
      return 0;
    }
    memcpy(buf,&str[_pos],len);
    _pos += len;
    sig_sem(_sem);

    return len;
  }
  return -1;
}
char* WSDbtronExecute::gets(char* buf,long len){
  if (len < 1){
    return 0;
  }
  if (_pid == 0){
#ifdef PDEBUG
  printf("WSDbtronExecute::gets here. this=0x%x return=0\n",this);
#endif
    return 0;
  }
  if (_mode == WS_EXEC_RD || _mode == WS_EXEC_RDWR){
    wai_sem(_sem,T_FOREVER);
    char* str = _string.getString();
    long slen1 = strlen(str);
    long slen2 = _pos + len;
    if (slen1 < slen2){
      len = slen1 - _pos;
    }
    if (len < 1){
      return 0;
    }
    int i=0;
    for(i=0; i< len-2; i++){
      buf[i] = str[_pos + i];
      if (buf[i] == '\n'){
        break;
      }
    }
    buf[i] = '\n';
    i++;
    buf[i] = 0;
    _pos += len;
    sig_sem(_sem);
#ifdef PDEBUG
  printf("gets #%s#..\n",buf);
#endif
    return buf;
  }
#ifdef PDEBUG
  printf("gets null2..\n");
#endif
  return 0;
}


long WSDbtronExecute::write(char* buf,long len){
  if (_pid == 0){
    return -1;
  }
  if (_mode == WS_EXEC_WR || _mode == WS_EXEC_RDWR){
    if (_handle1 != (void*)-1){
      return ::write((int)_handle1,buf,len);
    }
  }
  return -1;
}
void WSDbtronExecute::_watch_pid_(int ptr){
#ifdef PDEBUG
  printf("WSDbtronExecute::_watch_pid_.. start tid=%d\n",get_tid());
#endif
  WSDbtronExecute* _this = (WSDbtronExecute*)ptr;
  pid_t pid = _this->_pid;
  long id = _this->_id;
  if (pid > 0){
    int status;
    while(1){
      pid_t ret = waitpid(pid,&status,WNOHANG);
      if (ret == 0){
#ifdef PDEBUG
  printf("status=0x%x ret=%d eno=%d\n",status,ret,errno);
#endif
        wai_prc(100);
        continue;
      }
      break;
    }
    if (pid == _this->_pid && id == _this->_id){
      _this->_pid = 0;
    }
  }

#ifdef PDEBUG
  printf("close handles..\n");
#endif
  int h =(int)_this->_handle1;
  if (h != -1){
    close(h);
    _this->_handle1 = (void*)-1;
  }
  h =(int)_this->_handle2;
  if (h != -1){
    close(h);
    _this->_handle2 = (void*)-1;
  }
  h =(int)_this->_handle3;
  if (h != -1){
    close(h);
    _this->_handle3 = (void*)-1;
  }

  wai_sem(_this->_sem,T_FOREVER);
#ifdef PDEBUG
  printf("tsk1=%d tsk2=%d\n",_this->_tsk2,_this->_tsk3);
#endif
  if (_this->_tsk2 != 0){
    ter_tsk(_this->_tsk2);
    _this->_tsk2 = 0;
  }
  if (_this->_tsk3 != 0){
    ter_tsk(_this->_tsk3);
    _this->_tsk3 = 0;
  }
  sig_sem(_this->_sem);

  _read_event_send_type data;
  data.obj = _this;
  data.id = _this->_id;

  MESSAGE* msg;
  char buffer[128];
  msg = (MESSAGE*)buffer;
  msg->msg_type = MS_TYPE3;
  msg->msg_size = sizeof(data);
  memcpy(msg->msg_body.ANYMSG.msg_str,&data,sizeof(data));

  _this->_tsk1 = 0;
#ifdef PDEBUG
  printf("pid exited..\n");
#endif
  ext_tsk();
}

void WSDbtronExecute::_read_thread_(int ptr){
#ifdef PDEBUG
  printf("WSDbtronExecute::_read_thread_.. start tid=%d\n",get_tid());
#endif
  WSDbtronExecute* _this = (WSDbtronExecute*)ptr;
#ifdef PDEBUG
  printf("_this=0x%x\n",_this);
#endif
  void* handle = _this->_handle2;
  pid_t pid = _this->_pid;

//printf("here1\n");
  while(1){
    if (_this->_handle2 != handle){
      break;
    }
//printf("here2\n");
    if (_this->_pid == 0){
      break;
    }

    int status;
    pid_t ret2 = waitpid(pid,&status,WNOHANG);
    if (ret2 != 0){
      break;
    }

    char buffer[1024];
#ifdef PDEBUG
  printf("_read_thread start read..\n");
#endif
    int ret = ::read((int)_this->_handle2,buffer,1023);
#ifdef PDEBUG
  printf("_read_thread done read..\n");
#endif
    if (ret > 0){
      buffer[ret] = 0;
      wai_sem(_this->_sem,T_FOREVER);
      _this->_string << buffer;
      sig_sem(_this->_sem);
    }

    _read_event_send_type data;
    data.obj = _this;
    data.id = _this->_id;

    MESSAGE* msg;
    msg = (MESSAGE*)buffer;
    msg->msg_type = MS_TYPE3;
    msg->msg_size = sizeof(data);
    memcpy(msg->msg_body.ANYMSG.msg_str,&data,sizeof(data));
    snd_msg(0,msg,WAIT);

    if (ret < 1){
      break;
    }
  }
#ifdef PDEBUG
  printf("WSDbtronExecute::_read_thread return\n");
#endif
  _this->_tsk2 = 0;
  ext_tsk();
}
void WSDbtronExecute::_read_thread_2(int ptr){
#ifdef PDEBUG
  printf("WSDbtronExecute::_read_thread_.. start tid=%d\n",get_tid());
#endif
  WSDbtronExecute* _this = (WSDbtronExecute*)ptr;
#ifdef PDEBUG
  printf("_this=0x%x\n",_this);
#endif
  void* handle = _this->_handle3;
  pid_t pid = _this->_pid;

//printf("here1\n");
  while(1){
    if (_this->_handle3 != handle){
      break;
    }
//printf("here2\n");
    if (_this->_pid == 0){
      break;
    }
    int status;
    pid_t ret2 = waitpid(pid,&status,WNOHANG);
    if (ret2 != 0){
      break;
    }

    char buffer[1024];
#ifdef PDEBUG
  printf("_read_thread2 start read..\n");
#endif
    int ret = ::read((int)_this->_handle3,buffer,1023);
#ifdef PDEBUG
  printf("_read_thread2 start read..\n");
#endif
    if (ret > 0){
      buffer[ret] = 0;
      wai_sem(_this->_sem,T_FOREVER);
      _this->_string << buffer;
      sig_sem(_this->_sem);
    }

    _read_event_send_type data;
    data.obj = _this;
    data.id = _this->_id;

    MESSAGE* msg;
    msg = (MESSAGE*)buffer;
    msg->msg_type = MS_TYPE3;
    msg->msg_size = sizeof(data);
    memcpy(msg->msg_body.ANYMSG.msg_str,&data,sizeof(data));
    snd_msg(0,msg,WAIT);

    if (ret < 1){
      break;
    }
  }
#ifdef PDEBUG
  printf("WSDbtronExecute::_read_thread return\n");
#endif
  _this->_tsk3 = 0;
  ext_tsk();
}


long WSDbtronExecute::getId(){
  return _id;
}


