#include "filebuffer.hpp"

#include <dv-processing/core/dvassert.hpp>

using namespace dv;

FileBuffer::FileBuffer() {
}

FileBuffer::FileBuffer(std::vector<FileDataDefinition> &Table) {
	packetsDat.reserve(Table.size());

	for (auto elem : Table) {
		PacketBuffer newPack;
		newPack.packet = elem;
		newPack.status = StatusPacket::notRead;
		newPack.cached = false;
		packetsDat.push_back(newPack);
	}
}

void FileBuffer::addToCache(const PacketBuffer &packetToAdd, const std::vector<char> &dataPtr, const size_t dataSize) {
	dv::runtime_assert(
		[dataPtr] {
			return !dataPtr.empty();
		},
		[] {
			return "data vector cannot be empty";
		});
	dv::runtime_assert(
		[dataSize] {
			return dataSize != 0;
		},
		[] {
			return "data size cannot be zero";
		});

	mapPtr.insert({packetToAdd.packet.ByteOffset, dataPtr});
	mapSize.insert({packetToAdd.packet.ByteOffset, dataSize});

	auto added = std::find(packetsDat.begin(), packetsDat.end(), packetToAdd);

	if (added == packetsDat.end()) {
		// Not found.
		return;
	}

	added->cached = true;
}

void FileBuffer::removeFromCache(PacketBuffer &packetToRemove) {
	dv::runtime_assert(
		[packetToRemove] {
			return packetToRemove.packet.ByteOffset != 0;
		},
		[] {
			return "packet byte offset cannot be zero";
		});
	dv::runtime_assert(
		[this, packetToRemove] {
			return mapPtr.find(packetToRemove.packet.ByteOffset) != mapPtr.cend();
		},
		[] {
			return "packet not found in cache, but only existing packets can be removed";
		});

	mapPtr.erase(packetToRemove.packet.ByteOffset);
	mapSize.erase(packetToRemove.packet.ByteOffset);
	packetToRemove.status = StatusPacket::notRead;
}

void FileBuffer::updatePacketsTimeRange(
	const std::int64_t startTimestamp, const std::int64_t endTimestamp, const std::int32_t id) {
	currentStartTime = startTimestamp;
	currentStopTime  = endTimestamp;
	packetsDatRange.clear();

	for (auto &packdat : packetsDat) {
		if (packdat.packet.TimestampStart <= currentStopTime && packdat.packet.TimestampEnd >= currentStartTime
			&& packdat.packet.PacketInfo.StreamID() == id) {
			packdat.status = StatusPacket::Read;
			packetsDatRange.push_back(packdat);
		}
	}

	updateCache(); // remove packet not in the timeRange
}

void FileBuffer::updateCache() {
	for (auto &pack : packetsDat) {
		if (pack.status == StatusPacket::Read) {
			if (std::find(packetsDatRange.cbegin(), packetsDatRange.cend(), pack) == packetsDatRange.cend()) {
				if (mapPtr.find(pack.packet.ByteOffset) != mapPtr.cend()) {
					if (pack.packet.TimestampEnd < currentStartTime || pack.packet.TimestampStart > currentStopTime) {
						removeFromCache(pack);
						pack.cached = false;
					}
				}
			}
		}
	}
}

std::vector<PacketBuffer> FileBuffer::getInRange() {
	return packetsDatRange;
}
