/** @file */
#include "parsing.h"
#include "disk.h"
#include "exec.h"
#include "struct.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/**
 * @brief Attend que l'utilisateur appuie sur entrée pour récupérer la ligne
 * écrite puis la renvoi
 * @return La ligne écrite par l'utilisateur
 */
char *read_line() {
  char c;
  int i = 0;
  int j = 1;

  char *buffer = malloc(j * BUFSIZE * sizeof(char));
  if (buffer == NULL) {
    dprintf(STDERR_FILENO, "Line read buffer allocation error !");
    exit(EXIT_FAILURE);
  }

  while (1) {
    c = getchar();

    if (i == BUFSIZE - 1) {
      j++;
      buffer = realloc(buffer, j * BUFSIZE);
    }

    if (c == EOF || c == '\n') {
      buffer[i] = '\0';
      return buffer;
    } else {
      buffer[i] = c;
    }
    i++;
  }
}

/**
 * @brief
 * @param char *line la ligne à parser
 * @return la liste des arguments de la commande
 */
command **split_line(char *line) {
  char **args = malloc(BUFSIZE * sizeof(char *));
  int i = 0;
  if (args == NULL) {
    dprintf(STDERR_FILENO, "Allocation error");
    exit(EXIT_FAILURE);
  }

  char *token = strtok(line, " ");

  while (token != NULL) {
    args[i] = token;
    i++;
    token = strtok(NULL, " ");
  }
  args[i++] = NULL;

  if (args[0] == NULL) {
    return NULL;
  }

  command **cmd_lines = malloc(sizeof(command *));
  if (cmd_lines == NULL) {
    dprintf(STDERR_FILENO, "Command struct allocation error !");
    exit(EXIT_FAILURE);
  }

  cmd_lines[0] = malloc(sizeof(command));
  cmd_lines[0]->argv = malloc(sizeof(char *));

  i = 0;
  int idx_cmd = 0;
  int nbr_args_in_actual_cmd = 0;
  while (args[i] != NULL) {
    if (strcmp(args[i], "|") == 0) {
      cmd_lines[idx_cmd]->argv[nbr_args_in_actual_cmd + 1] = NULL;
      idx_cmd++;
      cmd_lines[idx_cmd] = malloc(sizeof(command));
      cmd_lines[idx_cmd]->argv = malloc(sizeof(char *));
      nbr_args_in_actual_cmd = 0;
    } else {
      cmd_lines[idx_cmd]->argv =
          realloc(cmd_lines[idx_cmd]->argv,
                  (nbr_args_in_actual_cmd + 1) * sizeof(char *));
      cmd_lines[idx_cmd]->argv[nbr_args_in_actual_cmd] = strdup(args[i]);
      nbr_args_in_actual_cmd++;
    }

    i++;
  }
  cmd_lines[idx_cmd + 1] = malloc(sizeof(command));
  cmd_lines[idx_cmd + 1] = NULL;

  return cmd_lines;
}

/**
 * @brief La boucle du shell, elle consiste en trois étapes :
 * - Quand l'utilisateur appui sur entrer pour récupérer son entrée standard
 * - Parser la ligne en fonction de ses espaces
 * - Exécuter la commande demandée
 * @see char* read_line()
 * @see char** split_line(char *line)
 * @see int execute_cmd(char **args)
 */
void shell_loop(disk *disk) {
  char *user = get_env_value("USER");

  if (user == NULL) {
    exit(EXIT_FAILURE);
  }

  char *hostname = get_env_value("HOST");

  char *line;
  command **args;
  int status = 1;

  do {
    // ça aussi c'est pas bien, ça sera à changer une fois le sgf fonctionnel
    dprintf(STDOUT_FILENO, "%s@%s %s> ", user, hostname, get_env_value("PWD"));
    line = read_line();
    args = split_line(line);
    status = execute_cmd(disk, args);

    free(line);
    free(args);
  } while (status);
}

void signal_handler(int sig) {
  (void)sig;
  char *user = get_env_value("USER");
  if (user == NULL) {
    exit(EXIT_FAILURE);
  }
  char *hostname = get_env_value("HOST");

  dprintf(STDOUT_FILENO, "\n");
  dprintf(STDOUT_FILENO, "%s@%s %s> ", user, hostname, get_env_value("PWD"));
}

void open_seyshell(disk *disk) {
  signal(SIGINT, signal_handler); // le processus père ne s'arrête pas quand il
                                  // y a un SIGINT = sigma boy
  env *envs = init_envs();
  shell_loop(disk);
  free(envs);
}
