/*
 * Copyright 2015 - 2022 gary@drinkingtea.net
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include "defines.hpp"
#include "types.hpp"
#include "typetraits.hpp"

#if __has_include(<cstring>)
#include<cstring>
#else
extern "C" {

void *memcpy(void *dest, const void *src, std::size_t size);

void *memmove(void *dest, const void *src, std::size_t size);

void *memset(void *ptr, int val, std::size_t size);

}
#endif

int ox_memcmp(const void *ptr1, const void *ptr2, std::size_t size) noexcept;

constexpr void *ox_memcpy(void *dest, const void *src, std::size_t size) noexcept {
	if (std::is_constant_evaluated() || !ox::defines::UseStdLib) {
		auto srcBuf = static_cast<const char *>(src);
		auto dstBuf = static_cast<char *>(dest);
		for (std::size_t i = 0; i < size; ++i) {
			dstBuf[i] = static_cast<char>(srcBuf[i]);
		}
		return dest;
	} else {
		return memcpy(dest, src, size);
	}
}

constexpr void *ox_memmove(void *dest, const void *src, std::size_t size) noexcept {
	if (std::is_constant_evaluated() || !ox::defines::UseStdLib) {
		auto srcBuf = static_cast<const char *>(src);
		auto dstBuf = static_cast<char *>(dest);
		for (std::size_t i = 0; i < size; ++i) {
			dstBuf[i] = static_cast<char>(srcBuf[i]);
		}
		return dest;
	} else {
		return memmove(dest, src, size);
	}
}

constexpr void *ox_memset(void *ptr, int val, std::size_t size) noexcept {
	if (std::is_constant_evaluated() || !ox::defines::UseStdLib) {
		auto buf = static_cast<uint8_t *>(ptr);
		for (std::size_t i = 0; i < size; ++i) {
			buf[i] = static_cast<uint8_t>(val);
		}
		return ptr;
	} else {
		return memset(ptr, val, size);
	}
}

namespace ox {

template<typename T>
void *memsetElements(T *ptr, T val, std::size_t elements) noexcept {
	return memset(ptr, val, elements * sizeof(T));
}

}
