/**
 * -------------------------------------------------------------------------
 *  Part of the CodeChecker project, under the Apache License v2.0 with
 *  LLVM Exceptions. See LICENSE for license information.
 *  SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 * -------------------------------------------------------------------------
 */

#ifndef __LOGGER_UTIL_H__
#define __LOGGER_UTIL_H__

#include <limits.h>
#include <linux/limits.h>
#include <string.h>

#define LOG_INFO(...) logPrint("INFO", __FILE__, __LINE__, __VA_ARGS__ );
#define LOG_WARN(...) logPrint("WARNING", __FILE__, __LINE__, __VA_ARGS__ );
#define LOG_ERROR(...) logPrint("ERROR", __FILE__, __LINE__, __VA_ARGS__ );

/**
 * Predicts the size of the string after escaping including the closing \0
 * character. For example:
 *
 * hello -> hello (length = 6)
 * hello world -> hello\\ world (length = 14)
 * "hello" -> \\\"hello\\\" (length = 14)
 * "hello world" -> \\\"hello\\ world\\\" (length = 22)
 *
 * BUGS:
 * The '\\', '\r' and '\v' characters are not escaped correctly.
 *
 * When we generate the json, we will automatically select this or the fixed
 * version whether the CC_LOGGER_NEW_ESCAPING environment variable is defined
 * or not. By default the buggy version will be selected.
 */
int predictEscapedSize(const char* str_);

/**
 * Does the same as predictEscapedSize, but fixes the escape bug.
 * The '\\', '\r' and '\v' characters are escaped correctly.
 */
int predictEscapedSizeFixed(const char* str_);

/**
 * Generates a shell-escaped version of the given string. The output buffer
 * must be large enough (recommended size is provided by predictEscapedSize()).
 * This escaped version is safe to place in a JSON string. After reading this
 * serialized form from the JSON, the original string will be returned. See
 * the documentation of predictEscapedSize() for some examples.
 *
 * BUGS:
 * The '\\', '\r' and '\v' characters are not escaped correctly.
 *
 * When we generate the json, we will automatically select this or the fixed
 * version whether the CC_LOGGER_NEW_ESCAPING environment variable is defined
 * or not. By default the buggy version will be selected.
 * 
 * @param str_ a string to escape (non null).
 * @param buff_ an output buffer (non null).
 * @return anways returns buff_.
 */
char* shellEscapeStr(const char* str_, char* buff_);

/**
 * Does the same as shellEscapeStr, but fixes the escape bug.
 * The '\\', '\r' and '\v' characters are escaped correctly.
 */
char* shellEscapeStrFixed(const char* str_, char* buff_);

/**
 * Resolves a given path to an absolute path. The given buffer (resolved) may
 * be modified on error.
 *
 * @param path_ a relative or absolute path.
 * @param resolved_ a buffer for resolved path (must be at least PATH_MAX).
 * @param mustExist_ if it true than makePathAbsolute chech if the file exists.
 * @return resolved_ or NULL on error.
 */
char* loggerMakePathAbs(const char* path_, char* resolved_, int mustExist_);

/**
 * The function returns true if str_ starts with prefix_.
 */
int startsWith(const char* str_, const char* prefix_);

/**
 * Typedef for free function for a vector.
 */
typedef void (*LoggerFreeFuc)(void*);

/**
 * Typedef for dup function for a vector.
 */
typedef void* (*LoggerDupFuc)(const void*);

/**
 * Typedef for cmp function for a vector.
 */
typedef int (*LoggerCmpFuc)(const void*, const void*);

/**
 * Typedef for predicate function for a vector. This should return 0 if the
 * condition is not true for the element.
 */
typedef int (*LoggerPredFuc)(const void*);

/**
 * A very simple vector.
 */
typedef struct _LoggerVector 
{
  /**
   * The actual size of the vector.
   */
  size_t size;
  /**
   * The capacity of the vector.
   */
  size_t capacity;
  /**
   * Free function for data elements.
   */
  LoggerFreeFuc dataFree;
  /**
   * Data array.
   */
  void** data;
} LoggerVector;

/**
 * Inits an empty vector.
 *
 * @param vec_ a vector struct (must be not NULL).
 * @return the vec_ param or NULL on error.
 */
LoggerVector* loggerVectorInit(LoggerVector* vec_);

/**
 * An advanced version of loggerVectorInit.
 *
 * @param vec_ a vector struct (must be not NULL).
 * @param cap_ inital capacity.
 * @param freeFunc_ free function for data (it could be NULL).
 * @return the vec_ param or NULL on error.
 */
LoggerVector* loggerVectorInitAdv(
  LoggerVector* vec_,
  size_t cap_,
  LoggerFreeFuc freeFunc_);

/**
 * Clears the vector (after it you can free the vector without memory leaks or
 * you can call a loggerVectorInit* on it).
 *
 * @param vec_ a vector struct (must be not NULL).
 */
void loggerVectorClear(LoggerVector* vec_);

