#include "pch.h"
#include "Mem\CMem.h"
#include <Windows.h>
#include "AlignSize.h"
//#include "CSystemSemaphore.h"
#include "aLog.h"

// 静态成员变量
size_t                CMemUsage::totalContainerSize=0;
size_t                CMemUsage::totalMemSize=0;
std::list<CMemUsage*> CMemUsage::MemList;
std::mutex            CMemUsage::MemListLock;

std::mutex CMem::uplocker;


void CMemUsage::report(std::wstring& str)
{
	std::lock_guard guard(MemListLock); // 加锁
	_updateUsage();
	str = L"内存使用统计：\n";
	for (auto mem : MemList)
	{
		if(mem->memName.empty()) {
			str += fmt::format(L"未命名, {}, {}k/{}k, {}M/{}M\n", mem->memType.c_str(), mem->memSize / 1_K, mem->containerSize / 1_K, mem->memSize / 1_M, mem->containerSize / 1_M);
		}else {
			str += fmt::format(L"{}, {}, {}k/{}k, {}M/{}M\n", mem->memName.c_str(), mem->memType.c_str(), mem->memSize / 1_K, mem->containerSize / 1_K, mem->memSize / 1_M, mem->containerSize / 1_M);
		}
	}

	str += fmt::format(L"\n总内存：{}M/{}M\n", totalMemSize / 1_M, totalContainerSize / 1_M);
}


bool CMem::_allocVirtual(void* addr, size_t size, bool commit)
{
	const DWORD flag = (commit) ? (MEM_RESERVE | MEM_COMMIT) : MEM_RESERVE;
    // 执行windows api
    Addr = static_cast<uint8_t*>(VirtualAlloc(
        addr,       // 0: 系统自动分配地址, 其他值：指定未使用的内存地址
        size,       // Size of allocation
        flag,
        PAGE_READWRITE)); //只支持Read + Write，未考虑其他情况
	// 结果
    if (Addr) { // 分配成功
        containerSize = size;
        memSize = (commit) ? size : 0;
		selfAlloc = true;
        return true;
    }
    else { // 调用处来处理不成功的情况，这里不管
        containerSize = 0;
        memSize = 0;
		selfAlloc = false;
        return false;
    }
}

uint8_t*  CMem::allocVirtual(size_t size)
{
	// 执行windows api
	return static_cast<uint8_t*>(VirtualAlloc(
		nullptr,    // 0: 系统自动分配地址, 其他值：指定未使用的内存地址
		size,       // Size of allocation
		MEM_RESERVE | MEM_COMMIT,
		PAGE_READWRITE)); //只支持Read + Write，未考虑其他情况
}

void CMem::freeVirtual(uint8_t* addr)
{
	if(addr) VirtualFree(addr, 0, MEM_RELEASE);
	// 未处理失败情况，假设不会失败
	// 此处addr未清零，在dealloc()中清零
}

bool CMem::_allocUpperVirtual(size_t size, bool commit)
{
	const DWORD flag = (commit) ? (MEM_RESERVE | MEM_TOP_DOWN | MEM_COMMIT) : (MEM_RESERVE | MEM_TOP_DOWN);
	// 执行windows api
	Addr = static_cast<uint8_t*>(VirtualAlloc(
		nullptr,       // System selects address
		size,          // Size of allocation
		flag,
		PAGE_READWRITE)); //只支持Read + Write，未考虑其他情况
// 结果
	if (Addr) {
		containerSize = size;
		memSize = 0;
		selfAlloc = true;
		return true;
	}
	else { // 调用处来处理不成功的情况，这里不管
		containerSize = 0;
		memSize = 0;
		selfAlloc = false;
		return false;
	}
}


