#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>

#include <grass/gis.h>
#include <grass/spawn.h>

#ifdef __MINGW32__
#include <io.h>
#include <fcntl.h>
#define pipe(fds) _pipe(fds, 4096, O_BINARY | O_NOINHERIT)
#endif

static FILE *do_popen(struct Popen *state, int wr, const char *program,
                      const char **args)
{
    int which = wr ? 0 : 1;
    const char *dir = wr ? "w" : "r";
    int pfd, cfd;
    int pipe_fds[2];
    const char *argv[2];

    state->fp = NULL;
    state->pid = -1;

    if (pipe(pipe_fds) < 0)
        return NULL;

    cfd = pipe_fds[wr ? 0 : 1];
    pfd = pipe_fds[wr ? 1 : 0];

    if (!args) {
        argv[0] = program;
        argv[1] = NULL;
        args = argv;
    }

    state->pid =
        G_spawn_ex(program, SF_ARGVEC, args, SF_REDIRECT_DESCRIPTOR, which, cfd,
                   SF_CLOSE_DESCRIPTOR, pfd, SF_BACKGROUND, NULL);

    if (state->pid == -1) {
        close(pipe_fds[0]);
        close(pipe_fds[1]);
        return NULL;
    }

    close(cfd);

    state->fp = fdopen(pfd, dir);

    return state->fp;
}

void G_popen_clear(struct Popen *state)
{
    state->fp = NULL;
    state->pid = -1;
}

FILE *G_popen_write(struct Popen *state, const char *program, const char **args)
{
    return do_popen(state, 1, program, args);
}

FILE *G_popen_read(struct Popen *state, const char *program, const char **args)
{
    return do_popen(state, 0, program, args);
}

void G_popen_close(struct Popen *state)
{
    if (state->fp)
        fclose(state->fp);

    if (state->pid != -1)
        G_wait(state->pid);
}
