// Copyright 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <errno.h>      // for errno
#include <stdio.h>      // for size_t, EOF
#include <string.h>     // for strcmp
#include <iostream>     // for ostream, operator<<
#include <string_view>
#include <vector>       // for vector

#include "aemu/base/logging/LogSeverity.h"  // for LogSeverity, EMULATOR_...

namespace android {
namespace base {

// Returns the minimal log level.
::LogSeverity getMinLogLevel();
void setMinLogLevel(::LogSeverity level);

class LogFormatter;

void setLogFormatter(LogFormatter* fmt);

// Convert a log level name (e.g. 'INFO') into the equivalent
// ::android::base LOG_<name> constant.
#define LOG_SEVERITY_FROM(x) EMULATOR_LOG_##x

// Helper macro used to test if logging for a given log level is
// currently enabled. |severity| must be a log level without the LOG_
// prefix, as in:
//
//  if (LOG_IS_ON(INFO)) {
//      ... do additionnal logging
//  }
//
#define LOG_IS_ON(severity) \
    (LOG_SEVERITY_FROM(severity) >= ::android::base::getMinLogLevel())

// For performance reasons, it's important to avoid constructing a
// LogMessage instance every time a LOG() or CHECK() statement is
// encountered at runtime, i.e. these objects should only be constructed
// when absolutely necessary, which means:
//  - For LOG() statements, when the corresponding log level is enabled.
//  - For CHECK(), when the tested expression doesn't hold.
//
// At the same time, we really want to use expressions like:
//    LOG(severity) << some_stuff << some_more_stuff;
//
// This means LOG(severity) should expand to something that can take
// << operators on its right hand side. This is achieved with the
// ternary '? :', as implemented by this helper macro.
//
// Unfortunately, a simple thing like:
//
//   !(condition) ? (void)0 : (expr)
//
// will not work, because the compiler complains loudly with:
//
//   error: second operand to the conditional operator is of type 'void',
//   but the third operand is neither a throw-expression nor of type 'void'
#define LOG_LAZY_EVAL(condition, expr) \
    !(condition) ? (void)0 : ::android::base::LogStreamVoidify() & (expr)

// Send a message to the log if |severity| is higher or equal to the current
// logging severity level. This macro expands to an expression that acts as
// an input stream for strings, ints and floating point values, as well as
// LogString instances. Usage example:
//
//    LOG(INFO) << "Starting flux capacitor";
//    fluxCapacitor::start();
//    LOG(INFO) << "Flux capacitor started";
//
// Note that the macro implementation is optimized to avoid doing any work
// if the severity level is disabled.
//
// It's possible to do conditional logging with LOG_IF()
#define LOG(severity) \
    LOG_LAZY_EVAL(LOG_IS_ON(severity), LOG_MESSAGE_STREAM_COMPACT(severity))

// A variant of LOG() that only performs logging if a specific condition
// is encountered. Note that |condition| is only evaluated if |severity|
// is high enough. Usage example:
//
//    LOG(INFO) << "Starting fuel injector";
//    fuelInjector::start();
//    LOG(INFO) << "Fuel injection started";
//    LOG_IF(INFO, fuelInjector::hasOptimalLevel())
//            << "Fuel injection at optimal level";
//
#define LOG_IF(severity, condition)                   \
    LOG_LAZY_EVAL(LOG_IS_ON(severity) && (condition), \
                  LOG_MESSAGE_STREAM_COMPACT(severity))

// A variant of LOG() that avoids printing debug information such as file/line
// information, for user-visible output.
#define QLOG(severity) \
    LOG_LAZY_EVAL(LOG_IS_ON(severity), QLOG_MESSAGE_STREAM_COMPACT(severity))

// A variant of LOG_IF() that avoids printing debug information such as
// file/line information, for user-visible output.
#define QLOG_IF(severity, condition)                  \
    LOG_LAZY_EVAL(LOG_IS_ON(severity) && (condition), \
                  QLOG_MESSAGE_STREAM_COMPACT(severity))

// A variant of LOG() that integrates with the utils/debug.h verbose tags,
// enabling statements to only appear on the console if the "-debug-<tag>"
// command line parameter is provided.  Example:
//
//    VLOG(virtualscene) << "Starting scene.";
//
// Which would only be visible if -debug-virtualscene or -debug-all is passed
// as a command line parameter.
//
// When logging is enabled, VLOG statements are logged at the INFO severity.
#define VLOG(tag) \
    LOG_LAZY_EVAL(VERBOSE_CHECK(tag), LOG_MESSAGE_STREAM_COMPACT(INFO))

// A variant of LOG() that also appends the string message corresponding
// to the current value of 'errno' just before the macro is called. This
// also preserves the value of 'errno' so it can be tested after the
// macro call (i.e. any error during log output does not interfere).
#define PLOG(severity) \
    LOG_LAZY_EVAL(LOG_IS_ON(severity), PLOG_MESSAGE_STREAM_COMPACT(severity))

// A variant of LOG_IF() that also appends the string message corresponding
// to the current value of 'errno' just before the macro is called. This
// also preserves the value of 'errno' so it can be tested after the
// macro call (i.e. any error during log output does not interfere).
#define PLOG_IF(severity, condition)                  \
    LOG_LAZY_EVAL(LOG_IS_ON(severity) && (condition), \
                  PLOG_MESSAGE_STREAM_COMPACT(severity))

// Evaluate |condition|, and if it fails, log a fatal message.
// This is a better version of assert(), in the future, this will
// also break directly into the debugger for debug builds.
//
// Usage is similar to LOG(FATAL), e.g.:
//
//   CHECK(some_condition) << "Something really bad happened!";
//
#define CHECK(condition) \
    LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "

// A variant of CHECK() that also appends the errno message string at
// the end of the log message before exiting the process.
#define PCHECK(condition) \
    PLOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "

// Define ENABLE_DLOG to 1 here if DLOG() statements should be compiled
// as normal LOG() ones in the final binary. If 0, the statements will not
// be compiled.
#ifndef ENABLE_DLOG
#if defined(NDEBUG)
#define ENABLE_DLOG 0
#else
#define ENABLE_DLOG 1
#endif
#endif

// ENABLE_DCHECK controls how DCHECK() statements are compiled:
//    0 - DCHECK() are not compiled in the binary at all.
//    1 - DCHECK() are compiled, but are not performed at runtime, unless
//        the DCHECK level has been increased explicitely.
//    2 - DCHECK() are always compiled as CHECK() in the final binary.
#ifndef ENABLE_DCHECK
#if defined(NDEBUG)
#define ENABLE_DCHECK 1
#else
#define ENABLE_DCHECK 2
#endif
#endif

// DLOG_IS_ON(severity) is used to indicate whether DLOG() should print
// something for the current level.
#if ENABLE_DLOG
#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
#else
// NOTE: The compile-time constant ensures that the DLOG() statements are
//       not compiled in the final binary.
#define DLOG_IS_ON(severity) false
#endif

// DCHECK_IS_ON() is used to indicate whether DCHECK() should do anything.
#if ENABLE_DCHECK == 0
// NOTE: Compile-time constant ensures the DCHECK() statements are
// not compiled in the final binary.
#define DCHECK_IS_ON() false
#elif ENABLE_DCHECK == 1
#define DCHECK_IS_ON() ::android::base::dcheckIsEnabled()
#else
#define DCHECK_IS_ON() true
#endif

// A function that returns true iff DCHECK() should actually do any checking.
bool dcheckIsEnabled();

// Change the DCHECK() level to either false or true. Should only be called
// early, e.g. after parsing command-line arguments. Returns previous value.
bool setDcheckLevel(bool enabled);

// DLOG() is like LOG() for debug builds, and doesn't do anything for
// release one. This is useful to add log messages that you don't want
// to see in the final binaries, but are useful during testing.
#define DLOG(severity) DLOG_IF(severity, true)

// DLOG_IF() is like DLOG() for debug builds, and doesn't do anything for
// release one. See DLOG() comments.
#define DLOG_IF(severity, condition)                   \
    LOG_LAZY_EVAL(DLOG_IS_ON(severity) && (condition), \
                  LOG_MESSAGE_STREAM_COMPACT(severity))

// DCHECK(condition) is used to perform CHECK() in debug builds, or if
// the program called setDcheckLevel(true) previously. Note that it is
// also possible to completely remove them from the final binary by
// using the compiler flag -DENABLE_DCHECK=0
#define DCHECK(condition)                         \
    LOG_IF(FATAL, DCHECK_IS_ON() && !(condition)) \
            << "Check failed: " #condition ". "

// DPLOG() is like DLOG() that also appends the string message corresponding
// to the current value of 'errno' just before the macro is called. This
// also preserves the value of 'errno' so it can be tested after the
// macro call (i.e. any error during log output does not interfere).
#define DPLOG(severity) DPLOG_IF(severity, true)

// DPLOG_IF() tests whether |condition| is true before calling
// DPLOG(severity)
#define DPLOG_IF(severity, condition)                  \
    LOG_LAZY_EVAL(DLOG_IS_ON(severity) && (condition), \
                  PLOG_MESSAGE_STREAM_COMPACT(severity))

// Convenience class used hold a formatted string for logging reasons.
// Usage example:
//
//    LOG(INFO) << LogString("There are %d items in this set", count);
//
class LogString {
public:
    LogString(const char* fmt, ...);
    const char* string() const { return mString.data(); }

private:
    std::vector<char> mString;
};

// Helper structure used to group the parameters of a LOG() or CHECK()
// statement.
struct LogParams {
    LogParams() {}
    LogParams(const char* a_file,
              int a_lineno,
              LogSeverity a_severity,
              bool quiet = false)
        : file(a_file), lineno(a_lineno), severity(a_severity), quiet(quiet) {}

