/*
 * --- GSMP-COPYRIGHT-NOTE-BEGIN ---
 * 
 * This copyright note is auto-generated by ./scripts/Create-CopyPatch.
 * Please add additional copyright information _after_ the line containing
 * the GSMP-COPYRIGHT-NOTE-END tag. Otherwise it might get removed by
 * the ./scripts/Create-CopyPatch script. Do not edit this copyright text!
 * 
 * GSMP: utility/include/Logger.hh
 * General Sound Manipulation Program is Copyright (C) 2000 - 2004
 *   Valentin Ziegler and René Rebe
 * 
 * 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; version 2. A copy of the GNU General
 * Public License can be found in the file LICENSE.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT-
 * ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * 
 * --- GSMP-COPYRIGHT-NOTE-END ---
 */
#ifndef UTILITY__LOGGER_HH__
#define UTILITY__LOGGER_HH__

#include "SplitStreamBuffer.hh"
#include <fstream>
#include "TypeInformation.hh"

namespace Utility
{
  class LogDeviceConfig
  {
  public:
    static const bool disabled = false;                   // no logging on this device at all
    static const bool thread_save = false;                // not supported yet...
    static const bool do_timestamping = true;             // print nummeric timestamps after writing to a different logdevice
    static const bool print_log_prelude = true;           // print time and date when creating/switching logfile
    //TODO:
    static const bool output_logfile_acces_errors = true; // print message to stderr on write errors
    static const bool redirect_to_stdout_on_error = true; // redirect all content to stdout after printing error message
  };


  class LogDestinationConfig
  {
  public:
    static const bool echo_log_stdout = false;            // log output will be branched to stdout
    static const bool log_to_file = true;                 // log output will be branched into file
    static const bool function_names_in_log = false;      // log messages will be prefixed with pretty function name
    static const bool context_in_log = false;             // log messages will be prefixed with context string

    static const bool echo_warn_stderr = true;            // warn options in analogy to log options
    static const bool warn_to_file = true;
    static const bool function_names_in_warn = true;
    static const bool context_in_warn = true;
  };


  // WL_TRAITS predefined configs
  class WL_Quiet
  {
  public:
    static const bool warn = false;
    static const bool log = false;
  };

  class WL_Warn
  {
  public:
    static const bool warn = true;
    static const bool log = false;
  };

  class WL_Verbose
  {
  public:
    static const bool warn = true;
    static const bool log = true;
  };



  class BasicLogDevice
  {
  protected:
    typedef uint64_t timestamp_t;
    typedef enum {std,file,both} direction;

    static timestamp_t current;
    static direction last_direction;
    static BasicLogDevice* last_device;
    

    BasicLogDevice ();

    bool TimeStamp (direction d);
    void PrintPrelude ();
    void PrintTimeStamp (std::ostream* str);

    void AllocateSplitStreams ();
    void DeallocateSplitStreams ();

    SplitStreamBuffer* split_buffer_cout;
    SplitStreamBuffer* split_buffer_cerr;
    
    std::ostream* split_stream_cout;
    std::ostream* split_stream_cerr;

    std::ofstream* log_file;
  };


  template <typename LDEVC = LogDeviceConfig>
  class LogDevice : public BasicLogDevice
  {
  public:
    typedef LDEVC Config;

    LogDevice (std::ofstream& logfile);
    LogDevice ();
    ~LogDevice ();

    void SwitchLogFile (std::ofstream& logfile);
    void NoLogFile ();

    template <typename LDESC>
    std::ostream& SplitLog ();

    template <typename LDESC>
    std::ostream& SplitWarn ();

  protected:
    direction DetermineDirection (const bool to_std, const bool to_file);
  };


  template <typename LDESC, typename LDEVC>
  class LogDestination
  {
  public:
    typedef LogDevice <LDEVC> Device;
    typedef typename LogDevice <LDEVC>::Config DeviceConfig;

    LogDestination (std::string i_context, Device& i_device);

    inline bool DoLog ()  {return (!LDEVC::disabled) && (LDESC::echo_log_stdout || LDESC::log_to_file);}
    inline bool DoWarn () {return (!LDEVC::disabled) && (LDESC::echo_warn_stderr || LDESC::warn_to_file);}

    std::ostream& Log ();

    std::ostream& Warn ();

  protected:
    std::string context;
    Device& device;
  };


  template <typename LDESC, typename LDEVC, typename WL_TRAITS>
  class Logger
  {
  public:
    typedef LogDestination <LDESC, LDEVC> Destination;
    Logger (Destination& i_destination);
  
    inline bool DoLog () {return destination.DoLog () && WL_TRAITS::log; }
    inline bool DoWarn () {return destination.DoLog () && WL_TRAITS::warn; }

    std::ostream& Log (const char* pretty_function_name);
    std::ostream& Warn (const char* pretty_function_name);
  private:
    Destination& destination;
  };


  template <typename LDESC, typename LDEVC, typename WL_TRAITS, typename OBJ>
  class ObjectLogger : public Logger <LDESC, LDEVC, WL_TRAITS>
  { 
  public:
    typedef typename Logger <LDESC, LDEVC, WL_TRAITS>::Destination Destination;
    ObjectLogger (Destination& i_destination, OBJ* i_parent);
    ~ObjectLogger ();

    std::ostream& Log (const char* pretty_function_name);
    std::ostream& Warn (const char* pretty_function_name);
  private:
    OBJ* parent;
  };


  // we export cout and cerr to avoid <iostream> inclusion
  extern std::ostream& wrap_cout;
  extern std::ostream& wrap_cerr;

} // end namespace Utility


// now we do some nasty preprocessor stuff...

#ifdef Q_LOG
#  ifndef UTILITY__CUSTOM_Q_LOG__
#    warning "the Q_LOG macro is already defined"
#  endif
#else
#  define Q_LOG(logger)    if (logger.DoLog ()) logger.Log (__PRETTY_FUNCTION__)
#endif

#ifdef Q_WARN
#  ifndef UTILITY__CUSTOM_Q_WARN__
#    warning "the Q_WARN macro is already defined"
#  endif
#else
#  define Q_WARN(logger)   if (logger.DoWarn ()) logger.Warn (__PRETTY_FUNCTION__)
#endif

#define UTILITY__LOGGER_TMPL__
#include "template/Logger.tcc"
#undef UTILITY__LOGGER_TMPL__

#endif // UTILITY__LOGGER_HH__

