//
//  Logger.hpp
//  SwitchboardSDK
//
//  Created by Balázs Kiss on 2017. 10. 05..
//  Copyright © 2017. Synervoz Inc. All rights reserved.
//

#pragma once

#include <switchboard/export.h>
#include <switchboard_core/LogDestination.hpp>
#include <switchboard_core/LogLevel.hpp>

#include <string>
#include <sstream>

namespace switchboard {

/**
 * Logger class.
 * @brief Provides logging functionality.
 */
class SWITCHBOARDSDK_EXPORT Logger {
public:
    /**
     * @brief Sets the log destination of the logger.
     */
    static void setLogDestination(LogDestination* destination);

    /**
     * @brief Gets the log destination of the logger.
     *
     * @returns The current log destination.
     */
    static LogDestination* getLogDestination();

    /**
     * @brief Sets the log level of the logger.
     *
     * @param level The log level to be set.
     */
    static void setLogLevel(LogLevel level);

    /**
     * @brief Gets the log level of the logger.
     *
     * @returns The current log level.
     */
    static LogLevel getLogLevel();

    /**
     * @brief Logs a trace level message.
     *
     * @param logMessage The log message.
     */
    static void trace(const std::string& logMessage);

    /**
     * @brief Logs a debug level message.
     *
     * @param logMessage The log message.
     */
    static void debug(const std::string& logMessage);

    /**
     * @brief Logs an info level message.
     *
     * @param logMessage The log message.
     */
    static void info(const std::string& logMessage);

    /**
     * @brief Logs a warning level message.
     *
     * @param logMessage The log message.
     */
    static void warning(const std::string& logMessage);

    /**
     * @brief Logs an error level message.
     *
     * @param logMessage The log message.
     */
    static void error(const std::string& logMessage);

    /**
     * @brief Stream-like interface for logging at different levels.
     */
    class LogStream {
    public:
        explicit LogStream(LogLevel level);
        LogStream(const LogStream&) = delete;
        LogStream& operator=(const LogStream&) = delete;
        LogStream(LogStream&& other) noexcept;

        LogStream& operator=(LogStream&& other) noexcept;

        ~LogStream();

        template<typename T>
        LogStream& operator<<(const T& value)  {
            stream << value;
            return *this;
        }

        // Handle std::endl and other manipulators
        LogStream& operator<<(std::ostream& (*manip)(std::ostream&)) {
            stream << manip;
            return *this;
        }

    private:
        LogLevel level;
        std::ostringstream stream;
    };

    /**
     * @brief Helper factory for creating LogStream instances at different log levels.
     */
    struct LogStreamFactory {
        LogLevel level;
        explicit LogStreamFactory(const LogLevel level) : level(level) {}

        template<typename T>
        LogStream operator<<(const T& value) {
            LogStream stream(level);
            stream << value;
            return stream;
        }
    };

    static LogStreamFactory traceStream;
    static LogStreamFactory debugStream;
    static LogStreamFactory infoStream;
    static LogStreamFactory warningStream;
    static LogStreamFactory errorStream;

private:
    static LogLevel logLevel;
    static LogDestination* destination;
};

}
