/**********************************************************************
 *
 * os_process_windows.c -- Windows version of osprocess.  See os_process.h
 *                         for more details
 *
 * Copyright (C) 2010  The New Zealand Digital Library Project
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#ifdef __WIN32__

#if defined(GSDL_USE_OBJECTSPACE)
#  include <ospace/std/iostream>
#elif defined(GSDL_USE_IOS_H)
#  include <iostream.h>
#else
#  include <iostream>
using namespace std;
#endif

#include "text_t.h"
#include "os_process_windows.h"


osprocesswindows::osprocesswindows(OSProcessPipeMode mode,
				   char* prog_name, char* argv[], 
				   char* envp[])
  : child_stdout_read_(NULL), child_stdin_write_(NULL),
    osprocess(mode,prog_name,argv,envp)
{
  HANDLE child_stdin_read = NULL;
  HANDLE child_stdout_write = NULL;

  SECURITY_ATTRIBUTES saAttr; 
  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
  saAttr.bInheritHandle = TRUE; 
  saAttr.lpSecurityDescriptor = NULL; 
  
  // Create a pipe for the child process's STDOUT. 
  if (!CreatePipe(&child_stdout_read_, &child_stdout_write, &saAttr, 0)) {
    cerr << "osprocesswindows::osprocesswindows(): Failed to create stdout pipe for child process" << endl; 
  }
  
  // Ensure the read handle to the pipe for STDOUT is not inherited.
  if (!SetHandleInformation(child_stdout_read_, HANDLE_FLAG_INHERIT, 0)) {
    cerr << "osprocesswindows::osprocesswindows(): Failed to set handle information for stdout pipe for child process" << endl;
  }
  
  // Create a pipe for the child process's STDIN. 
  if (!CreatePipe(&child_stdin_read, &child_stdin_write_, &saAttr, 0)) {
    cerr << "osprocesswindows::osprocesswindows(): Failed to create stdin pipe for child process" << endl;
  }
  
  // Ensure the write handle to the pipe for STDIN is not inherited. 
  if (!SetHandleInformation(child_stdin_write_, HANDLE_FLAG_INHERIT, 0)) {
    cerr << "osprocesswindows::osprocesswindows(): Failed to set handle information for stdin pipe for child process" << endl;
  }
  
  STARTUPINFOA si;
  memset(&si, 0, sizeof(si));
  memset(&pi_, 0, sizeof(pi_));
  
  si.cb = sizeof(si);
  
  // These two lines help prevent a DOS window popping up
  // when the process is created
  si.dwFlags |= STARTF_USESHOWWINDOW;
  si.wShowWindow = SW_HIDE;
  
  si.dwFlags |= STARTF_USESTDHANDLES;

  
  // any error messages in the child process goes to same place as parent
  // e.g. in the case of running as a CGI script, the Web server error log
  si.hStdError  = GetStdHandle(STD_ERROR_HANDLE); 
  
  if ((mode==uniRead) || (mode==biReadWrite)) {
    // Output from the child process comes to the parent process, down the
    // OUT pipe
    si.hStdOutput = child_stdout_write;
  }
  else {
    // uniWrite
    // Output from the chlid process goes to the same place as parent
    si.hStdOutput  = GetStdHandle(STD_OUTPUT_HANDLE); 
  }
  
  if ((mode==uniWrite) || (mode==biReadWrite)) {
    // Input to the child process can be sent from the parent process, down the
    // In pipe
    si.hStdInput = child_stdin_read;
  }
  else {
    // uniRead

    // Input to the child process comes from same place as parent ?!?
    si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE); 
  }
  
  text_t cmd_line = prog_name;
  if (argv != NULL) {
    int i=1;
    while (argv[i] != NULL) {
      cmd_line += " \"";
      cmd_line += argv[i];
      cmd_line += "\"";
      i++;
    }
  }

  char* cmd_line_cstr = cmd_line.getcstr();

  int rv = CreateProcess(NULL,    // no application name
			 cmd_line_cstr, 
			 NULL, 
			 NULL,    // no process or thread security attribues 
			 TRUE,    // Inherit handles
			 0,       // Creation flag
			 NULL,    // No environment block
			 ".",     // current working directory
			 &si, 
			 &pi_);    // process info filled out as a result

  delete [] cmd_line_cstr;

  if (!rv) {
    cerr << "os_process_windows(): Error creating child process" << endl;
  }

  // close the handles that aren't used by the parent process

  if (!CloseHandle(child_stdout_write)) {
    cerr <<  "os_process_windows(): Error closing child's StdOut Write Handle" << endl;
  }

  if (!CloseHandle(child_stdin_read)) {
    cerr << "os_process_windows(): Error closing child's StdIn Read Handle" << endl;
  }
}  
  

osprocesswindows::~osprocesswindows() 
{
  // close any file handles that are still open
  close();

  // Close process and thread handles
  CloseHandle( pi_.hProcess );  
  pi_.hProcess = NULL;
  
  CloseHandle( pi_.hThread );   
  pi_.hThread = NULL;
}



int osprocesswindows::write(char* buffer, const int buffer_len) 
{ 
  DWORD actual_write_len;
  
  bool write_ok 
    = WriteFile(child_stdin_write_, buffer, buffer_len, &actual_write_len, NULL);
  if (!write_ok) {
    cerr << "osproesswindows::write() Error: failed to write data" << endl;
  }
  return actual_write_len;
} 


int osprocesswindows::read(char* buffer, const int buffer_len) 
{ 
  DWORD actual_read_len;
  
  bool read_ok 
    = ReadFile(child_stdout_read_, buffer, buffer_len, &actual_read_len, NULL);
  if (!read_ok) {
    cerr << "osproesswindows::read() Error: failed to read data" << endl;
  }

  return actual_read_len;
}


void osprocesswindows::wait()
{
  WaitForSingleObject(pi_.hProcess, INFINITE);
}

  
bool osprocesswindows::close_write_pipe(OSProcessWarnStatus warn_status)
{
  bool write_close_ok = true;

  if (child_stdin_write_ != NULL) {

    write_close_ok = CloseHandle(child_stdin_write_);

    if (write_close_ok) {
      child_stdin_write_ = NULL;
    }
    else {
      // not OK
      cerr << "osprocesswindows::close(): Error - Failed to close stdin write handle on pipe to child process" << endl;
    }
  }
  else if (warn_status == withWarning) {
    cerr << "osprocesswindows::close_write_pipe(): Warning - Tried to close already closed pipe" << endl;
  }

  return write_close_ok;
}


bool osprocesswindows::close_read_pipe(OSProcessWarnStatus warn_status)
{
  bool read_close_ok = true;
  
  if (child_stdout_read_ != NULL) {
    
    read_close_ok = CloseHandle(child_stdout_read_);
    
    if (read_close_ok) {
      child_stdout_read_ = NULL;
    }
    else {
      cerr << "osprocesswindows::close(): Error - Failed to close handle stdout read on pipe to chlild process" << endl;
    }
  }
  else if (warn_status == withWarning) {
    cerr << "osprocesswindows::close_read_pipe(): Warning - Tried to close already closed pipe" << endl;
  }
  
  return read_close_ok;
}








#endif