    friend bool operator==(const LogParams& s1, const LogParams& s2) {
        return s1.lineno == s2.lineno && s1.severity == s2.severity &&
               s1.quiet == s2.quiet &&
               ((s1.file == nullptr && s2.file == nullptr) ||  // both null..
                (s1.file != nullptr && s2.file != nullptr &&
                 (s1.file == s2.file ||
                  strcmp(s1.file, s2.file) == 0))  // or the same
               );
    }

    const char* file = nullptr;
    int lineno = -1;
    LogSeverity severity = LOG_SEVERITY_FROM(DEBUG);
    bool quiet = false;
};

class LogstreamBuf : public std::streambuf {
public:
    LogstreamBuf();

    size_t size();
    char* str();

protected:
    int overflow(int c) override;

private:
    std::vector<char> mLongString;
    char mStr[256];
};

// Helper class used to implement an input stream similar to std::istream
// where it's possible to inject strings, integers, floats and LogString
// instances with the << operator.
//
// This also takes a source file, line number and severity to avoid
// storing these in the stack of the functions were LOG() and CHECK()
// statements are called.
class LogStream {
public:
    LogStream(const char* file, int lineno, LogSeverity severity, bool quiet);
    ~LogStream() = default;

    template <typename T>
    std::ostream& operator<<(const T& t) {
        return mStream << t;
    }

