/**
 * File: subprocess.c
 * ------------------
 * Implements the subprocess routine, which is similar to popen and allows
 * the parent process to spawn a child process, print to its standard output,
 * and then suspend until the child process has finished.
 */

#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include "stsh-parser/stsh-parse.h"
#include "fork-utils.h" // this needs to be the last #include in the list


// A struct that stores a PID and FD we can write to
typedef struct subprocess_t {
    pid_t pid;
    int supplyfd;
} subprocess_t;

/* subprocess runs the given shell command in the background
 * in a child process and returns the PID of that child process
 * as well as a file descriptor we can write to to write to that
 * child's STDIN.
 */
subprocess_t subprocess(const char *command) {
    // this line parses the command into a pipeline like is done for you on assign3
    pipeline p(command);
    
    // Make a pipe
    int fds[2];
    pipe(fds);
    
    pid_t pidOrZero = fork();
    if (pidOrZero == 0) {
        // We are not writing to the pipe, only reading from it
        close(fds[1]);

        // Duplicate the read end of the pipe into STDIN
        dup2(fds[0], STDIN_FILENO);
        close(fds[0]);

        // Run the command
        execvp(p.commands[0].argv[0], p.commands[0].argv);
        fprintf(stderr, "execvp failed to invoke this: %s.\n", command);
        exit(1);
    }

    close(fds[0]);

    subprocess_t returnStruct;
    returnStruct.pid = pidOrZero;
    returnStruct.supplyfd = fds[1];
    return returnStruct;
}

/* This version of subprocess is the same as above except it uses pipe2 with O_CLOEXEC
 * to automatically close the pipe in the child process instead of us closing
 * manually.
 */
subprocess_t subprocess2(const char *command) {
    pipeline p(command);
    
    // Make a pipe
    int fds[2];
    pipe2(fds, O_CLOEXEC);
    
    pid_t pidOrZero = fork();
    if (pidOrZero == 0) {
        // We are not writing to the pipe, only reading from it
        // close(fds[1]);    pipe2 no longer needs this close call

        // Duplicate the read end of the pipe into STDIN
        dup2(fds[0], STDIN_FILENO);
        // close(fds[0]);    pipe2 no longer needs this close call

        // Run the command
        execvp(p.commands[0].argv[0], p.commands[0].argv);
        fprintf(stderr, "execvp failed to invoke this: %s.\n", command);
        exit(1);
    }

    close(fds[0]);

    subprocess_t returnStruct;
    returnStruct.pid = pidOrZero;
    returnStruct.supplyfd = fds[1];
    return returnStruct;
}

int main(int argc, char *argv[]) {
    // Spawn a child that is running the grep command
    //subprocess_t sp = subprocess("/usr/bin/grep -E Sunny|Cloudy");
    subprocess_t sp = subprocess("/usr/bin/grep Sunny");

    // We want to feed these lines as input to grep to print only sunny days
    const char * recent_weather[] = { "Sunny 72", "Rainy 55", "Cloudy 62",
        "Sunny 80", "Sunny 75", "Cloudy 61", "Sunny 68", "Rainy 60", "Sunny 85"
    }; 
    size_t nelems = sizeof(recent_weather) / sizeof(recent_weather[0]);

    // write each entry on its own line to the STDIN of the child process
    for (size_t i = 0; i < nelems; i++) {
        dprintf(sp.supplyfd, "%s\n", recent_weather[i]);
    }

    // Close the write FD to indicate the input is closed
    close(sp.supplyfd);

    // Wait for the child to finish before exiting
    waitpid(sp.pid, NULL, 0);
    return 0;
}
