#if defined(__MINGW32__)
#include <sys/fcntl.h>
#include <tchar.h>
#include <windows.h>

/*
 * Display the error message of Windows system call 
 */
void ErrorExit(LPTSTR lpszFunction) 
{ 
    /* Retrieve the system error message for the last-error code */
    TCHAR buff[1024] = {};
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        &buff,
        sizeof(buff)/sizeof(buff[0]), 
        NULL );

    /* Display the error message and exit the process */
    _ftprintf(stderr, "%s\n%s\n", buff, lpszFunction);

    ExitProcess(dw); 
}

/*
 * Convert a Windows file handle to crt type FILE*
 */
FILE* fhopen(HANDLE hFile, const char *zMode){
    int fd = _open_osfhandle((intptr_t)hFile, _O_BINARY);

    if( fd != -1)
        return _fdopen(fd, zMode);
    else
        return NULL;
}


/*
 * Spawn child process and redirect its io to our handles
 */
static DWORD SpawnChild(LPTSTR lpCmdLine, HANDLE hIn, HANDLE hOut, HANDLE hErr)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    BOOL success;

    memset((void *)&si, 0, sizeof(STARTUPINFO));
    si.cb = sizeof (STARTUPINFO);

    /*
     * Indicate the hStdInput, hStdOutput, and hStdError members are valid. 
     */
    si.dwFlags = STARTF_USESTDHANDLES;

    si.hStdInput  = hIn;
    si.hStdOutput = hOut;
    si.hStdError  = hErr;

    /*
     * Ensure stdio inheritable.
     */
    success = SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, TRUE);
    if(!success) {
        ErrorExit("SetHandleInformation failed");
    }

    success = SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, TRUE);
    if(!success) {
        ErrorExit("SetHandleInformation failed");
    }

    success = SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT, TRUE);
    if(!success) {
        ErrorExit("SetHandleInformation failed");
    }

    /* 
     * Create child process
     */
    success = CreateProcess(NULL,	/* LPCSTR address of module name */
            lpCmdLine,           /* LPCSTR address of command line */
            NULL,		/* Process security attributes */
            NULL,		/* Thread security attributes */
            TRUE,		/* Inheritable Handes inherited. */
            0,		    /* DWORD creation flags  */
            NULL,       /* Use parent environment block */
            NULL,		/* Address of current directory name */
            &si,   /* Address of STARTUPINFO  */
            &pi);	/* Address of PROCESS_INFORMATION   */

    if(!success){
        ErrorExit("CreateProcess failed");
    }

    /* Close process and thread handles. */
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

    return pi.dwProcessId;
}

static DWORD win32_create_child(const char *zCmd, HANDLE *hIn, HANDLE *hOut)
{
   HANDLE hChildStdinRd, hChildStdinWr, 
          hChildStdoutRd, hChildStdoutWr;
   HANDLE hStderr;
 
   SECURITY_ATTRIBUTES saAttr; 
   BOOL fSuccess; 
 

   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
   saAttr.bInheritHandle = TRUE; /* Set the bInheritHandle flag so pipe handles are inherited. */
   saAttr.lpSecurityDescriptor = NULL; 

#if defined(FOSSIL_CHILD_DEBUG)
   char zDebugFName[200] = {0};
   const char *zTempPrefix = "sever_child_debug_output";

   static volatile long i = 0;

   InterlockedIncrement(&i);

   snprintf(zDebugFName, sizeof(zDebugFName), "%s_%05ld.txt", zTempPrefix, i);

   hStderr = CreateFile(zDebugFName,    // file to open
           GENERIC_WRITE,          // open for reading
           FILE_SHARE_READ,       // share for reading
           NULL,                  // default security
           CREATE_ALWAYS,         // existing file only
           FILE_ATTRIBUTE_NORMAL, // normal file
           NULL);                 // no attr. template

   SetHandleInformation( hStderr, HANDLE_FLAG_INHERIT, TRUE);
#else
   /* Get the handle to the current STDERR. */
   hStderr = GetStdHandle(STD_ERROR_HANDLE);
#endif
 
   /* Create a pipe for the child process's STDOUT. */
   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 4096)) 
      ErrorExit("Stdout pipe creation failed\n"); 

   /* Ensure the read handle to the pipe for STDOUT is not inherited. */
   SetHandleInformation( hChildStdoutRd, HANDLE_FLAG_INHERIT, FALSE);

   /* Create a pipe for the child process's STDIN. */
   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 4096)) 
      ErrorExit("Stdin pipe creation failed\n"); 

   /* Ensure the write handle to the pipe for STDIN is not inherited. */
   SetHandleInformation( hChildStdinWr, HANDLE_FLAG_INHERIT, FALSE);
  
   /* Spawn child and redirect its io */
  DWORD processId = SpawnChild(zCmd, hChildStdinRd, hChildStdoutWr, hStderr);

  /* Close pipe handles that used by child to read and write */
  CloseHandle(hChildStdinRd); 
  CloseHandle(hChildStdoutWr);
#if defined(FOSSIL_CHILD_DEBUG)
  CloseHandle(hStderr);
#endif

  *hIn = hChildStdoutRd;
  *hOut = hChildStdinWr; 

  return processId;
}

int popen2(const char *zCmd, FILE **ppIn, FILE **ppOut, int *pChildPid){
  HANDLE in, out;
  DWORD childProcessId = win32_create_child(zCmd, &in, &out);

  if(childProcessId == 0){
      fossil_fatal("create child process failed");
  }

  *ppIn = fhopen(in, "rb");
  *ppOut = fhopen(out, "wb");

  if( (*ppIn) == NULL || (*ppOut) == NULL){
      fossil_fatal("fhopen failed!");
  }

  *pChildPid = childProcessId;
}
#endif /* __MINGW32__ */
