// SPDX-License-Identifier: BSD-3-Clause
#ifndef SUBSTRATE_INTERNAL_FD_COMPAT
#define SUBSTRATE_INTERNAL_FD_COMPAT

#include <algorithm>
#include <cstdint>
#include <cstddef>
#ifndef _MSC_VER
#	include <unistd.h>
#else
#	include <io.h>
#endif
#include <sys/stat.h>
#include <substrate/internal/types>

#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__)
#	if !defined(_WINDOWS)
#		define _WINDOWS 1
#	endif
#endif

#if defined(_WIN32)
#	define O_NOCTTY _O_BINARY
#	if defined(__MINGW32__) || defined(__MINGW64__)
#		include <share.h>
#		undef fstat // macro conflicts with fstat definition below
#	endif
#else
#	define O_TEXT 0
#	define O_BINARY 0
#endif


// NOLINTNEXTLINE(modernize-concat-nested-namespaces)
namespace substrate
{
	namespace internal
	{
#ifdef _WIN32
		using stat_t = struct ::_stat64;
#else
		using stat_t = struct stat;
#endif

#ifndef _WIN32
		inline int32_t fdopen(const char *const fileName, const int flags, const mode_t mode) noexcept
			{ return open(fileName, flags, mode); }
		inline ssize_t fdread(const int32_t fd, void *const bufferPtr, const size_t bufferLen) noexcept
			{ return read(fd, bufferPtr, bufferLen); }
		inline ssize_t fdwrite(const int32_t fd, const void *const bufferPtr, const size_t bufferLen) noexcept
			{ return write(fd, bufferPtr, bufferLen); }
		using ::fstat;
		inline off_t fdseek(const int32_t fd, const off_t offset, const int32_t whence) noexcept
			{ return lseek(fd, offset, whence); }
		inline off_t fdtell(const int32_t fd) noexcept
			{ return lseek(fd, 0, SEEK_CUR); }
		inline int32_t fdtruncate(const int32_t fd, const off_t size) noexcept
			{ return ftruncate(fd, size); }

		inline int32_t fdclose(const int32_t fd) noexcept
			{ return close(fd); }
		inline int32_t fddup(const int32_t fd) noexcept
			{ return dup(fd); }
#else
		inline int32_t fdopen(const char *const fileName, const int flags, const mode_t mode) noexcept
		{
			int32_t fd{};
			_sopen_s(&fd, fileName, flags, _SH_DENYNO, mode);
			return fd;
		}

		inline int32_t fdopen(const wchar_t *const fileName, const int flags, const mode_t mode) noexcept
		{
			int32_t fd{};
			_wsopen_s(&fd, fileName, flags, _SH_DENYNO, mode);
			return fd;
		}

		inline ssize_t fdread(const int32_t fd, void *const bufferPtr, const size_t bufferLen) noexcept
			{ return _read(fd, bufferPtr, static_cast<uint32_t>(std::min<size_t>(bufferLen, INT_MAX))); }
		inline ssize_t fdwrite(const int32_t fd, const void *const bufferPtr, const size_t bufferLen) noexcept
			{ return _write(fd, bufferPtr, static_cast<uint32_t>(std::min<size_t>(bufferLen, INT_MAX))); }
		inline int fstat(int32_t fd, stat_t *stat) noexcept { return _fstat64(fd, stat); }
		inline off_t fdseek(const int32_t fd, const off_t offset, const int32_t whence) noexcept
			{ return _lseeki64(fd, offset, whence); }
		inline off_t fdtell(const int32_t fd) noexcept
			{ return _telli64(fd); }
		inline int32_t fdtruncate(const int32_t fd, const off_t size) noexcept
			{ return _chsize_s(fd, size); }

		inline int32_t fdclose(const int32_t fd) noexcept
			{ return _close(fd); }
		inline int32_t fddup(const int32_t fd) noexcept
			{ return _dup(fd); }
#endif
	} // namespace substrate::internal
} // namespace substrate

#endif /* SUBSTRATE_INTERNAL_FD_COMPAT */
/* vim: set ft=cpp ts=4 sw=4 noexpandtab: */
