/*******************************************************************************
* Copyright 2003-2020 Intel Corporation.
*
* This software and the related documents are Intel copyrighted  materials,  and
* your use of  them is  governed by the  express license  under which  they were
* provided to you (License).  Unless the License provides otherwise, you may not
* use, modify, copy, publish, distribute,  disclose or transmit this software or
* the related documents without Intel's prior written permission.
*
* This software and the related documents  are provided as  is,  with no express
* or implied  warranties,  other  than those  that are  expressly stated  in the
* License.
*******************************************************************************/

#ifndef __BASE_H__
#define __BASE_H__

#include "vm_base.h"
#include <vector>

#ifdef _MSC_VER
#pragma warning (disable:4505) // unreferenced local function has been removed
#endif

#ifdef __INTEL_COMPILER
#pragma warning (disable: 2415) // static storage duration was declared but never referenced
#endif

#if defined(__INTEL_COMPILER) || (_MSC_VER >= 1300)
    #define IPP_NOINLINE  __declspec(noinline)
#elif defined(__GNUC__)
    #define IPP_NOINLINE  __attribute__((noinline))
#else
    #define IPP_NOINLINE  
#endif

#define PI  3.14159265358979323846

#define MIN(x, y) (((x) < (y))?(x):(y))
#define MAX(x, y) (((x) > (y))?(x):(y))
#define ABS(x) (((x) < 0)?(-(x)):(x))
#define ROUND_NEAR(DIGIT) (((DIGIT) < 0)?ceil((DIGIT) - 0.5):floor((DIGIT) + 0.5))

#define MIN_UCHAR       (0)
#define MAX_UCHAR       (0xFF)
#define MIN_USHORT      (0)
#define MAX_USHORT      (0xFFFF)
#define MIN_UINT        (0)
#define MAX_UINT        (0xFFFFFFFF)
#define MIN_CHAR        (-128)
#define MAX_CHAR        (127)
#define MIN_SHORT       (-32768)
#define MAX_SHORT       (32767)
#define MIN_INT         (-2147483647 - 1)
#define MAX_INT         (2147483647)
#define MIN_DOUBLE_ABS  (2.2250738585072014e-308)
#define MAX_DOUBLE_ABS  (1.7976931348623158e+308)

#if !defined(USE_MIC)
    #define PRINT_FUNC_MESSAGE(STATUS, NAME, MESSAGE) printf("\nError %d in %s: %s\n", (int)STATUS, NAME, MESSAGE);
    #define PRINT_MESSAGE(MESSAGE) printf("\nError: %s\n", MESSAGE);
#else
    #define PRINT_FUNC_MESSAGE(STATUS, NAME, MESSAGE) printf("\nError %d in %s: %s\n", (int)STATUS, NAME, MESSAGE); fflush(0);
    #define PRINT_MESSAGE(MESSAGE) printf("\nError: %s\n", MESSAGE); fflush(0);
#endif

#define CHECK_STATUS_PRINT_AC(STATUS, NAME, MESSAGE, ACTION) \
    if(STATUS != 0) { PRINT_FUNC_MESSAGE(STATUS, NAME, MESSAGE); if(STATUS < 0) {ACTION;} }

#define CHECK_STATUS_PRINT(STATUS, NAME, MESSAGE) \
    CHECK_STATUS_PRINT_AC(STATUS, NAME, MESSAGE, ;)

#define CHECK_STATUS_PRINT_BR(STATUS, NAME, MESSAGE) \
    CHECK_STATUS_PRINT_AC(STATUS, NAME, MESSAGE, break)

#define CHECK_STATUS_PRINT_RS(STATUS, NAME, MESSAGE) \
    CHECK_STATUS_PRINT_AC(STATUS, NAME, MESSAGE, return STATUS)

enum ReturnStatus
{
    STS_ERR_FAILED                = -999,
    STS_ERR_NOT_INITIALIZED       = -998,
    STS_ERR_NOT_ENOUGH_DATA       = -996,
    STS_ERR_NULL_PTR              = -995,
    STS_ERR_INIT                  = -899,
    STS_ERR_END_OF_STREAM         = -895,
    STS_ERR_ALLOC                 = -883,
    STS_ERR_UNSUPPORTED           = -879,
    STS_ERR_INVALID_PARAMS        = -876,
    STS_ERR_FILE_OPEN             = -875,
    STS_ERR_FORMAT                = -874,
    STS_OK                        =  0
};

typedef int Status;

struct CodeStringTable
{
    int         iCode;
    const char *pString;
};

static const CodeStringTable StringOfBaseStatus[] = {
    { STS_OK,                   "Success" },
    { STS_ERR_FAILED,           "General failure" },
    { STS_ERR_NOT_INITIALIZED,  "Object is not initialized" },
    { STS_ERR_NOT_ENOUGH_DATA,  "Not enough input data" },
    { STS_ERR_NULL_PTR,         "Unexpected NULL pointer" },
    { STS_ERR_INIT,             "Failed to initialize object" },
    { STS_ERR_END_OF_STREAM,    "End of stream" },
    { STS_ERR_ALLOC,            "Failed to allocate memory" },
    { STS_ERR_UNSUPPORTED,      "Unsupported parameters/mode" },
    { STS_ERR_INVALID_PARAMS,   "Invalid parameters" },
    { STS_ERR_FILE_OPEN,        "Failed to open file" },
    { STS_ERR_FORMAT,           "Invalid format" },
};
static const char *sStatusEmpty = "<no status string>";

