/*
* 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 https://mozilla.org/MPL/2.0/.
 */

#include "MemMappedFileInternal.hpp"

#include "Base/Logger.hpp"
#include "Host/Windows/ErrorUtils.hpp"

namespace OpenVulkano
{
#ifdef _MSC_VER
	MemMappedFileInternal::MemMappedFileInternal(const char* file, MemMappedFile::FileMode fileMode)
	    : size(0), fileHandle(nullptr), fileMappingHandle(nullptr)
	{
		DWORD openMode { 0 }, mapMode { 0 }, viewMode { 0 };

		switch (fileMode)
		{
			case MemMappedFile::FileMode::READ_ONLY:
				openMode = GENERIC_READ;
				mapMode = PAGE_READONLY;
				viewMode = FILE_MAP_READ;
				break;
			case MemMappedFile::FileMode::WRITE_ONLY:
				openMode = GENERIC_WRITE;
				mapMode = PAGE_READWRITE;
				viewMode = FILE_MAP_WRITE;
				break;
			case MemMappedFile::FileMode::READ_WRITE:
				openMode = GENERIC_ALL;
				mapMode = PAGE_READWRITE;
				viewMode = FILE_MAP_ALL_ACCESS;
				break;
			default:
				break;
		}

		fileHandle = CreateFileA(file, // file to open
		                         openMode, // open for switch case
		                         NULL, // share mode
		                         NULL, // default security
		                         OPEN_ALWAYS, // always
		                         FILE_ATTRIBUTE_NORMAL, // normal file
		                         NULL); // no attribute template

		if (fileHandle == INVALID_HANDLE_VALUE || fileHandle == NULL)
		{
			Logger::FILESYS->critical("Failed to open file: {}.", ErrorUtils::GetLastErrorMessage());
			return;
		}

		LARGE_INTEGER fileSize;
		if (!GetFileSizeEx(fileHandle, &fileSize))
		{
			Logger::FILESYS->critical("Failed to get size of file: {}", ErrorUtils::GetLastErrorMessage());
			CloseHandle(fileHandle);
			return;
		}
		size = fileSize.QuadPart;

		fileMappingHandle = CreateFileMappingA(fileHandle,
		                                       NULL, // default security
		                                       mapMode, // mod for switch case
		                                       0, // maximum object size (high-order)
		                                       0, // maximum object size (low-order)
		                                       NULL); // name of the mapping object
		if (fileMappingHandle == INVALID_HANDLE_VALUE || fileMappingHandle == NULL)
		{
			Logger::FILESYS->critical("Failed to create file mapping handle: {}", ErrorUtils::GetLastErrorMessage());
			CloseHandle(fileHandle);
			size = 0;
			return;
		}

		address = MapViewOfFile(fileMappingHandle, // handle to the map object
		                        viewMode, // access for switch case
		                        0, // max. object size
		                        0, // size of the hFile
		                        0); // map the entire file

		if (address == nullptr)
		{
			Logger::FILESYS->critical("Failed to create file mapping: {}", ErrorUtils::GetLastErrorMessage());
			CloseHandle(fileMappingHandle);
			CloseHandle(fileHandle);
			size = 0;
		}
	}

	MemMappedFileInternal::~MemMappedFileInternal()
	{
		UnmapViewOfFile(address);
		CloseHandle(fileMappingHandle);
		CloseHandle(fileHandle);
	}
#else
	MemMappedFileInternal::MemMappedFileInternal(const char* file, MemMappedFile::FileMode fileMode)
	{
		fileHandle = open(file, fileMode);
		if (fstat(fileHandle, &fileStat) == -1)
		{
			Logger::FILESYS->critical("Failed to query stats for file: {}", file);
			return;
		}
		address = mmap(nullptr, fileStat.st_size, fileMode + 1, MAP_SHARED, fileHandle, 0);
		if (address == MAP_FAILED)
		{
			Logger::FILESYS->critical("Failed to memory map file: {}", file);
			address = nullptr;
		}
		size = fileStat.st_size;
	}

	MemMappedFileInternal::~MemMappedFileInternal()
	{
		munmap(address, fileStat.st_size);
		close(fileHandle);
	}
#endif


}