#include <stdio.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>


#include <signal.h>

#include <string.h>
#include <time.h>

#include "configuration.h"
#include "logger.h"
#include "utilities.h"

#define handle(a,mess)   \
    if(a){                          \
        perror(mess);               \
    }

#define DEFAULT_LEVEL         INFO
#define DEFAULT_OUTPUT        stdout
#define BASIC_PATTERN "%s[%s] <%s> : '%s'"
#define DATE_ADDON_PATTERN    "%s "

/*
+------+-------------------------------------------------------------+
| Code | Format correspondant                                        |
+------+-------------------------------------------------------------+
|  %a  | Nom du jour en abrégé                                       |
|  %A  | Nom du jour complet                                         |
|  %b  | Nom du mois en abrégé                                       |
|  %B  | Nom du mois complet                                         |
|  %c  | MM/JJ/AA HH:MM:SS                                           |
|  %d  | Numéro du jour dans le mois (01 à 31)                       |
|  %H  | Heure sur 24 heures (00 à 23)                               |
|  %I  | Heure sur 12 heures (01 à 12)                               |
|  %m  | Numéro du mois dans l'année (01 à 12)                       |
|  %M  | La minute (0 à 59)                                          |
|  %p  | AM ou PM suivant la partie de la journée                    |
|  %S  | La seconde (0 à 59)                                         |
|  %u  | Numéro du jour de la semaine (1 (lun.) à 7 (dim.))          |
|  %w  | Numéro du jour de la semaine (0 (dim.) à 6 (sam.))          |
|  %x  | MM/JJ/AA                                                    |
|  %X  | HH:MM:SS                                                    |
|  %y  | Année sur deux chiffres                                     |
|  %Y  | Année sur quatres chiffres                                  |
|  %Z  | Nom du fuseau horaire                                       |
|  %%  | Le caractère %                                              |
+------+-------------------------------------------------------------+
*/
#define DATE_PATTERN    "%a %b %d %H:%M:%S %Y"

struct _logger{
    LogLevel _level;

    int      _queue_id;

// Logger is on
    boolean  _activated;

    boolean _initialized;

    void   (*format)   (char *output, LogLevel lvl, char *header , char *message);
};

// singleton
static Logger singleton;

static
void writeTabulation(char * buffer, LogLevel lvl)
{
	int tabSize = 0,i;
	switch(lvl)
	{
	case DEBUG3:
		tabSize = 4;
		break;
	case DEBUG2:
		tabSize = 3;
		break;
	case DEBUG1:
		tabSize = 2;
		break;
	case INFO:
		tabSize = 1;
		break;
	case WARNING:
	case SEVERE:
	default:
		tabSize = 0;
		break;
	}

	for(i=0;i<tabSize;i++)
	{
		strcat(buffer,"\t");
	}
}

static
void basicFormatter(char *output, LogLevel lvl, char *header, char *message)
{
	char tabulation[10] = {0};

	writeTabulation(tabulation,lvl);

    sprintf(output,
            BASIC_PATTERN,
            tabulation,
            printLogLevel(lvl),
            header,
            message);
}

/*
static
void dateLog(LogLevel lvl)
{
    //date
    static char date_buffer[256];
    time_t timestamp = time(NULL);

    strftime(date_buffer,sizeof(date_buffer),DATE_PATTERN, localtime(&timestamp));

    //Problem here !
    fprintf(
            singleton._output,
            singleton._output_pattern,
            date_buffer,
            printLogLevel(lvl),
            singleton._header,
            singleton._message);
}
*/

static void createBasicLogger()
{
    key_t queue_id = ftok(SECRETS_FILE,LOGGER_QUEUE_ID);

    singleton._queue_id = msgget(queue_id, IPC_CREAT);

    singleton._level    = DEFAULT_LEVEL;
    singleton.format    = basicFormatter;

    singleton._activated   = TRUE;
    singleton._initialized = TRUE;

    printf("Logger initialized\n");
}

// Public
void logMessage(LogLevel level,char* header,char* description)
{
    if( singleton._activated )
    {
        if( level >= singleton._level )
        {
            MessageBuffer message;

            message.mtype = 1; // HELP Default Message

            singleton.format(
                    message.mtext,
                    level,
                    header,
                    description);

            // On l'écrit sur la file d'attente
            handle(
                    msgsnd(
                        singleton._queue_id,
                        (MessageBuffer *)&message,
                        MAX_STRING_INPUT,
                        NO_FLAG) == -1,
                    "error during message sending");
        }
    }
}

LogLevel getLoggerLevel()
{
    return ( singleton._activated ) ? singleton._level : -1;
}

void     setLoggerLevel(LogLevel level)
{
    if( singleton._activated )
        singleton._level = level;
}

char*    printLogLevel(LogLevel level){
    static char* levels[9] = {
                "ALL",
                "DEBUG3",
                "DEBUG2",
                "DEBUG1",
                "INFO",
                "NORMAL",
                "NONE",
                "WARNING",
                "SEVERE"
    };

    if( level > 0)
    	return levels[level / 100];
    else
    	return "UNKNOWN";
}

void  activateLogger()
{
    if( !singleton._initialized )
        createBasicLogger();
}

void  desactivateLogger()
{
    singleton._activated = FALSE;
}

void  makeDateLogger()
{
	key_t logger_shm_id = ftok(SECRETS_FILE,LOGGER_PID);
	int shmid = shmget(logger_shm_id,sizeof(pid_t),0);

	if( shmid < 0)
	{
		logMessage(SEVERE,"shm access","can't access to shm with logger pid");
		return;
	}

	int *logger_pid = (int *)shmat(shmid,NULL,SHM_RDONLY);

	kill(*logger_pid,SIGUSR2);

	shmdt(logger_pid);
}

void killLoggerProcess() {
	key_t logger_shm_id = ftok(SECRETS_FILE,LOGGER_PID);
	int shmid = shmget(logger_shm_id,sizeof(pid_t),0);

	if( shmid < 0)
	{
		logMessage(SEVERE,"shm access","can't access to shm with logger pid");
		return;
	}

	int *logger_pid = (int *)shmat(shmid,NULL,SHM_RDONLY);

	kill(*logger_pid,SIGUSR1);

	shmdt(logger_pid);
}
