/** GPU managment.
 *
 * Author: Daniel Krebs <github@daniel-krebs.net>
 * SPDX-FileCopyrightText: 2017 Institute for Automation of Complex Power Systems, EONERC
 * SPDX-License-Identifier: Apache-2.0
 *********************************************************************************/

#pragma once

#include <sstream>

#include <villas/plugin.hpp>
#include <villas/memory_manager.hpp>
#include <villas/memory.hpp>
#include <villas/log.hpp>

namespace villas {
namespace gpu {

class GpuAllocator;

class Gpu {
	friend GpuAllocator;
public:
	Gpu(int gpuId);
	~Gpu();

	bool init();

	std::string getName() const;

	GpuAllocator &getAllocator() const
	{ return *allocator; }


	bool makeAccessibleToPCIeAndVA(const MemoryBlock &mem);

	// Make some memory block accssible for this GPU
	bool makeAccessibleFromPCIeOrHostRam(const MemoryBlock &mem);

	void memcpySync(const MemoryBlock &src, const MemoryBlock &dst, size_t size);

	void memcpyKernel(const MemoryBlock &src, const MemoryBlock &dst, size_t size);

	MemoryTranslation
	translate(const MemoryBlock &dst);

private:
	bool registerIoMemory(const MemoryBlock &mem);
	bool registerHostMemory(const MemoryBlock &mem);

private:
	class impl;
	std::unique_ptr<impl> pImpl;

	// Master, will be used to derived slave addr spaces for allocation
	MemoryManager::AddressSpaceId masterPciEAddrSpaceId;

	MemoryManager::AddressSpaceId slaveMemoryAddrSpaceId;

	Logger logger;

	int gpuId;

	std::unique_ptr<GpuAllocator> allocator;
};


class GpuAllocator : public BaseAllocator<GpuAllocator> {
public:
	static constexpr size_t GpuPageSize = 64UL << 10;

	GpuAllocator(Gpu &gpu);

	std::string getName() const;

	std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
	allocateBlock(size_t size);

private:
	Gpu &gpu;
	// TODO: replace by multimap (key is available memory)
	std::list<std::unique_ptr<LinearAllocator>> chunks;
};

class GpuFactory : public Plugin {
public:
	GpuFactory();

	std::list<std::unique_ptr<Gpu>>
	make();

	void run(void*);

private:
	Logger logger;
};

} /* namespace villas */
} /* namespace gpu */