/*
 * SimpleConfigurator.cpp
 *
 * Copyright 2001, Glen Scott. All rights reserved.
 *
 * See the COPYING file for the terms of usage and distribution.
 */
#include "PortabilityImpl.hh"

#ifdef LOG4CPP_HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef LOG4CPP_HAVE_IO_H
#include <io.h>
#endif

#include <fstream>
#include <iostream>
#include <stdio.h>
#include <string>

#include <log4cpp/Appender.hh>
#include <log4cpp/BasicLayout.hh>
#include <log4cpp/Category.hh>
#include <log4cpp/DailyRollingFileAppender.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/Layout.hh>
#include <log4cpp/NDC.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Priority.hh>
#include <log4cpp/RollingFileAppender.hh>
#include <log4cpp/SimpleConfigurator.hh>
#include <log4cpp/SimpleLayout.hh>
#if LOG4CPP_HAVE_SYSLOG
#include <log4cpp/SyslogAppender.hh>
#endif
#ifndef LOG4CPP_DISABLE_REMOTE_SYSLOG
#include <log4cpp/RemoteSyslogAppender.hh>
#endif
#ifdef WIN32
#include <log4cpp/NTEventLogAppender.hh>
#endif

namespace log4cpp {

    void SimpleConfigurator::configure(const std::string& initFileName) {
        std::ifstream initFile(initFileName.c_str());

        if (!initFile) {
            throw ConfigureFailure(std::string("Config File ") + initFileName + " does not exist or is unreadable");
        }

        configure(initFile);
    }

