/*
 * Copyright (c) 2018-2020, NVIDIA CORPORATION.  All rights reserved.
 *
 * NVIDIA CORPORATION and its licensors retain all intellectual property
 * and proprietary rights in and to this software, related documentation
 * and any modifications thereto.  Any use, reproduction, disclosure or
 * distribution of this software and related documentation without an express
 * license agreement from NVIDIA CORPORATION is strictly prohibited.
 */

#include "nvmedia_image.h"
#include "nvmedia_core.h"
#include "nvscierror.h"

#include <iostream>
#include <memory>
#include <cstdarg>
#include <vector>

#include "NvSIPLCommon.hpp"

using namespace nvsipl;

#ifndef CUTILS_HPP
#define CUTILS_HPP


/** Helper MACROS */
#define CHK_PTR_AND_RETURN(ptr, api) \
    if (ptr == nullptr) { \
        LOG_ERR("%s failed\n", api); \
        return NVSIPL_STATUS_OUT_OF_MEMORY; \
    }

#define CHK_STATUS_AND_RETURN(status, api) \
    if (status != NVSIPL_STATUS_OK) { \
        LOG_ERR("%s failed. status: %u\n", api, status); \
        return status; \
    }

#define CHK_NVMSTATUS_AND_RETURN(nvmStatus, api) \
    if (nvmStatus != NVMEDIA_STATUS_OK) { \
        cout << api << " failed. " << nvmStatus << endl; \
        return NVSIPL_STATUS_ERROR; \
    }

#define CHK_NVSCISTATUS_AND_RETURN(nvSciStatus, api) \
    if (nvSciStatus != NvSciError_Success) { \
        cout << api << " failed. " << nvSciStatus << endl; \
        return NVSIPL_STATUS_ERROR; \
    }

#define LINE_INFO __FUNCTION__, __LINE__

//! Quick-log a message at debugging level
#define LOG_DBG(...) \
    CLogger::GetInstance().LogLevelMessage(LEVEL_DBG, LINE_INFO, __VA_ARGS__)

//! Quick-log a message at info level
#define LOG_INFO(...) \
    CLogger::GetInstance().LogLevelMessage(LEVEL_INFO, LINE_INFO, __VA_ARGS__)

//! Quick-log a message at warning level
#define LOG_WARN(...) \
    CLogger::GetInstance().LogLevelMessage(LEVEL_WARN, LINE_INFO, __VA_ARGS__)

//! Quick-log a message at error level
#define LOG_ERR(...) \
    CLogger::GetInstance().LogLevelMessage(LEVEL_ERR, LINE_INFO, __VA_ARGS__)

//! Quick-log a message at preset level
#define LOG_MSG(...) \
    CLogger::GetInstance().LogMessage(__VA_ARGS__)

#define LEVEL_NONE CLogger::LogLevel::LEVEL_NO_LOG

#define LEVEL_ERR CLogger::LogLevel::LEVEL_ERROR

#define LEVEL_WARN CLogger::LogLevel::LEVEL_WARNING

#define LEVEL_INFO CLogger::LogLevel::LEVEL_INFORMATION

#define LEVEL_DBG CLogger::LogLevel::LEVEL_DEBUG

//! \brief Logger utility class
//! This is a singleton class - at most one instance can exist at all times.
class CLogger
{
public:
    //! enum describing the different levels for logging
    enum LogLevel
    {
        /** no log */
        LEVEL_NO_LOG = 0,
        /** error level */
        LEVEL_ERROR,
        /** warning level */
        LEVEL_WARNING,
        /** info level */
        LEVEL_INFORMATION,
        /** debug level */
        LEVEL_DEBUG
    };

    //! enum describing the different styles for logging
    enum LogStyle
    {
        LOG_STYLE_NORMAL = 0,
        LOG_STYLE_FUNCTION_LINE = 1
    };

    //! Get the logging instance.
    //! \return Reference to the Logger object.
    static CLogger& GetInstance();

    //! Set the level for logging.
    //! \param[in] eLevel The logging level.
    void SetLogLevel(LogLevel eLevel);

    //! Get the level for logging.
    LogLevel GetLogLevel(void);

    //! Set the style for logging.
    //! \param[in] eStyle The logging style.
    void SetLogStyle(LogStyle eStyle);

    //! Log a message (cstring).
    //! \param[in] eLevel The logging level,
    //! \param[in] pszunctionName Name of the function as a cstring.
    //! \param[in] sLineNumber Line number,
    //! \param[in] pszFormat Format string as a cstring.
    void LogLevelMessage(LogLevel eLevel,
                         const char *pszFunctionName,
                         uint32_t sLineNumber,
                         const char *pszFormat,
                         ...);

    //! Log a message (C++ string).
    //! \param[in] eLevel The logging level,
    //! \param[in] sFunctionName Name of the function as a C++ string.
    //! \param[in] sLineNumber Line number,
    //! \param[in] sFormat Format string as a C++ string.
    void LogLevelMessage(LogLevel eLevel,
                         std::string sFunctionName,
                         uint32_t sLineNumber,
                         std::string sFormat,
                         ...);

    //! Log a message (cstring) at preset level.
    //! \param[in] pszFormat Format string as a cstring.
    void LogMessage(const char *pszFormat,
                    ...);

    //! Log a message (C++ string) at preset level.
    //! \param[in] sFormat Format string as a C++ string.
    void LogMessage(std::string sFormat,
                    ...);

private:
    //! Need private constructor because this is a singleton.
    CLogger() = default;
    LogLevel m_level = LEVEL_ERR;
    LogStyle m_style = LOG_STYLE_NORMAL;

    void LogLevelMessageVa(LogLevel eLevel,
                           const char *pszFunctionName,
                           uint32_t sLineNumber,
                           const char *pszFormat,
                           va_list ap);
    void LogMessageVa(const char *pszFormat,
                      va_list ap);
};
// CLogger class

class CUtils final
{
public:
    static NvMediaImage* CreateRgbaImage(NvMediaDevice *nvmDevice,
                                         NvMediaImage* imgRaw);
    static uint8_t* CreateRawBuffer(NvMediaImage *imgSrc);
    static uint8_t* CreateRgbaBuffer(NvMediaImage *imgDst);
    static SIPLStatus ConvertRawToRgba(NvMediaImage *imgSrc,
                                       uint8_t *pSrcBuf,
                                       NvMediaImage *imgDst,
                                       uint8_t *pDstBuf);
    static SIPLStatus IsRawImage(NvMediaImage *nvmImg,
                                 bool &bIsRaw);
private:
    static SIPLStatus ValidateImage(NvMediaImage *img,
                                    uint32_t expectedSurfFormat,
                                    uint32_t &pixelOrder,
                                    uint32_t &bytesPerPixel);
    static uint8_t* CreateImageBuffer(NvMediaImage *img,
                                      uint32_t surfFormat);
};

struct CloseNvMediaDevice {
    void operator ()(NvMediaDevice *device) const {
        NvMediaDeviceDestroy(device);
    }
};

SIPLStatus LoadNITOFile(std::string folderPath,
                        std::string moduleName,
                        std::vector<std::uint8_t>& nito);

#endif
