#pragma once
#include "InterpreterDefs.h"

namespace hybridclr
{
namespace interpreter
{

	inline void Copy1(void* dst, void* src)
	{
		*(uint8_t*)dst = *(uint8_t*)src;
	}

	inline void Copy2(void* dst, void* src)
	{
		*(uint16_t*)dst = *(uint16_t*)src;
	}

	inline void Copy4(void* dst, void* src)
	{
		*(uint32_t*)dst = *(uint32_t*)src;
	}

	inline void Copy8(void* dst, void* src)
	{
		*(uint64_t*)dst = *(uint64_t*)src;
	}

	inline void Copy12(void* dst, void* src)
	{
		if (dst <= src)
		{
			*(uint64_t*)dst = *(uint64_t*)src;
			*(uint32_t*)((byte*)dst + 8) = *(uint32_t*)((byte*)src + 8);
		}
		else
		{
			*(uint32_t*)((byte*)dst + 8) = *(uint32_t*)((byte*)src + 8);
			*(uint64_t*)dst = *(uint64_t*)src;
		}
	}

	inline void Copy16(void* dst, void* src)
	{
		if (dst <= src)
		{
			*(uint64_t*)dst = *(uint64_t*)src;
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
		}
		else
		{
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint64_t*)dst = *(uint64_t*)src;
		}
	}

	inline void Copy20(void* dst, void* src)
	{
		if (dst <= src)
		{
			*(uint64_t*)dst = *(uint64_t*)src;
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint32_t*)((byte*)dst + 16) = *(uint32_t*)((byte*)src + 16);
		}
		else
		{
			*(uint32_t*)((byte*)dst + 16) = *(uint32_t*)((byte*)src + 16);
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint64_t*)dst = *(uint64_t*)src;
		}
	}

	inline void Copy24(void* dst, void* src)
	{
		if (dst <= src)
		{
			*(uint64_t*)dst = *(uint64_t*)src;
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint64_t*)((byte*)dst + 16) = *(uint64_t*)((byte*)src + 16);
		}
		else
		{
			*(uint64_t*)((byte*)dst + 16) = *(uint64_t*)((byte*)src + 16);
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint64_t*)dst = *(uint64_t*)src;
		}
	}

	inline void Copy28(void* dst, void* src)
	{
		if (dst <= src)
		{
			*(uint64_t*)dst = *(uint64_t*)src;
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint64_t*)((byte*)dst + 16) = *(uint64_t*)((byte*)src + 16);
			*(uint32_t*)((byte*)dst + 24) = *(uint32_t*)((byte*)src + 24);
		}
		else
		{
			*(uint32_t*)((byte*)dst + 24) = *(uint32_t*)((byte*)src + 24);
			*(uint64_t*)((byte*)dst + 16) = *(uint64_t*)((byte*)src + 16);
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint64_t*)dst = *(uint64_t*)src;
		}
	}

	inline void Copy32(void* dst, void* src)
	{
		if (dst <= src)
		{
			*(uint64_t*)dst = *(uint64_t*)src;
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint64_t*)((byte*)dst + 16) = *(uint64_t*)((byte*)src + 16);
			*(uint64_t*)((byte*)dst + 24) = *(uint64_t*)((byte*)src + 24);
		}
		else
		{
			*(uint64_t*)((byte*)dst + 24) = *(uint64_t*)((byte*)src + 24);
			*(uint64_t*)((byte*)dst + 16) = *(uint64_t*)((byte*)src + 16);
			*(uint64_t*)((byte*)dst + 8) = *(uint64_t*)((byte*)src + 8);
			*(uint64_t*)dst = *(uint64_t*)src;
		}
	}

	inline void CopyStackObject(StackObject* dst, void* vsrc, uint32_t count)
	{
		StackObject* src = (StackObject*)vsrc;
		IL2CPP_ASSERT(dst + count <= src || src + count <= dst);
		switch (count)
		{
		case 8: dst[7] = src[7];
		case 7: dst[6] = src[6];
		case 6: dst[5] = src[5];
		case 5: dst[4] = src[4];
		case 4: dst[3] = src[3];
		case 3: dst[2] = src[2];
		case 2: dst[1] = src[1];
		case 1: *dst = *src; break;
		case 0: break;
		default: std::memcpy(dst, src, count * sizeof(StackObject));
		}
	}

	inline void CopyBySize(void* dst, void* src, uint32_t size)
	{
		switch (size)
		{
		case 1: Copy1(dst, src); break;
		default: std::memmove(dst, src, size); break;
		}
	}

	inline void InitDefault1(void* dst)
	{
		*(uint8_t*)dst = 0;
	}

	inline void InitDefault2(void* dst)
	{
		*(uint16_t*)dst = 0;
	}

	inline void InitDefault4(void* dst)
	{
		*(uint32_t*)dst = 0;
	}

	inline void InitDefault8(void* dst)
	{
		*(uint64_t*)dst = 0;
	}

	inline void InitDefault12(void* dst)
	{
		int32_t* p = (int32_t*)dst;
		p[0] = 0;
		p[1] = 0;
		p[2] = 0;
	}

	inline void InitDefault16(void* dst)
	{
		*(uint64_t*)dst = 0;
		*(uint64_t*)((byte*)dst + 8) = 0;
	}

	inline void InitDefault20(void* dst)
	{
		int32_t* p = (int32_t*)dst;
		p[0] = 0;
		p[1] = 0;
		p[2] = 0;
		p[3] = 0;
		p[4] = 0;
	}

	inline void InitDefault24(void* dst)
	{
		*(uint64_t*)dst = 0;
		*(uint64_t*)((byte*)dst + 8) = 0;
		*(uint64_t*)((byte*)dst + 16) = 0;
	}

	inline void InitDefault28(void* dst)
	{
		int32_t* p = (int32_t*)dst;
		p[0] = 0;
		p[1] = 0;
		p[2] = 0;
		p[3] = 0;
		p[4] = 0;
		p[5] = 0;
		p[6] = 0;
	}

	inline void InitDefault32(void* dst)
	{
		*(uint64_t*)dst = 0;
		*(uint64_t*)((byte*)dst + 8) = 0;
		*(uint64_t*)((byte*)dst + 16) = 0;
		*(uint64_t*)((byte*)dst + 24) = 0;
	}

	inline void InitDefaultN(void* dst, size_t size)
	{
		std::memset(dst, 0, size);
	}
}
}