// 分配虚拟内存
bool CWinMem::alloc(void* addr, size_t container, size_t size) 
{
	// addr可以为0或指定某个未分配的地址
	ASSERT(container != 0); //虚拟内存大小，不能为0
	ASSERT(size <= container);//物理内存大小不超过虚拟内存大小，可以为0

	AlignUp(container, 64 * 1024); // 调整虚拟内存大小对齐到64k。win默认按64k分配虚拟内存
	AlignUp(size, 4 * 1024);	// 调整物理内存大小对齐到4k。win默认按4k分配物理内存

	if (size == 0) {
		return _allocVirtual(addr, container, false); // 不分配物理内存
	} else if (size == container) {
		return _allocVirtual(addr, container, true); // 分配全部物理内存
	} else {
		const bool ret = _allocVirtual(addr, container, false); 
		if (!ret) {
			selfAlloc = false;
			return false;
		}
		if (nullptr != VirtualAlloc(Addr, size, MEM_COMMIT, PAGE_READWRITE)) { // 分配指定大小的物理内存
			memSize = size;
			selfAlloc = true;
		} else {
			selfAlloc = true; // 虽然没有物理内存， 分到虚拟内存了也算selfAlloc
			return false;
		}
	}
	return true;
}

bool CWinMem::allocUpper(size_t container, size_t size)
{
	ASSERT(container != 0);
	ASSERT(size <= container);

	AlignUp(container, 64_K);
	AlignUp(size, 4_K);

	std::lock_guard guard(uplocker);

	if (size == 0) {
		return _allocUpperVirtual(container, false);
	}
	else if (size == container) {
		return _allocUpperVirtual(container, true);
	}
	else {
		const bool ret = _allocUpperVirtual(container, false);
		if (!ret) {
			selfAlloc = false;
			return false;
		}
		if (nullptr != VirtualAlloc(Addr, size, MEM_COMMIT, PAGE_READWRITE)) {
			memSize = size;
			selfAlloc = true;
		}
		else {
			selfAlloc = true; // 虽然没有物理内存， 分到虚拟内存了也算selfAlloc
			return false;
		}
	}

	return true;
}

// 调整大小不影响containerSize、Addr和selfAlloc
bool CWinMem::resize(size_t size) 
{
	if (containerSize == 0) return false;
	AlignUp(size, 4 * 1024);
	if (size > containerSize) return false;
    if (size == memSize) {
        return true;
    }
    else if (size > memSize) {
		if (nullptr != VirtualAlloc(Addr + memSize, size - memSize, MEM_COMMIT, PAGE_READWRITE)) {
			memSize = size;
            return true;
		}
		else {
			return false;
		}
	}
	else {
		VirtualFree(Addr + size, memSize - size, MEM_DECOMMIT); // 有多的释放掉
        memSize = size;
		return true;
	}
};

// 调整大小不影响containerSize、Addr和selfAlloc
bool CWinMem::reserve(size_t size) 
{
	if (containerSize == 0) return false;
	AlignUp(size, 4 * 1024);
	if (size > containerSize) return false;
	if (size == memSize) return true;
	else if (size > memSize) {
		if (nullptr != VirtualAlloc(Addr + memSize, size - memSize, MEM_COMMIT, PAGE_READWRITE)) {
			memSize = size;
            return true;
		}
		else {
			return false;
		}
	}else {
		//VirtualFree(Addr + size, memSize - size, MEM_DECOMMIT); // 有多的不释放
		return true;
	}

}


void CWinMem::dealloc()
{
    if (Addr) {
        VirtualFree(Addr, 0, MEM_RELEASE); // 只能release整个区域
    }
	CMem::dealloc();
}

std::wstring CWinMem::details()
{
	std::wstring str = fmt::format(L"虚拟内存地址：{}, 大小{}M，物理内存大小{}M\n", (void*)Addr, containerSize / 1_M, memSize / 1_M);
	return str;
}



