// Copyright 2005 Rutger E.W. van Beusekom.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include "logstream.hpp"

#include <iostream>

#include <boost/thread/tss.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;

class nullstream: public ostream
{
public:
  nullstream(): ostream(0) {}
};

class thread_local
{
  static boost::mutex mutex_;
  static unsigned thread_id_;
public:
  logstream logstream_;
  nullstream nullstream_;
  unsigned thread_;
  thread_local()
  : logstream_()
  , thread_(0)
  {
    boost::mutex::scoped_lock lock(mutex_);
    thread_ = thread_id_++;
    lock.unlock();
    
    logstream_ << "****** " << boost::posix_time::second_clock::local_time()
               << " thread(" << thread_ << ") started ******"
               << endl;
  }
  ~thread_local()
  {
    logstream_ << "***** " << boost::posix_time::second_clock::local_time()
               << " thread(" << thread_ << ") stopped ******"
               << endl;
  }
};

boost::mutex thread_local::mutex_;
unsigned thread_local::thread_id_ = 0;

boost::thread_specific_ptr<thread_local> thread_local_;

qualifier info ("[I]");
qualifier warn ("[W]");
qualifier error("[E]");
qualifier trace("[T]");
qualifier debug("[D]");

////////////////////////////////////////////////////////////////////////////////
//  logger
////////////////////////////////////////////////////////////////////////////////
boost::mutex logger::mutex_;

void logger::log(const char* msg)
{
  boost::mutex::scoped_lock lock(mutex_);
  cout << msg << flush;
}
ostream& logger::acquire(bool enabled)
{
  if(thread_local_.get() == 0)
  {
    thread_local_.reset(new thread_local());
  }
  if(enabled)
  {
    return thread_local_->logstream_;
  }
  else
  {
    return thread_local_->nullstream_;
  }
}

////////////////////////////////////////////////////////////////////////////////
//  qualifier
////////////////////////////////////////////////////////////////////////////////
qualifier::qualifier(const char* type)
: type_(type)
, enabled_(true)
{
}
qualifier::~qualifier()
{
}
unsigned qualifier::thread_id() const
{
  return thread_local_->thread_;
}
ostream& qualifier::qualify() const
{
  ostream& os = logger::acquire(enabled_);
  os << *this;
  return os;
}
boost::posix_time::time_duration qualifier::time_stamp() const
{
  return boost::posix_time::microsec_clock::local_time().time_of_day();
}
ostream& operator << (ostream& os, const qualifier& q)
{
  os << q.type() << ' ' << q.time_stamp() << " (" << q.thread_id() << ") : ";

  return os;
}
////////////////////////////////////////////////////////////////////////////////
//  logstream::logstreambuf
////////////////////////////////////////////////////////////////////////////////
logstream::logstreambuf::logstreambuf()
: basic_streambuf<char, char_traits<char> >()
, buffer_(4)
{
  setp(begin(), end());
}
logstream::logstreambuf::~logstreambuf()
{
  if(pptr() != begin())
  {
    sync();
  }
}
streamsize logstream::logstreambuf::xsputn(const char* p, streamsize n)
{
  if(pptr() + n >= epptr())
  {
    unsigned offset = pptr() - pbase();
    buffer_.resize(buffer_.size() + n - (epptr() - pptr()));
    setp(begin(), end());
    pbump(offset);
  }

  copy(p, p+n, pptr());
  pbump(n);

  return n;
}
int logstream::logstreambuf::sync()
{
  if(pptr() + 1 >= epptr())
  {
    buffer_.push_back(0);
    setp(begin(), end());
  }
  else
  {
    *pptr() = 0;
  }

  streamsize n = pptr() - pbase();
  pbump(-n);
  
  logger::log(begin());
    
  return n;
}
int logstream::logstreambuf::overflow(int i)
{
  unsigned offset = pptr() - pbase();
  buffer_.push_back(i);
  setp(begin(), end());
  pbump(offset + 1);

  return i;
}
char* logstream::logstreambuf::begin()
{
  return &buffer_.front();
}
char* logstream::logstreambuf::end()
{
  return &buffer_.back() + 1;
}

////////////////////////////////////////////////////////////////////////////////
//  logstream
////////////////////////////////////////////////////////////////////////////////
logstream::logstream()
: basic_ostream<char>(&streambuf_)
, streambuf_()
{
}
logstream::~logstream()
{
}
