/*
 * 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 <boost/asio/ip/tcp.hpp>
#include <boost/noncopyable.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/once.hpp>
#include <boost/thread/thread.hpp>
#include <log4cpp/FactoryParams.hh>
#include <log4cpp/HierarchyMaintainer.hh>
#include <log4cpp/SmtpAppender.hh>
#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);
                }
            }
        }
    } // namespace

    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));
    }
} // namespace log4cpp
#endif // BOOST_VERSION >= 103500
#endif // LOG4CPP_HAS_BOOST
