/**
 * File: first-shell-soln.cc
 * -------------
 * A program that implements simple shell functionality - it can read in
 * and execute single commands the user enters by forking off a child process
 * and running execvp in that child process, while the parent calls waitpid
 * to wait for it.
 */

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

using namespace std;

/**
 * Create new process(es) for the provided pipeline. Spawns child processes with
 * input/output redirected to the appropriate pipes and/or files.
 */
void runPipeline(const pipeline& p) {
    /* A pipeline is a type that represents a full line the user has entered.
     * We'll see later that a single line can contain more than one "command"
     * chained together in a "pipeline", but for now we will just assume the
     * user enters only 1 command, like "ls -a" or "tools/submit", so we get
     * p.commands[0].  The type at this index is a 'command', which stores the
     * string but chopped up in the way that execvp needs,
     * via the "argv" field inside it.  For instance, "ls -a -l" would be
     * stored as {"ls", "-a", "-l", NULL} in the command's argv field.
     */
    command cmd = p.commands[0];

    pid_t pidOrZero = fork();
    if (pidOrZero == 0) {
        // If we are the child, execute the command
        execvp(cmd.argv[0], cmd.argv);
        /* If the child gets here, there was an error - terminate the child process.
         * We need to terminate the child so it doesn't continue to run code meant
         * only for the parent shell!
         */
        throw STSHException(string(cmd.argv[0]) + ": Command not found.");
    }

    // If we are the parent, wait for the child
    waitpid(pidOrZero, NULL, 0);
}

/**
 * Run the REPL loop: take input from the user, execute commands, and repeat
 */
int main(int argc, char* argv[]) {
    pid_t stshpid = getpid();
    rlinit(argc, argv);
    while (true) {
        string line;
        if (!readline(line) || line == "quit" || line == "exit") break;
        if (line.empty()) continue;
        try {
            pipeline p(line);
            runPipeline(p);
        } catch (const STSHException& e) {
            cerr << e.what() << endl;
            if (getpid() != stshpid) exit(0);  // if exception is thrown from child process, kill it
        }
    }
    return 0;
}
