////////////////////////////////////////////////////////////////////////////////
// 
// Message.hh 
//
//    produced: 2024-05-04 jr
//
////////////////////////////////////////////////////////////////////////////////
#ifndef MESSAGE_HH
#define MESSAGE_HH

#include <iostream>
#include <mutex>
#include <ctime>

#include "ContainerIO.hh"

namespace topcom {

  class IO_sync {
  public:
    static std::mutex mutex;
  };
  
  class Message {
  private:
    std::ostream*                       _ostptr;
  public:
    inline Message() : _ostptr(nullptr) {}
    inline Message(std::ostream& ost, bool activate = true) : _ostptr(activate ? &ost : nullptr) {}
    inline Message(std::ostream* ostptr, bool activate = true) : _ostptr(activate ? ostptr : nullptr) {}
    inline Message& activate(std::ostream& ost) { _ostptr = &ost; return *this; }
    inline Message& deactivate() { _ostptr = nullptr; return *this; }
    inline bool is_active() const { return (_ostptr != nullptr); }

    inline void lock() { IO_sync::mutex.lock(); }
    inline void unlock() { IO_sync::mutex.unlock(); }

    // short-cut functions for constant strings:
    Message& print_topcomheader();  // not inline because it uses configure variables
    
    inline Message& print_hseparator() {
      *this << "---------------------------------"
	    << "---------------------------------"
	    << "---------------------------------"
	    << "---------------------------------"
	    << "---------------------------------"
	    << "-------------------" << std::endl;
      return *this;
    }

    inline Message& print_dumpseparator() {
      *this << "#################################"
	    << "#################################"
	    << "#################################"
	    << "#################################"
	    << "#################################"
	    << "###################" << std::endl;
      return *this;
    }

    inline Message& print_timestamp() {
      std::time_t t = std::time(0);
      std::tm* now = std::localtime(&t);
      *this << "                    >>> "
	    << (now->tm_year + 1900) << '-'
	    << (now->tm_mon + 1) << '-'
	    <<  now->tm_mday << ' '
	    << now->tm_hour << ':'
	    << now->tm_min << ':'
	    << now->tm_sec
	    << " <<<                    "
	    << std::endl;
      return *this;
    }
    
    // friends for stream output:
    template <class T>
    friend Message& operator<<(Message&, const T&);
    friend Message& operator<<(Message&, std::ostream&(*)(std::ostream&));
  };

  template <class T>
  inline Message& operator<<(Message& msg, const T& output) {
    if (msg.is_active()) {
      *(msg._ostptr) << output;
    }
    return msg;
  }

  inline Message& operator<<(Message& msg, std::ostream&(*fp)(std::ostream&)) {
    if (msg.is_active()) {
      (*fp)(*msg._ostptr);
    }
    return msg;
  }

  class MessageStreams {
  private:
    static Message _result;
    static Message _forced;
    static Message _verbose;
    static Message _debug;
  public:
    static Message& result () { return _result;  }
    static Message& forced () { return _forced;  }
    static Message& verbose() { return _verbose; }
    static Message& debug  () { return _debug;   }
  };
  
  namespace message {

    // the following function define stream manipulators for messages:
    inline std::ostream& lock(std::ostream& ost) {
      ost.flush();
      IO_sync::mutex.lock();
      return ost;
    }

    inline std::ostream& unlock(std::ostream& ost) {
      ost.flush();
      IO_sync::mutex.unlock();
      return ost;
    }
    
    inline std::ostream& tab(std::ostream& ost) {
      ost << '\t';
      return ost;
    }
  }; // namespace message
  
}; // namespace topcom



#endif

// eof Message.hh
