/*
 * Copyright (C) 2015 by Multi-Tech Systems
 *
 * This file is part of libmts.
 *
 * libmts is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * libmts is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with libmts.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*! \file   MTS_Logger.h
 \brief  A logging utility
 \date   15JUN11
 \author Sean Godinez

 A logging utility capable of writing to stdout, syslog, or file
 */
#ifndef MTS_LOGGER_H_
#define MTS_LOGGER_H_

#include <mts/MTS_NonCopyable.h>
#include <mts/MTS_NonConstructable.h>
#include <mts/MTS_Lock.h>
#include <mts/MTS_Stdint.h>
#include <string>

#ifdef DEBUG
#define printFatal(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::FATAL_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::FATAL_LEVEL, MTS::Logger::PrintLevel::FATAL_LABEL, "%s:%s:%d| " format, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define printError(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::ERROR_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::ERROR_LEVEL, MTS::Logger::PrintLevel::ERROR_LABEL, "%s:%s:%d| " format, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define printWarning(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::WARNING_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::WARNING_LEVEL, MTS::Logger::PrintLevel::WARNING_LABEL, "%s:%s:%d| " format, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define printInfo(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::INFO_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::INFO_LEVEL, MTS::Logger::PrintLevel::INFO_LABEL, "%s:%s:%d| " format, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define printConfig(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::CONFIG_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::CONFIG_LEVEL, MTS::Logger::PrintLevel::CONFIG_LABEL, "%s:%s:%d| " format, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define printDebug(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::DEBUG_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::DEBUG_LEVEL, MTS::Logger::PrintLevel::DEBUG_LABEL, "%s:%s:%d| " format, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define printTrace(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::TRACE_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::TRACE_LEVEL, MTS::Logger::PrintLevel::TRACE_LABEL, "%s:%s:%d| " format, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#else
#define printFatal(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::FATAL_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::FATAL_LEVEL, MTS::Logger::PrintLevel::FATAL_LABEL, format, ##__VA_ARGS__)
#define printError(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::ERROR_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::ERROR_LEVEL, MTS::Logger::PrintLevel::ERROR_LABEL, format, ##__VA_ARGS__)
#define printWarning(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::WARNING_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::WARNING_LEVEL, MTS::Logger::PrintLevel::WARNING_LABEL, format, ##__VA_ARGS__)
#define printInfo(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::INFO_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::INFO_LEVEL, MTS::Logger::PrintLevel::INFO_LABEL, format, ##__VA_ARGS__)
#define printConfig(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::CONFIG_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::CONFIG_LEVEL, MTS::Logger::PrintLevel::CONFIG_LABEL, format, ##__VA_ARGS__)
#define printDebug(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::DEBUG_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::DEBUG_LEVEL, MTS::Logger::PrintLevel::DEBUG_LABEL, format, ##__VA_ARGS__)
#define printTrace(format, ...) \
    if (MTS::Logger::isPrintable(MTS::Logger::PrintLevel::TRACE_LEVEL)) \
	MTS::Logger::printfGeneric(MTS::Logger::PrintLevel::TRACE_LEVEL, MTS::Logger::PrintLevel::TRACE_LABEL, format, ##__VA_ARGS__)
#endif

namespace MTS {

    class Logger: private NonCopyable {

        public:

            class PrintLevel: private NonConstructable {
                public:
                    static const int32_t OFF_LEVEL;
                    static const int32_t MINIMUM_LEVEL;
                    static const int32_t FATAL_LEVEL;
                    static const int32_t ERROR_LEVEL;
                    static const int32_t WARNING_LEVEL;
                    static const int32_t INFO_LEVEL;
                    static const int32_t CONFIG_LEVEL;
                    static const int32_t DEBUG_LEVEL;
                    static const int32_t TRACE_LEVEL;
                    static const int32_t MAXIMUM_LEVEL;

                    static const char* OFF_LABEL;
                    static const char* FATAL_LABEL;
                    static const char* ERROR_LABEL;
                    static const char* WARNING_LABEL;
                    static const char* INFO_LABEL;
                    static const char* CONFIG_LABEL;
                    static const char* DEBUG_LABEL;
                    static const char* TRACE_LABEL;
                    static const char* MAXIMUM_LABEL;
            };

            enum PrintMode {
                NO_PRINTING,
                STDOUT_ONLY,
                FILE_ONLY,
                SYSLOG_ONLY,
                STDOUT_AND_FILE,
                STDOUT_AND_SYSLOG,
                STDERR_ONLY,
                STDERR_AND_SYSLOG
            };

            static int getPrintLevel();
            static const std::string& getPrintLevelString();
            static void setPrintLevel(int32_t level, bool silent = false);
            static bool isPrintable(int32_t level);

            static void printfFatal(const char* format, ...);
            static void printfError(const char* format, ...);
            static void printfWarning(const char* format, ...);
            static void printfInfo(const char* format, ...);
            static void printfConfig(const char* format, ...);
            static void printfDebug(const char* format, ...);
            static void printfTrace(const char* format, ...);
            static void printfGeneric(int level, const char* label, const char* format, ...);

            static void printf(int level, const char* format, ...);
            static void printf(const char* format, ...);

            static bool setup(const PrintMode& mode);
            static bool setup(const PrintMode& mode, const std::string& ident, const int& option, const int& facility);
            static bool setup(const PrintMode& mode, const std::string& filename);

        private:

            static volatile int m_iPrintLevel;
            static std::string m_sPrintLevel;
            static Logger::PrintMode m_eMode;
            static Lock m_oPrintLock;
            static FILE* m_pFile;
            static int m_iLogFacility;
            static std::string m_sIdent;
            static std::string m_sFileName;

            static int32_t syslogPrintLevelConversion(const int32_t& level);
            static void printMessage(const int32_t& level, const char* label, const char* format, va_list argptr);

            Logger();
    };
}

#endif /* MTS_LOG_H_ */