#include "sish.h"

#include <sys/stat.h>
#include <sys/wait.h>

#include <dirent.h>
#include <errno.h>
#include <libgen.h>
#include <pwd.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "builtins.h"
#include "cmd_exec.h"
#include "utils.h"

volatile sig_atomic_t interrupted = false;

/* special values */
pid_t last_pid_bg = 0;
int last_fg_exit_status = 0;

/* cli flags */
bool flag_tracing_enabled = false;
bool flag_command_specified = false;

int
shell_loop(int argc, char** argv)
{
	/* block Control + C, Z, \ (Int, tstop, quit)*/
	ignore_signals();

	while (true)
	{
		interrupted = false;
		if (!flag_command_specified)
		{
			/* output would otherwise appear after the command executed due to
			 * exit handlers flushing stdio buffers, so it needs to explicitly
			 * be prevented */
			printf("%s ", SHELL_NAME);
		}

		char* token_args[MAX_ARGS] = {0};
		int token_args_count = 0;

		char user_raw_input[BUFSIZ] = {0};

		if (flag_command_specified)
		{
			/* ./sish [flags] <cmd> - start at first non-option to accomdate
			 * both -x -c and -xc */
			for (int i = optind - 1; i < argc; i++)
			{
				strncat(user_raw_input, argv[i],
				        sizeof(user_raw_input) - strlen(user_raw_input) - 1);
				if (i < argc - 1)
				{
					strncat(user_raw_input, " ",
					        sizeof(user_raw_input) - strlen(user_raw_input) -
					            1);
				}
			}
		}
		else if (fgets(user_raw_input, sizeof(user_raw_input), stdin) == NULL)
		{
			/* supresses "[...] fgets: Interrupted system call." */
			if (!interrupted)
			{
				/* EOF was sent */
				if (feof(stdin))
				{
					printf("\n");
					exit(EXIT_SUCCESS);
				}
				fprintf(stderr, "%s: fgets: %s.\n", GET_PROGNAME_ERR,
				        strerror(errno));
			}
			/* man page dictates this is necessary when testing EOF */
			clearerr(stdin);
			continue;
		}

		if ((token_args_count = tokenize(user_raw_input, token_args)) == -1)
		{
			/* error message handled in the call above */
			continue;
		}

		try_expand_env_vars(token_args);

		run_commands(token_args);
		for (int i = 0; i < token_args_count; i++)
		{
			free(token_args[i]);
		}

		if (flag_command_specified)
		{
			exit(EXIT_SUCCESS);
		}
	}
}

int
main(int argc, char** argv)
{
	int opt;

	while ((opt = getopt(argc, argv, "xc:")) != -1)
	{
		switch (opt)
		{
		case 'x':
			flag_tracing_enabled = true;
			break;
		case 'c':
			flag_command_specified = true;
			break;
		default:
			fprintf(stderr, "Usage: %s [-x] [-c command]\n", GET_PROGNAME_ERR);
			exit(EXIT_FAILURE);
		}
	}

	set_shell_env(argv[0]);
	return shell_loop(argc, argv);
}