/**
 * Implementation of redirected I/O on child process stdout/stdin.
 *
 * @copyright 2008, Research In Motion Ltd.
 *
 * @version $Id: //depot/main/dsp/ecall/c/testlab/process.c#6 $
 */

#include <windows.h> 
#include <stdio.h> 
#include "process.h"
#include "error.h"

BOOL ProcessOpen(Process *p, const char *cmd)
{
    char buffer[MAX_PATH];
    SECURITY_ATTRIBUTES saAttr; 
    PROCESS_INFORMATION processInfo; 
    STARTUPINFO childInfo;
    STARTUPINFO myInfo;
    BOOL status;

    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL; 

    /* Create anonymous pipes for redirected I/O */
    Assert(
        CreatePipe(&p->hStdoutRd, &p->hStdoutWr, &saAttr, 0),
        "ProcessOpen: CreatePipe failed for stdout"); 
    SetHandleInformation(p->hStdoutRd, HANDLE_FLAG_INHERIT, 0);
    Assert(
        CreatePipe(&p->hStdinRd, &p->hStdinWr, &saAttr, 0),
        "ProcessOpen: CreatePipe failed for stdin"); 
    SetHandleInformation(p->hStdinWr, HANDLE_FLAG_INHERIT, 0);

    GetStartupInfo(&myInfo);
    ZeroMemory(&childInfo, sizeof(STARTUPINFO));
    childInfo.cb = sizeof(STARTUPINFO); 
    childInfo.hStdError = myInfo.hStdError;
    childInfo.hStdOutput = p->hStdoutWr;
    childInfo.hStdInput = p->hStdinRd;
    childInfo.dwFlags |= STARTF_USESTDHANDLES;
    ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));

    /* Copy cmd to buffer so const memory isn't passed to CreateProcess */
    strncpy(buffer, cmd, sizeof(buffer));
    buffer[sizeof(buffer) - 1] = '\0';

    status = CreateProcess(
        NULL, 
        buffer,          
        NULL,         /* process security attributes */
        NULL,         /* primary thread security attributes */
        TRUE,         /* handles are inherited */
        0,            /* creation flags */
        NULL,         /* use parent's environment */
        NULL,         /* use parent's current directory */
        &childInfo,   
        &processInfo);

    if(!status) {
        ProcessClose(p);
    }
    else {
        /* Close right away, since these are no longer needed */
        CloseHandle(processInfo.hProcess);
        CloseHandle(processInfo.hThread);
    }

    return status;
}

void ProcessClose(Process *p)
{
    /* Close both ends, just to be sure */
    if(p->hStdinWr) {
        CloseHandle(p->hStdinWr);
        p->hStdinWr = 0;
    }
    if(p->hStdinRd) {
        CloseHandle(p->hStdinRd);
        p->hStdinRd = 0;
    }
    if(p->hStdoutWr) {
        CloseHandle(p->hStdoutWr);
        p->hStdoutWr = 0;
    }
    if(p->hStdoutRd) {
        CloseHandle(p->hStdoutRd);
        p->hStdoutRd = 0 ;
    }
}

void ProcessRead(Process *p, void *buffer, int nBytes)
{
    /* We may need to read multiple chunks (if multiple writes were used) */
    while(nBytes > 0) {
        int n;
        Assert(
            ReadFile(p->hStdoutRd, buffer, nBytes, &n, NULL),
            "ProcessRead: ReadFile on stdout failed");
        nBytes -= n;
    }
}

void ProcessWrite(Process *p, const void *buffer, int nBytes)
{
    int n;
    Assert(
        WriteFile(p->hStdinWr, buffer, nBytes, &n, NULL),
        "ProcessWrite: WriteFile on stdin failed");
    Assert(n == nBytes, "ProcessWrite: write failed to complete");
}

#ifdef __TEST__
int main(int argc, char *argv[]) 
{ 
   int i;
   short frame[160];
   Process e;       /* encoder process */
   Process d;       /* decoder process */

   LogInit(stderr);

   if(!ProcessOpen(&e, "fr.exe -e")) {
       Abort("Couldn't start encoder");
   }
   if(!ProcessOpen(&d, "fr.exe -d")) {
       Abort("Couldn't start decoder");
   }
 
   /* Process a number of frames */
   for(i = 0; i < 1000; ++i) {
       printf("Frame=%d\n", i);
       ProcessWrite(&e, frame, sizeof(frame));
       ProcessRead(&e, frame, 36);
       ProcessWrite(&d, frame, 36);
       ProcessRead(&d, frame, sizeof(frame));
   }

   ProcessClose(&e);
   ProcessClose(&d);

   return 0; 
} 
#endif
