// ****************************************************************************
//  Project:        libguytools
// ****************************************************************************
//  Programmer:     Guy Voncken
//                  Police Grand-Ducale
//                  Service de Police Judiciaire
//                  Section Nouvelles Technologies
// ****************************************************************************
//  Module:         Log utility
// ****************************************************************************

// Copyright 2008, 2009, 2010, 2011, 2012 Guy Voncken
//
// This file is part of libguytools.
//
// libguytools 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.
//
// libguytools 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 libguytools. If not, see <http://www.gnu.org/licenses/>.

#ifndef _FILE_OFFSET_BITS
   #define _FILE_OFFSET_BITS 64
#endif

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>

#include <qmutex.h>

#include "toolglobalid.h"
#include "toolerror.h"
#include "toollog.h"


// -----------
//  Constants
// -----------

const char * LogLevelLookup [t_Log::Entries] = {"Debug", "Info", "Error"};
const int    LOG_HEADER_LEN = 75;

const char   LibGuyToolsVersion[] = LIBGUYTOOLS_VERSION;

// -----------------
//  Local variables
// -----------------

static bool    LogInitialised = false;
static QMutex  LogMutex;
static char  *pLogFilename;
static char    LogLineHeader[512];


t_Log::t_Log (const char* pFileName, APIRET &rc)
{
   if (!LogInitialised)
   {
      LogInitialised = true;
      pLogFilename = (char *) malloc (strlen (pFileName)+1);
      strcpy (pLogFilename, pFileName);
      Entry (Info, __FFL__, "Log file opened");
      rc = NO_ERROR;
   }
   else
   {
      rc = TOOLLOG_ERROR_INITIALISED_TWICE;
   }
}


t_Log::~t_Log(void)
{
   Entry (Info, __FFL__, "Log file closed");
   free (pLogFilename);
   LogInitialised = false;
}

bool t_Log::IsInitialised (void)
{
   return LogInitialised;
}


void t_Log::GetLibGuyToolsVersion (const char **pVersion)
{
   *pVersion = LibGuyToolsVersion;
}

void t_Log::vEntry (t_Level Level, const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, va_list pArguments)
{
   time_t      NowT;
   struct tm *pNowTM;
   FILE      *pFile;
   int         wr;
   static bool LogFileError = false;  // Variable prevents us from repetitive log error messages

   time (&NowT);
   pNowTM = localtime (&NowT);
   LogMutex.lock();
      wr  = strftime (&LogLineHeader[0] , sizeof(LogLineHeader)   , "%a %d.%b.%Y %H:%M:%S ", pNowTM);
      wr += snprintf (&LogLineHeader[wr], sizeof(LogLineHeader)-wr, "%08X ", (unsigned int)pthread_self());
//      wr += snprintf (&LogLineHeader[wr], sizeof(LogLineHeader)-wr, "%4d ", getpid());  // getpid returns alyways the same number


      if (pFileName && pFunctionName)
         wr += snprintf (&LogLineHeader[wr], sizeof(LogLineHeader)-wr, "%s %s %d", pFileName, pFunctionName, LineNr);

      while (wr < LOG_HEADER_LEN)
         LogLineHeader[wr++] = ' ';
      snprintf (&LogLineHeader[wr], sizeof(LogLineHeader)-wr, "%-5s - ", LogLevelLookup[Level]);


      if (LogInitialised)
      {
         pFile = fopen64 (pLogFilename, "a");
         if (pFile == NULL)
         {
            if (!LogFileError)
               printf ("\nLog file error: Can't be opened");
            LogFileError = true;
         }
         else
         {
            LogFileError = false;
            fprintf  (pFile, "%s", &LogLineHeader[0]);
            vfprintf (pFile, pFormat, pArguments);
            fprintf  (pFile, "\n");
            fclose   (pFile);
         }
      }
      if (!LogInitialised || (pFile == NULL))
      {
         printf  ("\n");
         printf  ("%s", &LogLineHeader[0]);
         vprintf (pFormat, pArguments);
      }
   LogMutex.unlock();
}

void t_Log::Entry (t_Level Level, const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, ...)
{
   va_list VaList;

   va_start(VaList, pFormat);
   vEntry (Level, pFileName, pFunctionName, LineNr, pFormat, VaList);
   va_end(VaList);
}

void t_Log::vEntryInfo (const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, va_list pArguments)
{
   vEntry (Info, pFileName, pFunctionName, LineNr, pFormat, pArguments);
}

void t_Log::vEntryDebug (const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, va_list pArguments)
{
   vEntry (Debug, pFileName, pFunctionName, LineNr, pFormat, pArguments);
}

void t_Log::vEntryError (const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, va_list pArguments)
{
   vEntry (Error, pFileName, pFunctionName, LineNr, pFormat, pArguments);
}


