/*
 * Copyright (c) 2016-2019 Arm Limited.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#ifndef ARM_COMPUTE_LOGGING_LOG_MSG_DECORATORS_H
#define ARM_COMPUTE_LOGGING_LOG_MSG_DECORATORS_H

#include "arm_compute/core/Error.h"
#include "arm_compute/core/utils/logging/Helpers.h"
#include "arm_compute/core/utils/logging/Types.h"

#include <chrono>
#include <ctime>
#include <string>
#ifndef NO_MULTI_THREADING
#include <thread>
#endif /* NO_MULTI_THREADING */

namespace arm_compute
{
namespace logging
{
/** Log message decorator interface */
class IDecorator
{
public:
    /** Default Destructor */
    virtual ~IDecorator() = default;
    /** Decorates log message
     *
     * @param[in] log_msg Log message to decorate
     */
    virtual void decorate(LogMsg &log_msg) = 0;
};

/** String Decorator
 *
 * Appends a user defined string in the log message
 */
class StringDecorator : public IDecorator
{
public:
    /** Defaults constructor
     *
     * @param str Sting to append
     */
    StringDecorator(const std::string &str) : _str(str)
    {
        _str = angle_wrap_value(str);
    }

    // Inherited methods overridden:
    void decorate(LogMsg &log_msg) override
    {
        log_msg.raw_ += _str;
    }

private:
    std::string _str;
};

/** Date Decorator
 *
 * Appends the date and time in the log message
 */
class DateDecorator : public IDecorator
{
public:
    // Inherited methods overridden:
    void decorate(LogMsg &log_msg) override
    {
        log_msg.raw_ += angle_wrap_value(get_time());
    }

private:
    /** Gets current system local time
     *
     * @return Local time
     */
    std::string get_time()
    {
        auto now  = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);

        // TODO: use put_time for gcc > 4.9
        char buf[100] = {0};
        std::strftime(buf, sizeof(buf), "%d-%m-%Y %I:%M:%S", std::localtime(&time));
        return buf;
    }
};

/** Thread ID Decorator
 *
 * Appends the thread ID in the log message
 */
class ThreadIdDecorator : public IDecorator
{
public:
    // Inherited methods overridden:
    void decorate(LogMsg &log_msg) override
    {
#ifndef NO_MULTI_THREADING
        log_msg.raw_ += angle_wrap_value(std::this_thread::get_id());
#else  /* NO_MULTI_THREADING */
        ARM_COMPUTE_UNUSED(log_msg);
#endif /* NO_MULTI_THREADING */
    }
};

/** Log Level Decorator
 *
 * Appends the logging level in the log message
 */
class LogLevelDecorator : public IDecorator
{
public:
    // Inherited methods overridden:
    void decorate(LogMsg &log_msg) override
    {
        log_msg.raw_ += angle_wrap_value(string_from_log_level(log_msg.log_level_));
    }
};
} // namespace logging
} // namespace arm_compute
#endif /* ARM_COMPUTE_LOGGING_LOG_MSG_DECORATORS_H */