struct Size
{
    Size()
    {
        width  = 0;
        height = 0;
    }
    Size(long long _width, long long _height)
    {
        width  = _width;
        height = _height;
    }

    long long width;
    long long height;
};

struct Point
{
    Point()
    {
        x = y = 0;
    }
    Point(long long _x, long long _y)
    {
        x = _x;
        y = _y;
    }

    long long x;
    long long y;
};

struct Rect
{
    Rect()
    {
        x = y = 0;
        width = height = 0;
    }
    Rect(long long _width, long long _height)
    {
        x = 0;
        y = 0;
        width = _width;
        height = _height;
    }
    Rect(long long _x, long long _y, long long _width, long long _height)
    {
        x = _x;
        y = _y;
        width = _width;
        height = _height;
    }

    long long x;
    long long y;
    long long width;
    long long height;
};

struct BorderSize
{
    BorderSize()
    {
        left = top = right = bottom = 0;
    }
    BorderSize(long long _size)
    {
        left = top = right = bottom = _size;
    }
    BorderSize(long long _left, long long _top, long long _right, long long _bottom)
    {
        left   = _left;
        top    = _top;
        right  = _right;
        bottom = _bottom;
    }

    long long left;
    long long top;
    long long right;
    long long bottom;
};

struct Vect
{
    int x1;
    int y1;
    int x2;
    int y2;
};

/*
// Dynamic strings
*/
class DString
{
public:
    DString();
    DString(const vm_char *pSrc, size_t len = 0);
    DString(const DString &src);

    ~DString();

    void Clear(); // reset string buffer

    // replace current string with a new string
    size_t Replace(const vm_char *str, size_t len = 0);

    // add new string to the end of the current string
    size_t Append(const vm_char *str, size_t len = 0);

    int Compare(const vm_char *str, bool bCaseSensitive = true);

    // find substring inside current string and return its position. -1 if nothing was found
    long long Find(const vm_char *str, bool forward = true, bool bCaseSensitive = true);

    // split string using code and write substrings to dst array
    static void Split(DString source, vm_char code, std::vector<DString> &substr, bool forward = true, size_t max = 0);

    // count the number of specified symbols
    size_t Count(vm_char code);

    size_t Trim();
    size_t TrimLeft();
    size_t TrimRight();

    // removes successive repeating symbol codes
    size_t RemoveDuplicates(int code = 0);

    // set actual size of string buffer to iSize+1 if it is smaller
    size_t Resize(size_t size);

    // adjust value of m_iLen if buffer was written indirectly
    size_t AdjustLength();

    // format input into DString, based on sprintf
    static DString Format(const vm_char *format, ...);

    inline const vm_char*         c_str() const   { return m_pData; }
    inline size_t                  Size() const   { return m_len;  }
    inline operator       vm_char*     ()         { return m_pData; }
    inline operator const vm_char*     () const   { return m_pData; }

    inline vm_char &operator[]         (size_t i) { return (i < m_size)?m_pData[i]:m_pData[m_size]; }

    DString& operator=(const vm_char* str)
    {
        Replace(str, vm_string_strlen(str, 0xFFFF));
        return *this;
    }

    DString& operator=(const DString &str)
    {
        Replace(str.m_pData, str.m_len);
        return *this;
    }

    DString operator+ (const DString &right)
    {
        DString temp;
        temp.Resize(this->Size() + right.Size());
        temp = *this;
        temp += right;
        return temp;
    }
    DString operator+ (const vm_char *right)
    {
        DString temp;
        temp.Resize(this->Size() + vm_string_strlen(right, 0xFFFF));
        temp = *this;
        temp += right;
        return temp;
    }
    DString operator+ (const vm_char right)
    {
        DString temp;
        temp.Resize(this->Size() + 1);
        temp = *this;
        temp += right;
        return temp;
    }

    void operator+= (const DString &right)
    {
        Append(right.m_pData, right.m_len);
    }
    void operator+= (const vm_char *right)
    {
        Append(right, (unsigned int)vm_string_strlen(right, 0xFFFF));
    }
    void operator+= (const vm_char right)
    {
        Append(&right, 1);
    }

    bool operator== (const vm_char *right)
    {
        if(!Compare(right))
            return true;
        return false;
    }

    bool operator== (const DString &right)
    {
        if(!Compare(right.m_pData))
            return true;
        return false;
    }

    bool operator!= (const vm_char *right)
    {
        if(Compare(right))
            return true;
        return false;
    }

