//
// Copyright (C) 1999-2002 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 <win/WSDwinAppDev.h>
#include <WScom.h>
#include <WSDfile.h>
#include <win/WSDwinExecute.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <WSCbaseList.h>
#include <WSDfileSystem.h>
#include <WSClocaleSet.h>

WSMFclassInit(WSDwinExecute,WSDexecute);

WSDexecute* _win_execute_chandler(){
  return new WSDwinExecute;
}
class _win_execute_init {
  public: _win_execute_init(){
    WSDexecute::setCreateInstanceHandler((void*)_win_execute_chandler);
  };
};
static _win_execute_init  _win_execute_init_execute;
WSDwinExecute::WSDwinExecute(){
  _thread_id = 0;
  _thread = 0;
  _thread2 = 0;
  InitializeCriticalSection(&_crs);
  _sem = 0;
  _read = 0;
  _write = 0;
  _data_len = 0;
  _buffer = 0;
}
WSDwinExecute::~WSDwinExecute(){
  if (_buffer != NULL){
    delete _buffer;
  }
  if (_pid != 0){
    killProcess();
  }
  if (_thread != NULL){
    TerminateThread(_thread,0);
    CloseHandle(_thread);
  }
  if (_thread2 != NULL){
    TerminateThread(_thread2,0);
    CloseHandle(_thread2);
    DeleteCriticalSection(&_crs);
  }
  if (_read != NULL){
    CloseHandle(_read);
    CloseHandle(_read2);
  }
  if (_write != NULL){
    CloseHandle(_write);
    CloseHandle(_write2);
  }
  if (_sem != 0){
    CloseHandle(_sem);
    _sem = 0;
  }
}
void WSDwinExecute::_callback(){
  if (_data_receive != NULL){
WSMFtrace("WSDwinExecute::_callback! this=0x%x\n",this);
    _data_receive(this);
    WSGIappObjectList()->execUpdate();
  }
WSMFtrace("WSDwinExecute::_callback! pid=%d\n",_pid);
  if (_pid == 0){
    killProcess();
  }
}
void WSDwinExecute::_check_thread(DWORD ptr){
  WSDwinExecute* obj = (WSDwinExecute*)ptr;
  WaitForInputIdle((HANDLE)obj->_pid,INFINITE);
  DWORD ret = STILL_ACTIVE;
  while(ret == STILL_ACTIVE){
    WaitForSingleObject((HANDLE)obj->_pid,1);
    GetExitCodeProcess((HANDLE)obj->_pid,&ret);
WSMFtrace("WSDwinExecute::_check_thread ret=%d  XXZZkk\n",ret);
    Sleep(50);
  }
  while(obj->_data_len > 0){
    Sleep(50);
  }
  if (obj->_thread != 0){
    TerminateThread(obj->_thread,0);
    CloseHandle(obj->_thread);
    obj->_thread = 0;
  }
WSMFtrace("WSDwinExecute::_check_thread send.\n",ret);
  obj->_data_len =0;
  if (obj->_pid != 0){
    CloseHandle((HANDLE)obj->_pid);
    obj->_pid = 0;
//printf("WSDwinExecute::_check_thread _pid=0!!\n");
  }
//  InitializeCriticalSection(&obj->_crs);
  CloseHandle(obj->_thread2);
  obj->_thread2 = NULL;
  SendMessage(WSGIwinAppDev()->getHWND(),WM_USER,WS_WIN_EXECUTE,(LPARAM)obj);
  ExitThread(0);
}
void WSDwinExecute::_read_thread(DWORD ptr){
  WSDwinExecute* obj = (WSDwinExecute*)ptr;
WSMFtrace("WSDwinExecute::_read_thread! started.\n");
  if (obj->_sem == NULL){
    obj->_sem = CreateSemaphore(0,0,1,0);
  }
//  long len;
  
  while(1){
    if (obj->_buffer == NULL){
      obj->_buffer = new char[4097];
    }
    DWORD rlen = 0;
    char buffer[4096];
    long ret = ReadFile(obj->_read,buffer,4096,&rlen,NULL);
WSMFtrace("WSDwinExecute::_read_thread! read data. ret=%d rlen=%d\n",ret,rlen);
    EnterCriticalSection(&obj->_crs);
    if (ret == 0 || rlen == 0){
      obj->_data_len = 0;
      delete obj->_buffer;
      obj->_buffer = NULL;
      LeaveCriticalSection(&obj->_crs);
      break;
    }
    memcpy(obj->_buffer,buffer,rlen);
    obj->_data_len = rlen;
    LeaveCriticalSection(&obj->_crs);
    SendMessage(WSGIwinAppDev()->getHWND(),WM_USER,WS_WIN_EXECUTE,(LPARAM)obj);
    WaitForSingleObject(obj->_sem,INFINITE);
    while(1){
      EnterCriticalSection(&obj->_crs);
      if (obj->_data_len < 1){
        LeaveCriticalSection(&obj->_crs);
        break;
      }
      LeaveCriticalSection(&obj->_crs);
      SendMessage(WSGIwinAppDev()->getHWND(),WM_USER,WS_WIN_EXECUTE,(LPARAM)obj);
      WaitForSingleObject(obj->_sem,INFINITE);
    }
  }
WSMFtrace("WSDwinExecute::execute thread exit.  XXZZg1\n");
  SendMessage(WSGIwinAppDev()->getHWND(),WM_USER,WS_WIN_EXECUTE,(LPARAM)obj);
}
long WSDwinExecute::execute(char* exename,long mode){
  if (exename == NULL){
WSMFtrace("WSDwinExecute::execute exename=NULL!!! XZZg1 return.\n");
    return WS_ERR;
  }
WSMFtrace("WSDwinExecute::execute #%s# dir=#%s#  XXZZg1\n",exename,(char*)_dir_name);
//printf("WSDwinExecute::execute #%s# dir=#%s#  XXZZg1\n",exename,_dir_name.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()));
  PROCESS_INFORMATION pi;
  STARTUPINFO si;
  ZeroMemory(&si,sizeof(STARTUPINFO));
  GetStartupInfo(&si);
  ZeroMemory(&pi,sizeof(PROCESS_INFORMATION));
  si.cb = sizeof(STARTUPINFO);
  si.dwFlags = STARTF_USESHOWWINDOW;
  si.wShowWindow = SW_SHOWNORMAL;

  _exe_name = WSGIappFileSystem()->adjustFileName(exename);

  killProcess();
  WSCstring tmp(_exe_name);
  while( tmp.replaceString("//","/",0));
  while( tmp.replaceString("/","\\",0));
  WSCstring dname;
  if (strcmp(_dir_name.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()),"")){
    dname = WSGIappFileSystem()->adjustFileName(_dir_name);
    while( dname.replaceString("//","/",0));
    while( dname.replaceString("/","\\",0));
    BOOL fl = SetCurrentDirectory(dname.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()));