bool CFileMapMem2::allocNamed(const std::wstring& name, size_t container, size_t size)
{
	// 确认调用前的状态，避免逻辑错误
	ASSERT(hand == nullptr);	// 未创建内存映射文件
	ASSERT(containerSize == 0);	// 未映射文件
	ASSERT(allMem.containerSize == 0); // 未分配虚拟内存
	ASSERT(allMem.Addr == nullptr);
	ASSERT(memSize == 0);		// 未映射文件
	// 确认参数有效
	ASSERT(container > 0);
	ASSERT(isAlign(container, 64_K));
	ASSERT(size ==0);

	_nativeKey = std::wstring(L"Ks_Grabber_MemMapFile_") + name;

	std::lock_guard guard(uplocker);

	allMem.Addr = static_cast<uint8_t*>(VirtualAlloc(
		nullptr,       // System selects address
		container * 2,          // Size of allocation
		MEM_RESERVE | MEM_TOP_DOWN,
		PAGE_READWRITE)); //只支持Read + Write，未考虑其他情况
	
	if(!allMem.Addr) return false;

	allMem.containerSize = container * 2;
	allMem.memSize = 0;

	// Create the file mapping.
	hand = CreateFileMapping(INVALID_HANDLE_VALUE
		, nullptr
		, PAGE_READWRITE | SEC_COMMIT // 不能加SEC_NOCACHE
		, container >> 32, container & 0xFFFFFFFFu,
		_nativeKey.c_str());

	if (!hand) {
		VirtualFree(allMem.Addr, 0, MEM_RELEASE); // 释放虚拟内存空间
		allMem.containerSize = 0;
		selfAlloc = false;
		return false;
	}

	// 供外部访问的内存属性
	Addr = nullptr; //地址后面mapFile()时再计算，反正只有映射了才能真正访问到。
	containerSize = container; // 容量可以先保存下来。
	memSize = 0;	
	memName = name;
	selfAlloc = true;

	return true;
}

void CFileMapMem2::dealloc()
{
	std::lock_guard guard(uplocker);
	unmapFile(false);

	if(hand)
	{
		CloseHandle(hand);
		hand = nullptr;
	}
	selfAlloc = false;
}

bool CFileMapMem2::resize(size_t size)
{
	std::lock_guard guard(uplocker);
	if (containerSize == 0) return false;
	if (size > containerSize) return false;

	if (size == 0) {
		if (memSize == containerSize) {
			unmapFile(true);
		}
		else if (memSize == 0) {
			return true;
		}
		else {
			return false;
		}

	}
	else if (size == containerSize) {
		if (memSize == containerSize) {
			return true; // nothing todo
		}
		else if (memSize == 0) {
			return mapFile();
		}
		else {
			return false;
		}

	}
	else {
		return false;
	}

	return true;
}

bool CFileMapMem2::mapFile()
{
	ASSERT(hand != nullptr);
	ASSERT(allMem.containerSize > 0);

	size_t fileSize = allMem.containerSize / 2;
	size_t halfSize1 = fileSize / 2;
	AlignDown(halfSize1, 64_K);
	size_t halfSize2 = allMem.containerSize/2 - halfSize1;

	ULARGE_INTEGER fileOffset1, fileOffset2;
	fileOffset1.QuadPart = 0;
	fileOffset2.QuadPart = halfSize1;
	VirtualFree(allMem.Addr, 0, MEM_RELEASE); // 释放虚拟内存空间
	//但内存总容量allMem.containerSize保持不变
	
	//      内存               映像文件
	// -------------
	// |      2     |
	// ==============  ---  ==============
	// |      1     |       |      1     |
	// --------------       --------------
	// |      2     |       |      2     |
	// ==============  ---  ==============
	// |      1     |
	// --------------
	Addr = (uint8_t*)MapViewOfFileEx(hand, FILE_MAP_ALL_ACCESS
		, fileOffset1.HighPart, fileOffset1.LowPart
		, fileSize, allMem.Addr + halfSize2);

	if(!Addr)
	{
		return false;
	}
	containerSize = memSize = allMem.containerSize/2;

	BottomMem.Addr = (uint8_t*)MapViewOfFileEx(hand, FILE_MAP_READ
		, fileOffset1.HighPart, fileOffset1.LowPart
		, halfSize1, allMem.Addr + halfSize2 + fileSize);

	TopMem.Addr = (uint8_t*)MapViewOfFileEx(hand, FILE_MAP_READ
		, fileOffset2.HighPart, fileOffset2.LowPart
		, halfSize2, allMem.Addr);

	if(!TopMem.Addr || !BottomMem.Addr) // 前面成功了，这里不大可能失败，稳妥起见加上
	{
		UnmapViewOfFile(Addr);
		Addr = nullptr;
		containerSize = memSize = 0;

		if (TopMem.Addr)	UnmapViewOfFile(TopMem.Addr);
		if (BottomMem.Addr) UnmapViewOfFile(BottomMem.Addr);

		TopMem.dealloc();
		BottomMem.dealloc();
		return false;
	}else
	{
		TopMem.containerSize = halfSize2;
		TopMem.memSize = 0;
		BottomMem.containerSize = halfSize1;
		TopMem.memSize = 0;
	}
	verifyMap();
	return true;
}

