/* 
 * File:   error.c
 * Author: Arthur Brandao
 *
 * Created on 8 novembre 2018
 */
#define _POSIX_C_SOURCE 200809L

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "str.h"
#include "error.h"

/* --- Extern --- */
int serrno = 0;
char* serrlib[] = {
    "Reussite",
    "Erreur lors de la creation de la socket",
    "Erreur lors du bind de la socket",
    "Erreur lors l'acceptation d'une connexion TCP",
    "Erreur lors de la reception des données",
    "Erreur lors de l'envoi des données",
    "Erreur lors de la fermeture de la socket",
    "Erreur mauvais type de socket"
};
Error error;

/* --- Fonctions de la structure --- */
void end(){
    time_t temps;
    int fd;
    //Ecriture derniere ligne
    temps = time(NULL) - error.start;
    fprintf(stderr, "----- Fin log processus %d (temps en seconde : %ld) -----\n", getpid(), temps);
    //On remet la sortie standard des erreurs
    fd = redirect_fd(STDERR_FILENO, error.errfd);
    if(fd == ERR){
        adderror("Impossible de rediriger la sortie d'erreur standard");
        return;
    }
    //Fermeture du fichier
    if(close(fd) == ERR){
        addperror("Erreur pendant la fermeture du fichier de log");
        return;
    }
    //Nettoyage memoire
    free(error.filename);
    error.start = 0;
    error.end = NULL;
    error.exit = NULL;
    error.exit_err = NULL;
    error.exit_status = NULL;
    error.exit_msg = NULL;
    error.add = NULL;
    error.print = NULL;
    error.printadd = NULL;
    //On indique que la structure est non inialisée
    error.init = 0;
}

void exit_status(int status){
    error.end();
    exit(status);
}

void exit_ok(){
    error.exit_status(EXIT_SUCCESS);
}

void exit_err(){
    error.exit_status(EXIT_FAILURE);
}

void exit_msg(int status, const char* format, ...){
    va_list arg;
    va_start(arg, format);
    vfprintf(stdout, format, arg);
    va_end(arg);
    error.exit_status(status);
}

void add(const char* format, ...){
    va_list arg;
    va_start(arg, format);
    vfprintf(stderr, format, arg);
    va_end(arg);
}

void printerr(const char* format, ...){
    va_list arg;
    va_start(arg, format);
    vdprintf(error.errfd, format, arg);
    va_end(arg);
}

void printadd(const char* format, ...){
    va_list arg;
    va_start(arg, format);
    vdprintf(error.errfd, format, arg);
    vfprintf(stderr, format, arg);
    va_end(arg);
}

/* --- Fonctions publiques --- */
int redirect_fd(int fd1, int fd2){
    int tmpfd;
    //On duplique le 1er fd
    tmpfd = dup(fd1);
    if(tmpfd == ERR){
        addperror("Erreur pendant la duplication");
        return ERR;
    }
    //On fait en sorte que fd1 soit lié au fichier de fd2
    if(dup2(fd2, fd1) == ERR){
        addperror("Erreur pendant le changement de fd");
        return ERR;
    }
    //On ferme l'ancien fd2
    if(close(fd2) == ERR){
        addperror("Erreur pendant la fermeture de fd2");
        return ERR;
    }
    //On retourne le nouveau fd de fd1
    return tmpfd;
}

int redirect_fd2(int fd1, int fd2){
    int tmpfd;
    //On duplique le 1er fd
    tmpfd = dup(fd1);
    if(tmpfd == ERR){
        addperror("Erreur pendant la duplication");
        return ERR;
    }
    //On fait en sorte que fd1 soit lié au fichier de fd2
    if(dup2(fd2, fd1) == ERR){
        addperror("Erreur pendant le changement de fd");
        return ERR;
    }
    //On retourne le nouveau fd de fd1
    return tmpfd;
}

void error_init(){
    //Creation nom fichier puis initialisation
    char filename[19];
    sprintf(filename, "err-%ld.log", time(NULL));
    error_finit(filename);
}

void error_finit(const char* filename){
    extern Error error;
    int fd, length;
    //Si il y a deja un log
    if(error.init){
        //On le ferme
        error.end();
    }
    //Ouverture fichier
    fd = open(filename, O_CREAT | O_RDWR, S_IRWXU);
    if(fd == ERR){
        addperror("Erreur lors de l'ouverture du fichier de log");
        return;
    }
    //On se place à la fin du fichier
    if(lseek(fd, 0L, SEEK_END) == ERR){
        addperror("Erreur lors du deplacement dans le fichier");
        return;
    }
    //Changement de la sortie d'erreur standard
    error.errfd = redirect_fd(STDERR_FILENO, fd);
    if(error.errfd == ERR){
        adderror("Impossible de rediriger la sortie d'erreur standard");
        return;
    }
    //Copie nom du fichier
    length = strlen(filename);
    error.filename = malloc(sizeof(char) * (length + 1));
    memset(error.filename, 0, length + 1);
    strncpy(error.filename, filename, length);
    //Timestamp de debut
    error.start = time(NULL);
    //Ajout fonctions
    error.end = end;
    error.exit = exit_ok;
    error.exit_err = exit_err;
    error.exit_status = exit_status;
    error.exit_msg = exit_msg;
    error.add = add;
    error.print = printerr;
    error.printadd = printadd;
    //Ecriture 1er ligne
    fprintf(stderr, "----- Log processus %d (timestamp : %ld) -----\n", getpid(), error.start);
    //On indique que l'inialisation a eu lieu
    error.init = 1;
}