/*
 *  filelog.c - Logging mechanism
 *
 *  Copyright (C) 1999 Hugo Haas
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>

#include "log.h"

extern struct loginfo log;

/* Mutex either for stdout or for a file */
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;

/* Variables used for last message repetition */
char last_message[BUFFER_SIZE] = "";
time_t last_repeat;
unsigned int repeats = 0;

/*
 * stdout_loginfo
 *
 * When run with debug options, write everything to stdout (run_as_daemon is 0)
 */

void stdout_loginfo(struct loginfo *li) {

  li->log = filedesc_log;
  li->level_or_fd = 1;
  if (li->file != NULL)
    free(li->file);
  li->file = NULL;
  li->open = dummy_open;
  li->close = dummy_close;
}

/*
 * init_loginfo
 *
 * Initializes a log_info structure (uses syslog)
 */

void init_loginfo(struct loginfo *li) {
  extern int run_as_daemon;

  if (!run_as_daemon) {
    stdout_loginfo(li);
    return;
  }
  li->log = (void *) syslog;
  li->level_or_fd = LOGLEVEL;
  if (li->file != NULL)
    free(li->file);
  li->file = NULL;
  li->open = syslog_open;
  li->close = dummy_close;
}

/*
 * log_in_file
 *
 * Set a loginfo structure such as it will log things in a file
 */

void log_in_file(struct loginfo *li, char *filename) {
  extern int run_as_daemon;

  if (!run_as_daemon) {
    stdout_loginfo(li);
    return;
  }
  li->log = filedesc_log;
  li->level_or_fd = 0;
  li->open = file_open;
  li->close = file_close;
  if (li->file != NULL)
    free(li->file);
  li->file = strdup(filename);
}

/*
 * dummy_open, dummy_close
 *
 * Dummy functions
 */

void dummy_open(int *fd, const char *filename) {}
void dummy_close(int *fd, const char *filename) {}

/*
 * syslog_open
 *
 * Open a connection to the system logging daemon. Register ippl as a daemon.
 */

void syslog_open(int *level, const char *filename) {

  openlog("ippl", 0, LOG_DAEMON);
}

/*
 * log_entry
 *
 * Log an entry into a stream
 */

void log_entry(int fd, char *entry) {
  time_t current = time(NULL);
  char date[27];
  char repeat_message[40];

  if (fd == 0)
    return;

  pthread_mutex_lock(&log_mutex);

  if (!strncmp(last_message, entry, BUFFER_SIZE)) {
    repeats++;
    last_repeat = current;
    pthread_mutex_unlock(&log_mutex);
    return;
  }

  if (repeats > 0) {
    snprintf(date, 27, asctime(localtime(&last_repeat)));
    snprintf(repeat_message, 40, "last message repeated %d time(s)\n", repeats);
    write(fd, date+4, strlen(date)-10);
    write(fd, " ", 1);
    write(fd, repeat_message, strlen(repeat_message));
    repeats = 0;
  }

  snprintf(date, 27, asctime(localtime(&current)));
  write(fd, date+4, strlen(date)-10);
  write(fd, " ", 1);
  write(fd, entry, (strlen(entry) < 1023) ? strlen(entry) : 1023 );
  write(fd, "\n", 1);

  strncpy(last_message, entry, BUFFER_SIZE);

  pthread_mutex_unlock(&log_mutex);
}

/*
 * filedesc_log
 *
 * Log information into a file descriptor
 */

void filedesc_log(const int fd, char *format, ...) {
  char buffer[BUFFER_SIZE];
  va_list msg;

  va_start(msg, format);
  vsnprintf(buffer, BUFFER_SIZE, format, msg);
  va_end(msg);

  log_entry(fd, buffer);
}

/*
 * file_open
 *
 * Open a log file
 */

void file_open(int *fd, const char *filename) {

  *fd = open((const char *) filename, O_WRONLY | O_APPEND | O_CREAT, 0640);
  if (*fd == 0) {
    log.log(log.level_or_fd, "Can't open log file %s.", filename);
  }
}

/*
 * file_close
 *
 * Close a log file
 */

void file_close(int *fd, const char *filename) {
 
  fsync(*fd);
  close(*fd);
}        