void CFileMapMem2::unmapFile(bool realloc)
{
	if (containerSize == 0 || hand == nullptr) return;

	if (memSize == containerSize) {
		if (TopMem.Addr)	UnmapViewOfFile(TopMem.Addr);
		if (BottomMem.Addr)	UnmapViewOfFile(BottomMem.Addr);
		if (Addr)			UnmapViewOfFile(Addr);

		TopMem.dealloc();
		BottomMem.dealloc();
		
		Addr = nullptr;
		containerSize = memSize = 0;

		if (realloc) {
			_allocVirtual(allMem.Addr, allMem.containerSize, false);
		}
		else {
			allMem.dealloc();
		}
	}
}

bool CFileMapMem2::verifyMap(bool compare_content)
{
	ASSERT(hand != nullptr);
	// 验证容量
	ASSERT(containerSize != 0);
	ASSERT(memSize = containerSize);
	ASSERT(containerSize = allMem.containerSize / 2);
	ASSERT(TopMem.containerSize + BottomMem.containerSize == containerSize);
	// 验证地址
	ASSERT(allMem.Addr);
	ASSERT(TopMem.Addr == allMem.Addr);
	ASSERT(Addr == allMem.Addr + TopMem.containerSize);
	ASSERT(BottomMem.Addr = Addr + containerSize);
	// 验证内容
	bool succ = true;
	if(compare_content)
	{
		uint64_t* p = (uint64_t*)(Addr);
		for(int i=0; i<containerSize/8; i++)
		{
			*p++ = i;
		}
		if (memcmp(Addr, BottomMem.Addr, BottomMem.containerSize) != 0)
		{
			alog->error("memcmp(Addr, BottomMem.Addr, BottomMem.containerSize) != 0");
			succ = false;
		}
		else {
			alog->info("底部映射验证通过");
		}

		if (memcmp(Addr + BottomMem.containerSize, TopMem.Addr, TopMem.containerSize) != 0)
		{
			alog->error("Addr + BottomMem.containerSize, TopMem.Addr, TopMem.containerSize) != 0");
			succ = false;
		}
		else {
			alog->info("顶部映射验证通过");
		}
	}
	return succ;
}

std::wstring CFileMapMem2::details()
{
	std::wstring str = fmt::format(L"虚拟内存地址：{}, 大小{}M\n", (void *)allMem.Addr, allMem.containerSize/1_M);

	str += fmt::format(L"数据内存地址：{}, 偏移量：{}M，大小{}M\n", (void*)Addr, (Addr - allMem.Addr) / 1_M, memSize / 1_M);
	str += fmt::format(L"底部内存地址：{}, 偏移量：{:4d}M，大小{:4d}M，映射到偏移量{:4d}M\n"
		, (void*)BottomMem.Addr, (BottomMem.Addr - allMem.Addr) / 1_M, BottomMem.containerSize / 1_M, (Addr - allMem.Addr) / 1_M);
	str += fmt::format(L"顶部内存地址：{}, 偏移量：{:4d}M，大小{:4d}M，映射到偏移量{:4d}M\n"
		, (void*)TopMem.Addr, (TopMem.Addr - allMem.Addr) / 1_M, TopMem.containerSize / 1_M, (TopMem.containerSize+ BottomMem.containerSize) / 1_M);

	return str;
}