    void SimpleConfigurator::configure(std::istream& initFile) {
        std::string nextCommand;
        std::string categoryName;

        while (initFile >> nextCommand) {
            /* skip comment lines */
            if (nextCommand[0] == '#') {
                std::string dummy;
                std::getline(initFile, dummy);
                continue;
            }
            /* stop on missing categoryName */
            if (!(initFile >> categoryName))
                break;

            log4cpp::Category& category = (categoryName.compare("root") == 0)
                                              ? log4cpp::Category::getRoot()
                                              : log4cpp::Category::getInstance(categoryName);

            if (nextCommand.compare("appender") == 0) {
                std::string layout;
                std::string appenderName;

                if (initFile >> layout >> appenderName) {
                    log4cpp::Appender* appender;
                    if (appenderName.compare("file") == 0) {
                        std::string logFileName;
                        if (!(initFile >> logFileName)) {
                            throw ConfigureFailure(
                                "Missing filename for log file logging configuration file for category: " +
                                categoryName);
                        }
                        appender = new log4cpp::FileAppender(categoryName, logFileName);
                    } else if (appenderName.compare("rolling") == 0) {
                        std::string logFileName;
                        size_t maxFileSize;
                        unsigned int maxBackupIndex = 1;
                        if (!(initFile >> logFileName)) {
                            throw ConfigureFailure(
                                "Missing filename for log file logging configuration file for category: " +
                                categoryName);
                        }
                        if (!(initFile >> maxFileSize)) {
                            throw ConfigureFailure(
                                "Missing maximum size for log file logging configuration file for category: " +
                                categoryName);
                        }
                        if (!(initFile >> maxBackupIndex)) {
                            throw ConfigureFailure(
                                "Missing maximum backup index for log file logging configuration file for category: " +
                                categoryName);
                        }
                        appender =
                            new log4cpp::RollingFileAppender(categoryName, logFileName, maxFileSize, maxBackupIndex);
                    } else if (appenderName.compare("dailyrolling") == 0) {
                        std::string logFileName;
                        unsigned int maxKeepDays = 1;
                        if (!(initFile >> logFileName)) {
                            throw ConfigureFailure(
                                "Missing filename for log file logging configuration file for category: " +
                                categoryName);
                        }
                        if (!(initFile >> maxKeepDays)) {
                            throw ConfigureFailure(
                                "Missing maximum keep days for log file logging configuration file for category: " +
                                categoryName);
                        }
                        appender = new log4cpp::DailyRollingFileAppender(categoryName, logFileName, maxKeepDays);
                    } else if (appenderName.compare("console") == 0) {
                        appender = new log4cpp::OstreamAppender(categoryName, &std::cout);
                    } else if (appenderName.compare("stdout") == 0) {
                        appender = new log4cpp::FileAppender(categoryName, ::dup(fileno(stdout)));
                    } else if (appenderName.compare("stderr") == 0) {
                        appender = new log4cpp::FileAppender(categoryName, ::dup(fileno(stderr)));
                    }
#if LOG4CPP_HAVE_SYSLOG
                    else if (appenderName.compare("syslog") == 0) {
                        std::string syslogName;
                        int facility;
                        if (!(initFile >> syslogName)) {
                            throw ConfigureFailure("Missing syslogname for SysLogAppender for category: " +
                                                   categoryName);
                        }
                        if (!(initFile >> facility)) {
                            facility = LOG_USER;
                        } else {
                            // * 8
                            facility *= 8;
                        }
                        appender = new log4cpp::SyslogAppender(categoryName, syslogName, facility);
                    }
#endif
#if defined(WIN32)
                    else if (appenderName.compare("nteventlog") == 0) {
                        std::string source;
                        if (!(initFile >> source)) {
                            throw ConfigureFailure("Missing source for NTEventLogAppender for category: " +
                                                   categoryName);
                        }
                        appender = new log4cpp::NTEventLogAppender(categoryName, source);
                    }
#endif
#if !defined(LOG4CPP_DISABLE_REMOTE_SYSLOG)
                    else if (appenderName.compare("remotesyslog") == 0) {
                        std::string syslogName;
                        std::string relayer;
                        int facility;
                        int portNumber;
                        if (!(initFile >> syslogName)) {
                            throw ConfigureFailure("Missing syslogname for SysLogAppender for category: " +
                                                   categoryName);
                        }
                        if (!(initFile >> relayer)) {
                            throw ConfigureFailure("Missing syslog host for SysLogAppender for category: " +
                                                   categoryName);
                        }
                        if (!(initFile >> facility)) {
                            facility = LOG_USER;
                        }
                        if (!(initFile >> portNumber)) {
                            portNumber = 514;
                        }
                        appender =
                            new log4cpp::RemoteSyslogAppender(categoryName, syslogName, relayer, facility, portNumber);
                    }
#endif // LOG4CPP_DISABLE_REMOTE_SYSLOG
                    else {
                        throw ConfigureFailure("Invalid appender name (" + appenderName +
                                               ") in logging configuration file for category: " + categoryName);
                    }
                    if (layout.compare("basic") == 0)
                        appender->setLayout(new log4cpp::BasicLayout());
                    else if (layout.compare("simple") == 0)
                        appender->setLayout(new log4cpp::SimpleLayout());
                    else if (layout.compare("pattern") == 0) {
                        log4cpp::PatternLayout* layout = new log4cpp::PatternLayout();
                        initFile >> std::ws; // skip whitespace
                        char pattern[1000];
                        initFile.getline(pattern, 1000);
                        layout->setConversionPattern(std::string(pattern));
                        appender->setLayout(layout);
                    } else {
                        throw ConfigureFailure("Invalid layout (" + layout +
                                               ") in logging configuration file for category: " + categoryName);
                    }
                    category.addAppender(appender);
                }
            } else if (nextCommand.compare("priority") == 0) {
                std::string priority;
                if (!(initFile >> priority)) {
                    throw ConfigureFailure("Missing priority in logging configuration file for category: " +
                                           categoryName);
                }

                try {
                    category.setPriority(log4cpp::Priority::getPriorityValue(priority));
                } catch (const std::invalid_argument&) {
                    throw ConfigureFailure("Invalid priority (" + priority +
                                           ") in logging configuration file for category: " + categoryName);
                }
            } else if (nextCommand.compare("category") == 0) {
                /*
                  This command means we should "refer" to the category
                  (in order to have it created). We've already done this
                  in common setup code for all commands.
                */
            } else {
                throw ConfigureFailure("Invalid format in logging configuration file. Command: " + nextCommand);
            }
        }
    }
} // namespace log4cpp
