/*
 * Appender.cpp
 *
 * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved.
 * Copyright 2000, Bastiaan Bakker. All rights reserved.
 *
 * See the COPYING file for the terms of usage and distribution.
 */

#include "PortabilityImpl.hh"
#include <iostream>
#include <log4cpp/Appender.hh>

namespace log4cpp {
    static int appenders_nifty_counter; // zero initialized at load time
    static char
        appenderMapStorage_buf[sizeof(Appender::AppenderMapStorage)]; // memory for the nifty-counter singleton object
    Appender::AppenderMapStorage& Appender::_appenderMapStorageInstance =
        reinterpret_cast<Appender::AppenderMapStorage&>(appenderMapStorage_buf); // memory for placement new

    Appender::AppenderMapStorage::AppenderMapStorage() {
        _allAppenders = new AppenderMap();
    }
    Appender::AppenderMapStorage::~AppenderMapStorage() {
        _deleteAllAppenders();
        delete _allAppenders;
    }

    Appender::AppenderMapStorageInitializer::AppenderMapStorageInitializer() {
        if (appenders_nifty_counter++ == 0) {
// MSVC's <crtdbg.h> requires redefinition of the new operator, but could not deal with placement new form of it
#ifdef MSVC_MEMORY_LEAK_CHECK
#pragma push_macro("new")
#define new new
#endif                                                               // MSVC_MEMORY_LEAK_CHECK
            new (&_appenderMapStorageInstance) AppenderMapStorage(); // placement new
#ifdef MSVC_MEMORY_LEAK_CHECK
#pragma pop_macro("new")
#endif // MSVC_MEMORY_LEAK_CHECK
        }
    }
    Appender::AppenderMapStorageInitializer::~AppenderMapStorageInitializer() {
        if (--appenders_nifty_counter == 0) {
            (&_appenderMapStorageInstance)->~AppenderMapStorage();
        }
    }

    /* assume _appenderMapMutex locked */
    Appender::AppenderMap& Appender::_getAllAppenders() {
        return *_appenderMapStorageInstance._allAppenders;
    }

    Appender* Appender::getAppender(const std::string& name) {
        threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex);
        AppenderMap& allAppenders = Appender::_getAllAppenders();
        AppenderMap::iterator i = allAppenders.find(name);
        return (allAppenders.end() == i) ? NULL : ((*i).second);
    }

    void Appender::_addAppender(Appender* appender) {
        // REQUIRE(_allAppenders.find(appender->getName()) == _getAllAppenders().end())
        threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex);
        _getAllAppenders()[appender->getName()] = appender;
    }

    void Appender::_removeAppender(Appender* appender) {
        threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex);
        // private called from destructor only, but may be triggered by client code in several treads
        _getAllAppenders().erase(appender->getName());
    }

    bool Appender::reopenAll() {
        threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex);
        bool result = true;
        AppenderMap& allAppenders = _getAllAppenders();
        for (AppenderMap::iterator i = allAppenders.begin(); i != allAppenders.end(); i++) {
            result = result && ((*i).second)->reopen();
        }

        return result;
    }

    void Appender::closeAll() {
        threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex);
        AppenderMap& allAppenders = _getAllAppenders();
        for (AppenderMap::iterator i = allAppenders.begin(); i != allAppenders.end(); i++) {
            ((*i).second)->close();
        }
    }

    void Appender::_deleteAllAppenders() {
        // deleting each appenders will cause a lock on Appender::_appenderMapMutex to be obtained again within
        // destructor. to avoid nested locks:
        std::vector<Appender*> appenders;
        {
            threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex);
            AppenderMap& allAppenders = _getAllAppenders();
            appenders.reserve(allAppenders.size());
            for (AppenderMap::iterator i = allAppenders.begin(); i != allAppenders.end();) {
                Appender* app = (*i).second;
                ++i;
                appenders.push_back(app);
            }
            allAppenders.clear();
        }
        _deleteAllAppendersWOLock(appenders);
    }

    void Appender::_deleteAllAppendersWOLock(std::vector<Appender*>& appenders) {
        /* assume Appender::_appenderMapMutex not locked */
        AppenderMap& allAppenders = _getAllAppenders();
        for (std::vector<Appender*>::iterator i = appenders.begin(); i != appenders.end(); ++i) {
            Appender* app = (*i);
            delete (app);
        }
    }

    Appender::Appender(const std::string& name) : _name(name) {
        _addAppender(this);
    }

    Appender::~Appender() {
        _removeAppender(this);
    }
} // namespace log4cpp