WSMFtrace("WSDwinExecute::execute2 dir=#%s#  XXZZg1\n",dname.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()));
//printf("WSDwinExecute::execute2 dir=#%s#  XXZZg1\n",dname.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()));
    if (fl == FALSE){
      return WS_ERR;
    }
  }
WSMFtrace("WSDwinExecute::execute2 #%s#  XXZZg1\n",tmp.getString( WSGIappLocaleSet()->getSystemLocaleEncoding() ));
  if (_thread2 != NULL){
    TerminateThread(_thread2,0);
    CloseHandle(_thread2);
    _thread2 = NULL;
  }
  if (mode == WS_EXEC_RDWR || mode == WS_EXEC_RD || mode == WS_EXEC_WR){
//WSMFtrace("WSDwinExecute::execute2 #%s#\n",(char*)tmp);
    if (_thread != NULL){
      TerminateThread(_thread,0);
      CloseHandle(_thread);
      _thread = NULL;
      DeleteCriticalSection(&_crs);
      InitializeCriticalSection(&_crs);
    }
//    InitializeCriticalSection(&_crs);
    if (_sem != 0){
      CloseHandle(_sem);
      _sem = 0;
    }
    if (_read != NULL){
      CloseHandle(_read);
      CloseHandle(_read2);
      _read = NULL;
    }
    if (_write != NULL){
      CloseHandle(_write);
      CloseHandle(_write2);
      _write = NULL;
    }

#if 0     
    if (mode == WS_EXEC_RD){
      CreatePipe(&_read,&si.hStdOutput,NULL,0);
      si.hStdError = si.hStdOutput;
    }else if (mode == WS_EXEC_WR){
      CreatePipe(&si.hStdInput,&_write,NULL,0);
    }else{
#endif
      SECURITY_ATTRIBUTES sec;
      sec.nLength = sizeof(SECURITY_ATTRIBUTES);
      sec.lpSecurityDescriptor = NULL;
      sec.bInheritHandle = TRUE;
//    if (_read == NULL){
      CreatePipe(&si.hStdInput,&_write,&sec,0);
      CreatePipe(&_read,&si.hStdOutput,&sec,0);
      _read2 = si.hStdOutput;
      _write2 = si.hStdInput;
//    }else{
//      si.hStdOutput = _read2;
//      si.hStdInput = _write2;
//    }
      si.hStdError = si.hStdOutput;
WSMFtrace("WSDwinExecute::execute2 _read=0x%x _write=0x%x XXZZg1\n",_read,_write);
//printf("WSDwinExecute::execute2 _read=0x%x _write=0x%x XXZZg1\n",_read,_write);
#if 0
    }
#endif
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_MINIMIZE;
//    long ret1 = CreateProcess(NULL,tmp,NULL,NULL,TRUE,DETACHED_PROCESS,NULL,NULL,&si,&pi);
    CreateProcess(NULL,
       tmp.getString( WSGIappLocaleSet()->getSystemLocaleEncoding()),
       NULL,NULL,TRUE,0,NULL,NULL,&si,&pi);
    if (pi.hProcess == 0){
      return WS_ERR;
    }
    _pid = (int)pi.hProcess;
    _handle = pi.hProcess;
WSMFtrace("WSDwinExecute::execute2 handle=0x%x XXZZg1\n",_handle);
    if (mode != WS_EXEC_WR && _data_receive){
      _thread  = CreateThread(0,0,(LPTHREAD_START_ROUTINE)_read_thread,
                              (void*)this,0,&_thread_id);
WSMFtrace("WSDwinExecute::execute this=0x%x thread=0x%x err=%d\n",this,_thread,GetLastError());
    }
    DWORD dust;

    _thread2 = CreateThread(0,0,(LPTHREAD_START_ROUTINE)_check_thread,
                              (void*)this,0,&dust);
    _mode = mode;

    return WS_NO_ERR;
  }
