module logging;

// Simple logging facility

private:

  import std.format,std.string, std.file, std.stdio;

  char[]                basedir = ".";
  LogFile[ char[] ]     named_logs;

  class LogFile
  {
    char[]  id;
    char[]  buffer;
    int     flush_after = 1024;
    uint    total_written = 0;
    int     max_size = int.max;

    this( char[] _id )
    {
      id = strip(_id); if ( id=="" ) id="default";
      remove_old_file();
    }

    char[]  filename()  { return basedir ~ "/" ~ id ~ ".log" ; }
    char[]  backup()    { return basedir ~ "/" ~ id ~ ".bak" ; }

    void    remove_old_file()
    {
      if ( std.file.exists( filename )==false ) return;
      if ( std.file.exists( backup ) )
      {
        if ( std.file.isdir( backup ) ) return;
        std.file.remove( backup );
      }
      std.file.rename( filename, backup );
    }

    void    write( TypeInfo[] types, void* argptr )
    {
      if ( total_written > max_size ) return;
      char[] line = "";
      int index = 0;

      try {
        void accept( dchar c ) { line~= c; }
        doFormat( &accept, types, argptr );
      }
      catch( Exception e ) { writefln("Exception writing log: %s", e.msg ); }

      buffer ~= (line ~ '\n' );
      total_written += line.length;

      if ( ( total_written >= max_size )||
           ( buffer.length >= flush_after ) ) flush();
    }

    private void flush()
    {
      if ( buffer.length==0 ) return;
      std.file.append( filename, cast(void[])buffer );
      buffer.length = 0;
    }
  }


LogFile     findLog( char[] id )
{
  id = tolower( strip( id ) );
  LogFile* ptr = (id in named_logs );
  if ( ptr !is null ) return ptr[0];

  LogFile logfile = new LogFile( id );
  named_logs[id] = logfile;
  return logfile;
}


static ~this()
{
  FlushLogs();
}

public:

void FlushLogs()
{
  foreach( char[] id, LogFile log; named_logs ) log.flush();
}

void  SetLoggingDir( char[] dir )
{
  if ( !isdir( dir ) ) throw new Exception( format( "Logging Directory '%s' does not exist!", dir ) );
  while((dir.length>0) && (dir[ dir.length-1 ]=='/') ) dir.length = dir.length-1;
  basedir = dir.dup;
}

void  SetLogBufferSize( uint sz ) {
  foreach( char[] n, LogFile f; named_logs ) f.flush_after = sz;
}


void  SetLogBufferSize( char[] id, uint sz ) {
  findLog( id ).flush_after = sz;
}


void  SetMaxLogSize( uint sz ) {
  foreach( char[] n, LogFile f; named_logs ) f.max_size = sz;
}


void  SetMaxLogSize( char[] id, uint sz ) {
  findLog(id).max_size = sz;
}


void  Log( char[] id, ... )
{
  findLog(id).write( _arguments, _argptr );
}


// typical logging helper functions
void LogInfo( ... )
{
  findLog( "info" ).write( _arguments, _argptr );
}

void LogWarning( ... )
{
  findLog( "warning" ).write( _arguments, _argptr );
}

void LogError( ... )
{
  findLog( "error" ).write( _arguments, _argptr );
}

void LogFatal( ... )
{
  findLog( "fatal" ).write( _arguments, _argptr );
}


void  DebugLogs()
{
  writefln( "Begin Log Debug Info *****" );
  foreach( char[] n, LogFile log ; named_logs )
  {
    writefln( "Log: %s", n );
    writefln( "Max size: %s", log.max_size );
    writefln( "Chars written: %s", log.total_written );
    writefln( "Chars in buffer: %s", log.buffer.length );
    writefln( "Flush after %s:", log.flush_after );
    writefln();
  }
  writefln( "End Log Debug Info *****" );
}
