// fglogger.cc

#include "fglogger.h"

#ifndef _FGEXC_H
#include "fgexc.h"
#endif

#ifndef _FGGLOB_H
#include "fgglob.h"
#endif

#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

// Threading: FGLogger is a shared object so we need to serialize access
// to it
#include <pthread.h>

// Statics
FGString FGLogger::msFileName = "fglog.out";
FGLogger* FGLogger::mspLogger = 0;

// Synchronization object
// Should be private member static but can't forward declare
// pthread_mutex_t
static pthread_mutex_t msLogMutex;

void
FGLogger::SetLogName(const FGString& name)
{
  msFileName = name;
}

FGLogger&
FGLogger::GetLogger(void)
{
  if (mspLogger == 0) {
    mspLogger = new FGLogger(msFileName);
    pthread_mutex_init(&msLogMutex, NULL);
  }

  return *mspLogger;
}

FGLogger::FGLogger(const FGString& fileName)
{
  if (!FGGlob::gLogDisabled) {
    mpFile = fopen(fileName, "a");
    if (mpFile == NULL) {
      FGString details("`");
      details += fileName;
      details += '\'';
      throw FGException(FGException::kLogFileOpenFailed, details);
    }
  } else {
    mpFile = NULL;
  }
}

void
FGLogger::LogMsg(const FGString& msg, EFGLogSeverity severity,
                 bool locked)
{
  MutexAutoUnlocker autoLocker;

  // NB. It locked is set "true", client MUST have called public
  // ::Lock() method on the logger class!
  if (!locked) {
    autoLocker.Prime(&msLogMutex);
  }

  if (FGGlob::gVerbose) {
    printf("%s\n", (const char*)msg);
  }

  if (FGGlob::gLogDisabled || severity == kFGLLVerbose) {
    return;
  }

  FGString dateStamp = GetDateStamp();

  fprintf(mpFile, "%s %s\n", (const char*)dateStamp, (const char*)msg);
  fflush(mpFile);
}

void
FGLogger::LogException(const FGException& e, bool locked)
{
  MutexAutoUnlocker autoLocker;

  // NB. It locked is set "true", client MUST have called public
  // ::Lock() method on the logger class!
  if (!locked) {
    autoLocker.Prime(&msLogMutex);
  }

  // {ce} - 07-Dec-99: oops, forgot to log this to stdout
  FGString dateStamp = GetDateStamp();
  FGString excText = e.GetFullDetails();

  if (FGGlob::gVerbose)
  {
    printf("%s\n", (const char*)excText);
  }

  if (FGGlob::gLogDisabled) {
    return;
  }

  fprintf(mpFile, "%s %s\n", (const char*)dateStamp, (const char*)excText);
  fflush(mpFile);
}

FGString
FGLogger::GetDateStamp(void) const
{
  char dateBuf[64];
  struct timeval now;

  gettimeofday(&now, NULL);
  struct tm* pTime = localtime(&now.tv_sec);

  strftime(dateBuf, sizeof(dateBuf), "[%d %b %Y %H:%M]", pTime);

  FGString ret(dateBuf);
  return ret;
}

void
FGLogger::Lock(void)
{
  pthread_mutex_lock(&msLogMutex);
}

void
FGLogger::Unlock(void)
{
  pthread_mutex_unlock(&msLogMutex);
}

// Helper classes
FGLogger::MutexAutoUnlocker::MutexAutoUnlocker()
: mpMutex(0)
{
}

FGLogger::MutexAutoUnlocker::~MutexAutoUnlocker()
{
  if (mpMutex)
  {
    pthread_mutex_unlock(static_cast<pthread_mutex_t*>(mpMutex));
  }
}

void
FGLogger::MutexAutoUnlocker::Prime(void* pMutex)
{
  mpMutex = pMutex;
  pthread_mutex_lock(static_cast<pthread_mutex_t*>(mpMutex));
}

