# include <sys/types.h>
# include <unistd.h>
# include <fcntl.h>
# include <pslib.h>
# include "pmFPA.h"
# include "pmDetrendDB.h"

void closePipes (int *stdin_fd, int *stdout_fd, int *stderr_fd)
{

    if (stdin_fd[0]  != 0)
        close (stdin_fd[0]);
    if (stdin_fd[1]  != 0)
        close (stdin_fd[0]);
    if (stdout_fd[0] != 0)
        close (stdout_fd[0]);
    if (stdout_fd[1] != 0)
        close (stdout_fd[1]);
    if (stderr_fd[0] != 0)
        close (stderr_fd[0]);
    if (stderr_fd[1] != 0)
        close (stderr_fd[1]);
}

static void psPipeFree (psPipe *pipe)
{
    return;
}

psPipe *psPipeAlloc ()
{
    psPipe *pipe = (psPipe *)psAlloc(sizeof(psPipe));
    psMemSetDeallocator(pipe, (psFreeFunc) psPipeFree);

    pipe->stdin  = 0;
    pipe->stdout = 0;
    pipe->stderr = 0;
    return (pipe);
}

psPipe *psPipeOpen (char *command)
{

    int stdin_fd[2], stdout_fd[2], stderr_fd[2], status;
    pid_t pid;

    bzero (stdin_fd,  2*sizeof(int));
    bzero (stdout_fd, 2*sizeof(int));
    bzero (stderr_fd, 2*sizeof(int));

    if (pipe (stdin_fd)  < 0) {
        psError (PS_ERR_UNKNOWN, true, "cannot create pipe file descriptor");
        closePipes (stdin_fd, stdout_fd, stderr_fd);
        return NULL;
    }
    if (pipe (stdout_fd) < 0) {
        psError (PS_ERR_UNKNOWN, true, "cannot create pipe file descriptor");
        closePipes (stdin_fd, stdout_fd, stderr_fd);
        return NULL;
    }
    if (pipe (stderr_fd) < 0) {
        psError (PS_ERR_UNKNOWN, true, "cannot create pipe file descriptor");
        closePipes (stdin_fd, stdout_fd, stderr_fd);
        return NULL;
    }

    psArray *cmd = psStringSplitArray (command, " ", false);
    if (cmd->n <= 0) {
        psError (PS_ERR_UNKNOWN, true, "empty command for pipe");
        psFree (cmd);
        closePipes (stdin_fd, stdout_fd, stderr_fd);
        return NULL;
    }

    // create the command line array needed by execvp
    char **argv = (char **)psAlloc((cmd->n+1)*sizeof(char *));
    for (int i = 0; i < cmd->n; i++) {
        argv[i] = cmd->data[i];
    }
    argv[cmd->n] = NULL;

    pid = fork ();
    if (!pid) { /* must be child process */
        /* close the other ends of the pipes */
        close (stdin_fd[1]);
        close (stdout_fd[0]);
        close (stderr_fd[0]);

        /* tie our ends of the pipes to stdin, stdout, stderr */
        dup2 (stdin_fd[0],  STDIN_FILENO);
        dup2 (stdout_fd[1], STDOUT_FILENO);
        dup2 (stderr_fd[1], STDERR_FILENO);

        /* set all three unblocking */
        setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
        setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
        setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);

        status = execvp (argv[0], argv);
        // fprintf (stderr, "error starting remote shell process\n");

        // this statement exits the child, not the parent, process
        exit (1);
    }
    psFree (cmd);
    psFree (argv);

    if (pid == -1) {
        psError (PS_ERR_UNKNOWN, true, "unable to create child process");
        closePipes (stdin_fd, stdout_fd, stderr_fd);
        return NULL;
    }

    /* close the other ends of the pipes */
    close (stdin_fd[0]);
    stdin_fd[0]  = 0;
    close (stdout_fd[1]);
    stdout_fd[1] = 0;
    close (stderr_fd[1]);
    stderr_fd[1] = 0;

    /* make the pipes non-blocking */
    fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
    fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
    fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);

    psPipe *pipe = psPipeAlloc();

    pipe->stdin  = stdin_fd[1];
    pipe->stdout = stdout_fd[0];
    pipe->stderr = stderr_fd[0];

    return (pipe);
}

bool psPipeClose (psPipe *pipe)
{

    close (pipe->stdin);
    close (pipe->stdout);
    close (pipe->stderr);
    return true;
}