//  CreateProcess(NULL,tmp,NULL,NULL,FALSE,DETACHED_PROCESS,NULL,NULL,&si,&pi);
  BOOL ret1 = CreateProcess(NULL,
       tmp.getString( WSGIappLocaleSet()->getSystemLocaleEncoding()),
       NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
//printf("WSDwinExecute::exec pid=0x%x %d %d\n",pi.hProcess,ret1,GetLastError());
  if (pi.hProcess == 0){
    return WS_ERR;
  }
  _pid = (int)pi.hProcess;
  _handle = pi.hProcess;
//printf("WSDwinExecute::exec pid=0x%x\n",pi.hProcess);
  DWORD dust;
  _thread2 = CreateThread(0,0,(LPTHREAD_START_ROUTINE)_check_thread,
                              (void*)this,0,&dust);
WSMFtrace("WSDwinExecute::execute this=0x%x thread2=0x%x err=%d \n",this,_thread2,GetLastError());
  return WS_NO_ERR;
}
long WSDwinExecute::killProcess(){
WSMFtrace("WSDwinExecute::killProcess XXZZkk TerminateProcess start 0x%x\n",_pid);
  if (_thread2 != NULL){
    TerminateThread(_thread2,0);
    CloseHandle(_thread2);
    _thread2 = 0;
//printf("WSDwinExecute::killProcess _thread2=0!!\n");
  }
  if (_thread != NULL){
    TerminateThread(_thread,0);
    CloseHandle(_thread);
    _thread = 0;
    DeleteCriticalSection(&_crs);
    InitializeCriticalSection(&_crs);
//printf("WSDwinExecute::killProcess _thread=0!!\n");
  }
  if (_sem != 0){
    CloseHandle(_sem);
    _sem = 0;
  }
  if (_read != NULL){
    CloseHandle(_read);
    CloseHandle(_read2);
    _read = NULL;
  }
  if (_write != NULL){
    CloseHandle(_write);
    CloseHandle(_write2);
    _write = NULL;
  }
  if (_buffer != NULL){
    delete _buffer;
    _buffer = NULL;
  }
  _data_len = 0;
  if (_pid == 0){
    _handle = 0;
    return WS_NO_ERR;
  }else{
    unsigned int ret =0;
WSMFtrace("XXZZkk TerminateProcess 0x%x\n",_pid);
    TerminateProcess((HANDLE)_pid,ret);
    CloseHandle((HANDLE)_pid);
//printf("WSDwinExecute::killProcess _pid=0!! 0x%x\n",_pid);
    _pid = 0;
    _handle = 0;
  }
  return WS_NO_ERR;
}

