#include "ChunkManager.h"

ChunkManager::ChunkManager(EncryptionManager& em)
	:eman(em)
{ }

ChunkManager::~ChunkManager()
{ }

//-----------------------------------------------------------------------------
//	Kompresja blokowa
//	
// Dzielenie vectora na chunki dokładnie po 128KB
// Kompresowanie chunków bez nagłówka
//-----------------------------------------------------------------------------
std::vector<char> ChunkManager::chunked(const std::vector<char>& raw, const bool& compress, const bool& encrypt)
{
	//std::vector<BlockSize> blockSizes;

	// Maksymalny rozmiar chunka
	const size_t maxBlockSize = BLOCK_SIZE;
	const size_t rawSize = raw.size();

	uint16_t blockLen = 0;
	uint32_t lastChunkRawSize;
	std::vector<char> compressedBlocks;
	for (size_t offset = 0; offset < rawSize; offset += maxBlockSize)
	{
		// Rozmiar chunka
		const size_t chunkSize = std::min(maxBlockSize, rawSize - offset);

		auto begin = raw.begin() + offset;
		auto end = begin + chunkSize;

		// Skopiuj fragment danych do chunka
		std::vector<char> chunk(begin, end);

		std::vector<char> outChunk;

		// Przetwórz chunki i przetwórz
		if (compress)
		{
			// Zaszyfruj i skompresuj lub tylko skompresuj
			outChunk = encrypt ? eman.encrypt(cman.compress(chunk)) : cman.compress(chunk);
		}
		else
		{
			// Zaszyfruj lub skopiuj
			outChunk = encrypt ? eman.encrypt(chunk) : std::move(chunk);
		}

		uint32_t chs = chunk.size();
		uint32_t zch = outChunk.size();

		//addIntToVector<uint32_t>(compressedBlocks, chs);
		lastChunkRawSize = chs;
		addIntToVector<uint32_t>(compressedBlocks, zch);
		compressedBlocks.insert(compressedBlocks.end(), outChunk.begin(), outChunk.end());

		blockLen++;
	}

	std::vector<char> zip;
	// Wstaw liczbę o ilości bloków do vectora;
	// Przekonpwertuj usigned int32 na ciąg znkaów
	// uint16_t blockLen = blockSizes .size();
	addIntToVector<uint16_t>(zip, blockLen);
	addIntToVector<uint32_t>(zip, maxBlockSize);
	addIntToVector<uint32_t>(zip, lastChunkRawSize);

	// Dodaj skompresowane dane
	zip.insert(zip.end(), compressedBlocks.begin(), compressedBlocks.end());

	return zip;
}

//////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
//	Dekompresja blokowa
//-----------------------------------------------------------------------------
std::vector<char> ChunkManager::dechunked(const std::vector<char>& zip, const bool& compress, const bool& encrypt)
{
	size_t offset = 0;
	const uint16_t chunkLen = getIntFromVector<uint16_t>(zip, offset);
	const uint32_t chunkBeforeSize = getIntFromVector<uint32_t>(zip, offset);
	const uint32_t chunkLastSize = getIntFromVector<uint32_t>(zip, offset);

	std::vector<char> chunksString;

	// Dekompresja bloków
	for (size_t i = 0; i < chunkLen; ++i)
	{
		// Pobierz rozmiar chunków przed i po skompresowaniem
		uint32_t chunkSize = i < chunkLen - 1 ? chunkBeforeSize : chunkLastSize;
		uint32_t chunkZipSize = getIntFromVector<uint32_t>(zip, offset);

		// Pobierz blok chunka
		std::vector<char> inChunk(chunkZipSize);
		std::memcpy(inChunk.data(), zip.data() + offset, chunkZipSize);
		offset += chunkZipSize;

		// Jeśli flaga encrypt jest aktywna najpierw zdeszyfruj blok
		std::vector<char> zipChunk = encrypt ? eman.decrypt(inChunk) : std::move(inChunk);

		// Zdeklarój pusty chunk
		std::vector<char> chunk = compress ? cman.decompress(zipChunk, chunkSize) : std::move(zipChunk);

		// Scal chunki
		chunksString.insert(chunksString.end(), chunk.begin(), chunk.end());
	}

	return chunksString;
}