//
//  Copyright (c) 2019 Rally Tactical Systems, Inc.
//  All rights reserved.
//

#ifndef Platform_h
#define Platform_h

#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>

#if defined(WIN32)
    #include <WinSock2.h>
	#include <Ws2tcpip.h>
	#include <mswsock.h>
#else
	#include <unistd.h>
	#include <arpa/inet.h>
	#include <netinet/in.h>
    #include <signal.h>
	#include <sys/types.h>
	#include <sys/socket.h>
	#include <sys/un.h>

    #if defined( __ANDROID__ )
        #include <sys/prctl.h>
    #endif    
#endif

#include <openssl/x509v3.h>

#ifdef WIN32
    #define strncasecmp                             _strnicmp
    #define strcasecmp                              _stricmp
    #define PATH_MAX                                MAX_PATH
    #define pid_t                                   int
    #define SHUT_RDWR                               SD_BOTH
	#ifdef _WIN64
		#define ssize_t		                        __int64
	#else
		#define ssize_t		                        __int32
	#endif

    #define MY_MIN(a, b)                            ((a) < (b) ? (a) : (b))
    #define MY_MAX(a, b)                            ((a) > (b) ? (a) : (b))
#else
    #include <string.h>

    /* CMake sets these via RTS.cmake when libc provides them; else guess common BSD / modern glibc / musl. */
    #if !defined(RTS_HAVE_STRLCPY)
        #if defined(__APPLE__) || defined(__ANDROID__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__MUSL__)
            #define RTS_HAVE_STRLCPY 1
        #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38))
            #define RTS_HAVE_STRLCPY 1
        #endif
    #endif
    #if !defined(RTS_HAVE_STRLCAT)
        #if defined(__APPLE__) || defined(__ANDROID__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__MUSL__)
            #define RTS_HAVE_STRLCAT 1
        #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38))
            #define RTS_HAVE_STRLCAT 1
        #endif
    #endif

    static const int INVALID_SOCKET = -1;

    // Non-Windows: MSVC-compatible _s helpers (dstsz = total destination buffer size, in bytes).
    namespace rts_platform_str
    {
        inline int strcpy_s(char *dst, size_t dstsz, const char *src)
        {
            if (!dst || dstsz == 0)
            {
                return -1;
            }
            if (!src)
            {
                dst[0] = '\0';
                return -1;
            }
#if defined(RTS_HAVE_STRLCPY)
            const size_t r = strlcpy(dst, src, dstsz);
            return (r >= dstsz) ? -1 : 0;
#else
            size_t i = 0;
            for (; i + 1 < dstsz && src[i]; ++i)
            {
                dst[i] = src[i];
            }
            dst[i] = '\0';
            return (src[i] != '\0') ? -1 : 0;
#endif
        }

        inline int strncpy_s(char *dst, size_t dstsz, const char *src, size_t n)
        {
            if (!dst || dstsz == 0)
            {
                return -1;
            }
            if (!src)
            {
                dst[0] = '\0';
                return -1;
            }
            const size_t cap = dstsz - 1;
            size_t count = n;
            if (count > cap)
            {
                count = cap;
            }
            size_t i = 0;
            for (; i < count && src[i]; ++i)
            {
                dst[i] = src[i];
            }
            dst[i] = '\0';
            return 0;
        }

        inline int strcat_s(char *dst, size_t dstsz, const char *src)
        {
            if (!dst || dstsz == 0)
            {
                return -1;
            }   
            if (!src)
            {
                return -1;
            }
            size_t dlen = 0;
            while (dlen < dstsz && dst[dlen])
            {
                ++dlen;
            }
            if (dlen >= dstsz)
            {
                return -1;
            }
#if defined(RTS_HAVE_STRLCAT)
            const size_t r = strlcat(dst, src, dstsz);
            return (r >= dstsz) ? -1 : 0;
#else
            const size_t remain = dstsz - dlen - 1;
            size_t i = 0;
            for (; i < remain && src[i]; ++i)
            {
                dst[dlen + i] = src[i];
            }
            dst[dlen + i] = '\0';
            return (src[i] != '\0') ? -1 : 0;
#endif
        }

        inline int sprintf_s(char *dst, size_t dstsz, const char *fmt, ...)
        {
            if (!dst || dstsz == 0)
            {
                return -1;
            }
            va_list ap;
            va_start(ap, fmt);
            const int n = static_cast<int>(std::vsnprintf(dst, dstsz, fmt, ap));
            va_end(ap);
            if (n < 0 || static_cast<size_t>(n) >= dstsz)
            {
                dst[dstsz - 1] = '\0';
                return -1;
            }
            return n;
        }
    }

    #define strtok_s(__str, __delim, __ctx)         strtok_r((__str), (__delim), (__ctx))
    #define strcpy_s(__dst, __sz, __src)            (::rts_platform_str::strcpy_s((__dst), (__sz), (__src)))
    #define strncpy_s(__dst, __sz, __src, __mx)     (::rts_platform_str::strncpy_s((__dst), (__sz), (__src), (__mx)))
    #define strcat_s(__dst, __sz, __src)            (::rts_platform_str::strcat_s((__dst), (__sz), (__src)))
    #define sprintf_s(__dst, __sz, __fmt, ...)      (::rts_platform_str::sprintf_s((__dst), (__sz), (__fmt), ##__VA_ARGS__))
    #define SOCKET                                  int
    #define sscanf_s                                sscanf
    #define closesocket                             close
    #define MY_MIN(a, b)                            std::min((a), (b))
    #define MY_MAX(a, b)                            std::max((a), (b))
#endif

#if !defined(htonll)
    #define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
#endif

#if !defined(ntohll)
    #define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
#endif

#ifdef __cplusplus
extern "C"
{
#endif

#if defined(WIN32)
    #ifdef PLATFORM_EXPORTS
        // Windows needs dllexport to produce an import lib without a .DEF file
        #define PLATFORM_API  __declspec(dllexport) extern
    #else
        #define PLATFORM_API  extern
    #endif
#else
    #define PLATFORM_API
#endif

PLATFORM_API bool PlatformInit();
PLATFORM_API void PlatformDeinit();
PLATFORM_API void PlatformOnProcessStarted();
PLATFORM_API void PlatformOnProcessEnded();
PLATFORM_API void PlatformOnThreadStarted();
PLATFORM_API void PlatformOnThreadEnded();
PLATFORM_API void PlatformSetThreadName(const char *nm);
PLATFORM_API void PlatformGetThreadName(char *buff, size_t buffSize);
PLATFORM_API void PlatformOnPlatformChangeNotification(const char *notification);
PLATFORM_API bool PlatformStoreSecret(const char *key, const char *val, const char *aesKeyHex);
PLATFORM_API bool PlatformDeleteSecret(const char *key);
PLATFORM_API bool PlatformGetSecret(const char *key, char **retval, const char *aesKeyHex);
PLATFORM_API void PlatformEnableAec(intptr_t p1, intptr_t p2, intptr_t p3, intptr_t p4);
PLATFORM_API void PlatformDisableAec(intptr_t p1, intptr_t p2, intptr_t p3, intptr_t p4);
PLATFORM_API void PlatformAbort();
PLATFORM_API int PlatformAddTrustedRootCertificates(X509_STORE *store, std::vector<std::string> *locations);
PLATFORM_API void PlatformSetDefaultTrustedRootCertificateLocations(std::vector<std::string> *locations);

#ifdef __cplusplus
}
#endif

#endif /* Platform_h */