long WSDwinExecute::read(char* buf,long len){
WSMFtrace("WSDwinExecute::read this=0x%x\n");
  if (_read == 0){
    return 0;
  }
  if (_data_len == 0){
    return 0;
  }
  if (_mode == WS_EXEC_RD || _mode == WS_EXEC_RDWR){
WSMFtrace("WSDwinExecute::read here1\n");
    EnterCriticalSection(&_crs);
    if (len < _data_len){
      memcpy(buf,_buffer,len);
      _data_len = _data_len - len;
      memcpy(_buffer,&_buffer[len],_data_len);
    }else{
      memcpy(buf,_buffer,_data_len);
      delete _buffer;
      _buffer = NULL;
      _data_len = 0;
      len = _data_len;
    }
    LeaveCriticalSection(&_crs);
    long semacnt;
    ReleaseSemaphore(_sem,1,&semacnt); 
WSMFtrace("WSDwinExecute::read this=0x%x len=%d\n",this,len);
    return len;
  }
  return -1;
}
char* WSDwinExecute::gets(char* buf,long len){
WSMFtrace("WSDwinExecute::gets this=0x%x\n",this);
  if (_read == 0 || _data_len == 0){
WSMFtrace("WSDwinExecute::gets this=0x%x ret=0\n",this);
    return 0;
  }
WSMFtrace("WSDwinExecute::gets here1 this=0x%x\n",this);
  if (_mode == WS_EXEC_RD || _mode == WS_EXEC_RDWR){
    char buffer[4098];
WSMFtrace("WSDwinExecute::gets relen=%d pre=#%s#\n",_data_len,_buffer);
WSMFtrace("WSDwinExecute::gets read data. rlen=%d\n",_data_len);
    EnterCriticalSection(&_crs);
WSMFtrace("WSDwinExecute::gets read data. start.\n");
    int i;
    for(i=0; i<4096; i++){
      if (_buffer[i] == '\n'){
        i++;
        memcpy(buffer,_buffer,i);
        buffer[i] = 0;
        if (_buffer[i-2] == 0x0d){
          buffer[i-2] = '\n';
          buffer[i-1] = 0;
        }
        break;
      }
      if (_buffer[i] == 0){
        memcpy(buffer,_buffer,i);
        buffer[i] = 0;
        break;
      }
    }
    long slen = strlen(buffer);
    if (len -1 < slen){
      memcpy(buf,buffer,len -1);
      buf[len-1] = 0;
      memcpy(buffer,_buffer,4096);
      _data_len = _data_len - len +1;
      memcpy(_buffer,&buffer[len-1],4096-(len-1));
    }else{
WSMFtrace("WSDwinExecute::gets 0x%x 0x%x\n",buffer[i-2],buffer[i-1]);
      strcpy(buf,buffer);
      _data_len = _data_len - i;
      if (_data_len < 0){
        _data_len =0;
      }else{
        memcpy(buffer,_buffer,4096);
        memcpy(_buffer,&buffer[i],_data_len);
WSMFtrace("WSDwinExecute::gets leftlen=%d #%s#\n",_data_len,_buffer);
      }
      if (_data_len == 0){
        delete _buffer;
        _buffer = NULL;
      }
    }
    LeaveCriticalSection(&_crs);
    long semacnt;
    ReleaseSemaphore(_sem,1,&semacnt); 
WSMFtrace("WSDwinExecute::gets this=0x%x len=%d  ret=##%s##\n",this,strlen(buf),buf);
WSMFtrace("WSDwinExecute::gets 0x%x 0x%x 0x%x\n",buf[i-3],buf[i-2],buf[i-1]);
    return buf;
  }
WSMFtrace("WSDwinExecute::gets here2 this=0x%x\n",this);
  return 0;
}


long WSDwinExecute::write(char* buf,long len){
  if (_mode == WS_EXEC_WR || _mode == WS_EXEC_RDWR){
    if (_write != NULL){
      DWORD ret;
      WriteFile(_write,buf,len,&ret,NULL);
      return ret;
    }
  }
  return -1;
}


