//----------------------------------------------------------------------------
// Copyright 2012-2025 Joe Lowe
//
// Permission is granted to any person obtaining a copy of this Software,
// to deal in the Software without restriction, including the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and sell copies of
// the Software.
//
// The above copyright and permission notice must be left intact in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS WITHOUT WARRANTY.
//----------------------------------------------------------------------------
// file name:  portability.c
// created:    2012.05.04
//----------------------------------------------------------------------------
#ifdef _MSC_VER

#ifndef _CRT_NONSTDC_NO_WARNINGS
#define _CRT_NONSTDC_NO_WARNINGS
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE
#endif
#pragma warning(disable: 4100) // unreferenced parameter
#pragma warning(disable: 4163) // not available as intrinsic
#pragma warning(disable: 4505) // unreferenced function
#pragma warning(disable: 4200) // zero sized array in struct
#pragma warning(disable: 6011) // dereference nullptr
#ifdef __cplusplus
#pragma warning(disable: 4355) // this reference in base constructor list
#endif

#else

#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif

#endif

#include <stdint.h>
#include <limits.h>
#include <stdarg.h>
#include <string.h>
#include <wchar.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>

#ifdef _WIN32

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x501
#include <windows.h>
#ifndef FD_T
#define FD_T HANDLE
#define FD_INVALID INVALID_HANDLE_VALUE
#endif

#else

#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#ifndef FD_T
#define FD_T int
#define FD_INVALID -1
#endif

#endif

#ifndef containerof
#define containerof(s,m,pm) ((s*)((const char*)(pm)-offsetof(s,m)))
#endif
#ifndef countof
#define countof(a) (sizeof(a)/sizeof(a[0]))
#endif
#ifndef scast
#define scast(t,e) ((t)((e) +!sizeof(*(uint64_t*)0=1u+(e)) +!sizeof(*(uint64_t*)0=1u+(t)1) ))
#endif
#define IGNORE_LEAK(e)
#ifndef ASSERT
#define ASSERT(e)
#endif
#ifndef C_ASSERT
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
#endif
#ifndef __cplusplus
#undef nullptr
#define nullptr ((void*)0)
#endif

#ifdef _WIN32

static __inline int strcmpf( const char* a, const char* b)
{
   return _stricmp( a, b);
}

static __inline HANDLE stdout_fd( void)
{
   return GetStdHandle( STD_OUTPUT_HANDLE);
}

static HANDLE stdin_fd( void)
{
   return GetStdHandle( STD_INPUT_HANDLE);
}

static int/*err*/ read_fd( HANDLE fd, void* data, size_t max_size, size_t* out_size)
{
   int err = 0;
   DWORD size;

   if (!ReadFile( fd, data, (unsigned)max_size, &size, 0/*overlapped*/))
   {
      size = 0;
      err = GetLastError();
   }

   *out_size = size;
   return err;
}

static void close_fd( HANDLE fd)
{
   if (fd != FD_INVALID)
   {
      CloseHandle( fd);
   }
}

static long last_pipe_instance = 0;

static int/*err*/ create_socket_( int/*bool*/ bidir, int/*bool*/ async, HANDLE* outHandle1, HANDLE* outHandle2)
{
   int err = 0;
   HANDLE handle1;
   HANDLE handle2 = INVALID_HANDLE_VALUE;
   int options1 = FILE_FLAG_FIRST_PIPE_INSTANCE;
   int options2 = 0;
   int access2 = GENERIC_WRITE;
   __int64 now;
   enum { maxPipeNameChars = 100 };
   wchar_t pipeName[ maxPipeNameChars];

   if (bidir)
   {
      options1 |= PIPE_ACCESS_DUPLEX;
      access2 |= GENERIC_READ;
   }
   else
   {
      options1 |= PIPE_ACCESS_INBOUND;
   }
   if (async)
   {
      options1 |= FILE_FLAG_OVERLAPPED;
      options2 |= FILE_FLAG_OVERLAPPED;
   }
      // CreatePipe creates handles that do not support overlapped IO and
      // are unidirectional. Use named pipe instead.
   GetSystemTimeAsFileTime( (FILETIME*)&now );
   wsprintfW( pipeName, L"\\\\.\\Pipe\\AnonymousPipe.%u.%p.%d.%I64d", GetCurrentProcessId(), &now, InterlockedIncrement( &last_pipe_instance), now);
   handle1 = CreateNamedPipeW( pipeName, options1, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 1/*pipeCount*/, 0/*outBufSize*/, 0/*inBufSize*/, 30*1000/*timeout*/, 0/*security*/);
   if (!handle1)
   {
      handle1 = INVALID_HANDLE_VALUE;
   }
   if (handle1 == INVALID_HANDLE_VALUE)
   {
      err = GetLastError();
   }
   if (!err)
   {
      handle2 = CreateFileW( pipeName, access2, 0/*shareMode*/, 0/*security*/, OPEN_EXISTING, options2, 0/*template*/);
      if (handle2 == INVALID_HANDLE_VALUE)
      {
         err = GetLastError();
         CloseHandle( handle1);
         handle1 = INVALID_HANDLE_VALUE;
      }
   }

   *outHandle1 = handle1;
   *outHandle2 = handle2;
   return err;
}

static int/*err*/ create_socket( HANDLE* fd1, HANDLE* fd2)
{
   return create_socket_( 1/*bidir*/, 1/*async*/, fd1, fd2);
}

#else

static __inline__ int strcmpf( const char* a, const char* b)
{
   return strcmp( a, b);
}

static __inline__ int stdout_fd( void)
{
   return 1;
}

//static int/*err*/ read_fd( int fd, void* data, size_t max_size, size_t* out_size)
//{
//   int err = 0;
//   ptrdiff_t size;
//
//   do
//   {
//      size = read( fd, data, (unsigned)max_size);
//      if (size < 0)
//      {
//         err = errno;
//         size = 0;
//      }
//   } while (err == EINTR);
//
//   *out_size = (size_t)size;
//   return err;
//}

static void close_fd( int fd)
{
   if (fd != FD_INVALID)
   {
      close( fd);
   }
}

static int/*err*/ create_socket( int* fd1, int* fd2)
{
   int err = 0;
   int fds[2];

   if (socketpair( AF_UNIX, SOCK_STREAM, 0, fds) != 0)
   {
      err = errno;
      fds[0] = -1;
      fds[1] = -1;
   }

   *fd1 = fds[0];
   *fd2 = fds[1];
   return err;
}

//static void setlocale_once(void)
//{
//   static int first = 0;
//   if(!first)
//   {
//      first = 1;
//      setlocale(LC_CTYPE,0);
//   }
//}

#endif
