#include "CompressingManager.h"

CompressingManager::CompressingManager()
{ }

CompressingManager::~CompressingManager()
{ }

//-----------------------------------------------------------------------------
//	Kompresja blokowa
//	
// Dzielenie vectora na chunki dokładnie po 128KB
// Kompresowanie chunków bez nagłówka
//-----------------------------------------------------------------------------
std::vector<char> CompressingManager::compress(const std::vector<char>& raw)
{
	//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);

		// Obliczanie rozmiaru skompresowanego bloku
		int maxZipChunkSize = LZ4_compressBound(chunkSize);

		// Buffor wyjściowy nadpisany skompresowanymi danymi
		std::vector<char> zipChunk(maxZipChunkSize);

		// Kompresja
		int zipSize = LZ4_compress_default(chunk.data(), zipChunk.data(), chunkSize, maxZipChunkSize);

		// Zmiana rozmiaru do faktycznego rozmiaru po kompresji
		zipChunk.resize(zipSize);

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

		//addIntToVector<uint32_t>(compressedBlocks, chs);
		lastChunkRawSize = chs;
		addIntToVector<uint32_t>(compressedBlocks, zch);
		compressedBlocks.insert(compressedBlocks.end(), zipChunk.begin(), zipChunk.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> CompressingManager::decompress(const std::vector<char>& zip)
{
	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> zipChunk(chunkZipSize);
		std::memcpy(zipChunk.data(), zip.data() + offset, chunkZipSize);
		offset += chunkZipSize;

		// Zdeklarój pusty chunk
		std::vector<char> chunk(chunkSize);

		// Dekompresja chunka
		int sizeData = LZ4_decompress_safe(zipChunk.data(), chunk.data(), static_cast<int>(chunkZipSize), static_cast<int>(chunkSize));

		if (sizeData < 0)
		{
			throw std::runtime_error("LZ4 Decompressing Error");
		}

		// Dostosowanie rozmiaru vectora po skompresowaniu
		chunk.resize(sizeData);

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

	return chunksString;
}