/*
 * Copyright 2002, Log4cpp Project. All rights reserved.
 *
 * See the COPYING file for the terms of usage and distribution.
 */

#if defined(LOG4CPP_HAVE_BOOST)
#include <boost/version.hpp>
#if BOOST_VERSION >= 103500

#define LOG4CPP_HAVE_INT64_T
#include <log4cpp/SmtpAppender.hh>
#include <log4cpp/FactoryParams.hh>
#include <log4cpp/HierarchyMaintainer.hh>
#include <boost/asio/ip/tcp.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/once.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/thread.hpp>
#include <boost/noncopyable.hpp>
#include <memory>

namespace log4cpp
{
   void sender_shutdown();

   struct SmptAppender::mail_params
   {
      mail_params(const std::string& host,
                  const std::string& from,
                  const std::string& to,
                  const std::string& subject) : host_(host), from_(from), to_(to), subject_(subject)
      {
      }

      std::string host_;
      std::string from_;
      std::string to_;
      std::string subject_;
   };

   namespace
   {
      struct sender : public boost::noncopyable
      {
         sender();
         static sender& instance();
         void shutdown();
         static sender* instance_;
         void send(const SmptAppender::mail_params& mp, const LoggingEvent& event);
         void operator()();
         typedef std::list<std::pair<SmptAppender::mail_params, LoggingEvent> > mails_t;

         boost::mutex mails_mutex_;
         boost::condition data_condition_;
         mails_t mails_;
         std::LOG4CPP_UNIQUE_PTR<boost::thread> sender_thread_;
         volatile bool should_exit_;
      };
      
      sender* sender::instance_ = 0;
      boost::once_flag sender_once = BOOST_ONCE_INIT;
      
      sender::sender() : should_exit_(false)
      {
         sender_thread_.reset(new boost::thread(boost::ref(*this)));   
         HierarchyMaintainer::getDefaultMaintainer().register_shutdown_handler(&sender_shutdown);
      }

      void create_sender()
      {
         sender::instance_ = new sender;
      }

      sender& sender::instance()
      {
         boost::call_once(&create_sender, sender_once);
         return *instance_;
      }

      void sender::shutdown()
      {
         {boost::mutex::scoped_lock lk(mails_mutex_);
            should_exit_ = true;
            data_condition_.notify_all();
         }

         sender_thread_->join();
         delete instance_;
      }

      void sender::send(const SmptAppender::mail_params& mp, const LoggingEvent& event)
      {
         boost::mutex::scoped_lock lk(mails_mutex_);
         mails_.push_back(mails_t::value_type(mp, event));
         data_condition_.notify_all();
      }
      
      void sender::operator()()
      {
         while(!should_exit_)
         {
            mails_t::iterator i;
            
            {boost::mutex::scoped_lock lk(mails_mutex_);
               if (should_exit_ && mails_.empty())
                  return;               

               if (mails_.empty())
               {
                  data_condition_.wait(lk);
                  if (should_exit_ && mails_.empty())
                     return;
               }

               i = mails_.begin();
            }
            
            try
            {
               boost::asio::ip::tcp::iostream s;
               s.exceptions(std::ios::failbit | std::ios::eofbit | std::ios::badbit);
               s.connect(i->first.host_, "25");
               std::string buf;
               std::getline(s, buf);
               s << "HELO test\xd\xa" << std::flush;
               std::getline(s, buf);
               s << "MAIL FROM:" << i->first.from_<< "\xd\xa" << std::flush;
               std::getline(s, buf);
               s << "RCPT TO:" << i->first.to_ << "\xd\xa" << std::flush;
               std::getline(s, buf);
               s << "DATA\xd\xa" << std::flush;
               std::getline(s, buf);
               s << "Subject: " << i->first.subject_ << "\xd\xa""Content-Transfer-Encoding: 8bit\xd\xa\xd\xa"
                  << i->second.message << "\xd\xa" ".\xd\xa" << std::flush;
               std::getline(s, buf);
               s << "QUIT\xd\xa" << std::flush;
               std::getline(s, buf);
            }
            catch(const std::exception& e)
            {
               std::cout << e.what();
            }
            
            {boost::mutex::scoped_lock lk(mails_mutex_);
               mails_.erase(i);
            }
         }
      }
   }

   void sender_shutdown()
   {
      sender::instance().shutdown();
   }

   SmptAppender::SmptAppender(const std::string& name, 
                              const std::string& host, 
                              const std::string& from,
                              const std::string& to, 
                              const std::string& subject) : LayoutAppender(name), 
                                                            mail_params_(new mail_params(host, from, to, subject))
                                                            
   {
   }

   void SmptAppender::_append(const LoggingEvent& event)
   {
      sender::instance().send(*mail_params_, event);
   }
   
   SmptAppender::~SmptAppender()
   {
      delete mail_params_;
   }

   std::LOG4CPP_UNIQUE_PTR<Appender> create_smtp_appender(const FactoryParams& params)
   {
      std::string name, host, from, to, subject;
      params.get_for("SMTP appender").required("name", name)("host", host)("from", from)
                                              ("to", to)("subject", subject);
      return std::LOG4CPP_UNIQUE_PTR<Appender>(new SmptAppender(name, host, from, to, subject));
   }
}
#endif // BOOST_VERSION >= 103500
#endif // LOG4CPP_HAS_BOOST