    const char* str() { return mStreamBuf.str(); }

    const size_t size() { return mStreamBuf.size(); }
    const LogParams& params() const { return mParams; }

private:
    LogParams mParams;
    LogstreamBuf mStreamBuf;
    std::ostream mStream;
};

// Add your own types when needed:
std::ostream& operator<<(std::ostream& stream,
                         const android::base::LogString& str);
std::ostream& operator<<(std::ostream& stream, const std::string_view& str);

// Helper class used to avoid compiler errors, see LOG_LAZY_EVAL for
// more information.
class LogStreamVoidify {
public:
    LogStreamVoidify() {}
    // This has to be an operator with a precedence lower than << but
    // higher than ?:
    void operator&(std::ostream&) {}
};

// This represents an log message. At creation time, provide the name of
// the current file, the source line number and a severity.
// You can them stream stuff into it with <<. For example:
//
//   LogMessage(__FILE__, __LINE__, LOG_INFO) << "Hello World!\n";
//
// When destroyed, the message sends the final output to the appropriate
// log (e.g. stderr by default).
class LogMessage {
public:
    // To suppress printing file/line, set quiet = true.
    LogMessage(const char* file,
               int line,
               LogSeverity severity,
               bool quiet = false);
    ~LogMessage();

    LogStream& stream() const { return *mStream; }

protected:
    // Avoid that each LOG() statement
    LogStream* mStream;
};

// Helper macros to avoid too much typing. This creates a new LogMessage
// instance with the appropriate file source path, file source line and
// severity.
#define LOG_MESSAGE_COMPACT(severity) \
    ::android::base::LogMessage(__FILE__, __LINE__, LOG_SEVERITY_FROM(severity))

#define LOG_MESSAGE_STREAM_COMPACT(severity) \
    LOG_MESSAGE_COMPACT(severity).stream()

// A variant of LogMessage for outputting user-visible messages to the console,
// without debug information.
#define QLOG_MESSAGE_COMPACT(severity)              \
    ::android::base::LogMessage(__FILE__, __LINE__, \
                                LOG_SEVERITY_FROM(severity), true)

#define QLOG_MESSAGE_STREAM_COMPACT(severity) \
    QLOG_MESSAGE_COMPACT(severity).stream()

// A variant of LogMessage that saves the errno value on creation,
// then restores it on destruction, as well as append a strerror()
// error message to the log before sending it for output. Used by
// the PLOG() implementation(s).
//
// This cannot be a sub-class of LogMessage because the destructor needs
// to restore the saved errno message after sending the message to the
// LogOutput and deleting the stream.
class ErrnoLogMessage {
public:
    ErrnoLogMessage(const char* file,
                    int line,
                    LogSeverity severity,
                    int errnoCode);
    ~ErrnoLogMessage();

    LogStream& stream() const { return *mStream; }

private:
    LogStream* mStream;
    int mErrno;
};

// Helper macros to avoid too much typing.
#define PLOG_MESSAGE_COMPACT(severity)                   \
    ::android::base::ErrnoLogMessage(__FILE__, __LINE__, \
                                     LOG_SEVERITY_FROM(severity), errno)

#define PLOG_MESSAGE_STREAM_COMPACT(severity) \
    PLOG_MESSAGE_COMPACT(severity).stream()

namespace testing {

// Abstract interface to the output where the log messages are sent.
// IMPORTANT: Only use this for unit testing the log facility.
class LogOutput {
public:
    LogOutput() {}
    virtual ~LogOutput() {}

    // Send a full log message to the output. Not zero terminated, and
    // Does not have a trailing \n which can be added by the implementation
    // when writing the message to a file.
    // Note: if |severity| is LOG_FATAL, this should also terminate the
    // process.
    virtual void logMessage(const LogParams& params,
                            const char* message,
                            size_t message_len) = 0;

    // Set a new log output, and return pointer to the previous
    // implementation, which will be NULL for the default one.
    // |newOutput| is either NULL (which means the default), or a
    // custom instance of LogOutput.
    static LogOutput* setNewOutput(LogOutput* newOutput);
};

}  // namespace testing

}  // namespace base
}  // namespace android