    bool operator!= (const DString &right)
    {
        if(Compare(right.m_pData))
            return true;
        return false;
    }

protected:
    static vm_char *allocate(size_t size)
    {
        vm_char *data = new vm_char[size];
        data[size-1] = VM_STRING('\0'); // always set string terminator at the and of buffer
        return data;
    }

    vm_char *m_pData;      // string buffer
    size_t   m_size;       // buffer size in symbols
    size_t   m_len;        // string length
};

/*
// Autobuffer
*/
template<typename T>
class AutoBuffer
{
public:
    AutoBuffer()                                        { m_pBuffer = NULL; m_size = 0; }
    AutoBuffer(size_t size, size_t elSize = sizeof(T))  { m_pBuffer = NULL; Alloc(size, elSize); }
    ~AutoBuffer()                                       { Release(); }
    inline T* Alloc(size_t size, size_t elSize = sizeof(T))
    {
        Release();
        m_size = size;
        m_pBuffer = (T*)malloc((int)(size*elSize));
        return m_pBuffer;
    }
    inline void Release()             { if(m_pBuffer) free(m_pBuffer); m_pBuffer = NULL; m_size = 0; }
    inline size_t GetSize()           { return m_size; }
    inline operator T* ()             { return (T*)m_pBuffer;}
    inline operator const T* () const { return (const T*)m_pBuffer;}
private:
    // Disable copy operations
    AutoBuffer(AutoBuffer &) {};
    AutoBuffer& operator =(const AutoBuffer &) {return *this;};

    size_t m_size;
    T*     m_pBuffer;
};

/*
// IO classes
*/
class File
{
public:
    enum Origin
    {
        ORIGIN_BEGIN   = SEEK_SET,
        ORIGIN_END     = SEEK_END,
        ORIGIN_CURRENT = SEEK_CUR
    };

    File();
    File(const char* pFileName, const char* mode);
    ~File();

    Status Open(const char* pFileName, const char* mode);
    Status Close();

    size_t Read(void *pBuffer, size_t elementSize, size_t count);
    size_t Write(void *pBuffer, size_t size, size_t count);

    int    Print(const char *format, ...);

    Status Seek(size_t pos, Origin origin);
    unsigned long long GetLen();

    inline bool IsOpened() { return (m_pFile)?true:false; };

private:
    FILE       *m_pFile;
    DString     m_fileName;
};

class Buffer
{
public:
    Buffer();
    Buffer(const char* pMem, size_t len);
    Buffer(size_t len);
    Buffer(const char* pFileName);
    ~Buffer();

    Status SetBuffer(const char* pMem, size_t len);
    Status Alloc(size_t len);
    Status Load(const char* pFileName);
    void Reset();

    void* Get();

private:
    bool        m_external;
    const char *m_pBuffer;
    size_t      m_len;
};

// Class to safely share arbitrary buffer between several objects
class SharedObject
{
public:
    SharedObject();
    ~SharedObject();
    SharedObject(const SharedObject& right);

    SharedObject& operator = (const SharedObject& right);

    void  attach(void* obj);
    void* deattach();

    inline void* get() { return pInst->pObj; }

    inline int getCounter() { return pInst->counter; }

private:
    struct ObjContainer
    {
        void        *pObj;
        volatile int counter;
    };

    ObjContainer *pInst;
};

/*
// CMD parser
*/
namespace cmd
{

enum KeyFlags
{
    KF_OPTIONAL   = 0x0001, // value can be specified without key, in parsing order
    KF_WAS_PARSED = 0x1000  // key was processed by parser
};

enum KeyType
{
    KT_UNKNOWN = 0,
    KT_STRING,
    KT_INTEGER,
    KT_POSITIVE,
    KT_DOUBLE,
    KT_BOOL,
    KT_DSTRING
};

struct OptDef
{
    char         keyShort;
    const char  *keyLong;
    unsigned int arraySize;
    KeyType      keyType;
    int          flags;
    void        *pValue;
    const char  *pDescription;
};

// Function to parse command line according to options specified in pOptions argument.
// Returns number of successfully parsed options
Status OptParse(int argc, char **argv, const OptDef pOptions[]);

// Function to print option table as usage message
void OptUsage(const OptDef pOptions[]);
}

/*
// Utility functions
*/
const char* GetBaseStatusString(Status status);

template<class T> inline T alignValue(T iValue, T iAlignValue)
{
    return (T)((iValue + (iAlignValue - 1)) & ~(iAlignValue - 1));
}

template<class T> void SortObjects(T *pArray, unsigned int iSize)
{
    bool bOutOfOrder = false;
    T    tempEntry;
    unsigned int i;

    do
    {
        bOutOfOrder = false;
        tempEntry   = pArray[0];
        for(i = 1; i < iSize; i++)
        {
            if(tempEntry.PriorityCompare(pArray[i]))
            {
                pArray[i - 1] = pArray[i];
                pArray[i]     = tempEntry;
                bOutOfOrder    = true;
            }
            else
                tempEntry = pArray[i];
        }
        iSize--;
    } while(bOutOfOrder);
}

DString CheckTestDirs(DString sTestFile);

char* GetProgName(char* argv[]);

#endif