/**
 * Adds all element from the source vector to the vec_ param using the dup_
 * function for duplicating the elements.
 *
 * @param vec_ a vector.
 * @param source_ source vector.
 * @param position_ position where the new elements are inserted (optional)
 * @param dup_ a function to duplicate items.
 * @return zero on error, non-zero on success.
 */
int loggerVectorAddFrom(
  LoggerVector* vec_,
  const LoggerVector* source_,
  const size_t* position_,
  LoggerDupFuc dup_);

/**
 * Adds an item to a vector. The element will be owned by the vector and
 * freed by dataFree function (if it not null).
 *
 * @param vec_ a vector struct (must be not NULL).
 * @param data_ an item.
 * @return zero on error, non-zero on success.
 */
int loggerVectorAdd(LoggerVector* vec_, void* data_);

/**
 * Like loggerVectorAdd() but first it tries to find the element in the vector
 * using the given comparation fuction and if there is a match, the functions
 * simply returns with success (after it freed the data_).
 *
 * @param vec_ a vector struct (must be not NULL).
 * @param data_ an item.
 * @param cmp_ a comparation function.
 * @return zero on error, non-zero on success.
 */
int loggerVectorAddUnique(LoggerVector* vec_, void* data_, LoggerCmpFuc cmp_);

/**
 * Removes (and frees) an item from the vector.
 *
 * @param vec_ a vector struct (must not be NULL).
 * @param index_ an item index.
 */
void loggerVectorErase(LoggerVector* vec_, size_t index_);

/**
 * Replaces the item in the vector at the given position.
 *
 * @param vec_ a vector struct (must not be NULL).
 * @param index_ an item index.
 * @param data_ the new data to insert.
 */
void loggerVectorReplace(LoggerVector* vec_, size_t index_, void* data_);

/**
 * Finds an element using the given comparation fuction.
 *
 * @param vec_ a vector struct (must not be NULL).
 * @param data_ an item.
 * @param cmp_ a comparation function.
 * @return SIZE_MAX if the element not found otherwise the item`s index.
 */
size_t loggerVectorFind(
  LoggerVector* vec_,
  const void* data_,
  LoggerCmpFuc cmp_);

/**
 * Finds an element using the given predicate fuction.
 *
 * @param vec_ a vector struct (must be not NULL).
 * @param pred_ a predicate function.
 * @return SIZE_MAX if the element not found otherwise the item`s index.
 */
size_t loggerVectorFindIf(
  LoggerVector* vec_,
  LoggerPredFuc pred_);

/**
 * An strdup implementation (its not in ANSI C).
 *
 * @param str_ string to duplicate.
 * @return a newly allocated duplicate of str_.
 */
char* loggerStrDup(const char* str_);

/**
 * An strndup implementation (its not in ANSI C).
 *
 * @param str_ string to duplicate.
 * @param n_ max number of chars to duplicate.
 * @return a newly allocated duplicate of str_.
 */
char* loggerStrNDup(const char* str_, size_t n_);

/**
 * Return the file`s extension.
 *
 * @param path_ the path of the file.
 * @param tolower_ if it true, the extension will be converted to lower-cased.
 * @return the file extension or NULL if no extension.
 */
char* loggerGetFileExt(const char* path_, int tolower_);

/**
 * Returns the file`s directory.
 *
 * @param absPath_ the absolute file path.
 * @return parent directory or NULL on error.
 */
char* loggerGetFileDir(const char* absPath_);

/**
 * Returns the absolute file name without extension.
 *
 * @param absPath_ the absolute file path.
 * @return the absolute file name without extension or NULL on error.
 */
char* loggerGetFilePathWithoutExt(const char* absPath_);

/**
 * Return the file`s name with or without it`s extension.
 * 
 * @param absPath_ the absolute file path.
 * @param withoutExt_ with or without extension.
 * @return file name or NULL on error..
 */
char* loggerGetFileName(const char* absPath_, int withoutExt_);

/**
 * Aquire lock for the given log file.
 * @param logFile_ log file path.
 * @return -1 on error, lock file descriptor on success.
 */
int aquireLock(char const* logFile_);

/**
 * Remove the given lock.
 * @param lockFile_ lock file descriptor.
 */
void freeLock(int lockFile_);

/**
 * Print log messages to a file specified by the 'CC_LOGGER_DEBUG_FILE'
 * environment variable. If this environment variable is not set it will do
 * nothing.
 *
 * @param logLevel_ log level name.
 * @param fileName_ file name from which the log is coming from.
 * @param line_ line number at which the log function is called from.
 * @param fmt_ Message that contains the text to be written to the stream. It
 *  can optionally contain the follwing format tags:
 *  - %s: string of characters.
 *  - %d: signed decimal integer.
 *  - %a': Array of strings. Two aguments must be passed when using this. The
 *         first argument is the size of the array and the second argument
 *         is the array itself.
 *  These formatted tags will be replaced by the values specified in
 *  subsequent additional arguments and formatted as requested.
 */
void logPrint(char* logLevel_, char* fileName_, int line_, char* fmt_, ...);

#endif /* __LOGGER_UTIL_H__ */
