#include "export_csv.hpp"

#include <boost/filesystem.hpp>

const char *ExportCsv::initDescription() {
	return "Writing event in Comma-Separated-Value format.";
}

void ExportCsv::initInputs(dv::InputDefinitionList &in) {
	in.addEventInput("events");
}

void ExportCsv::initConfigOptions(dv::RuntimeConfig &config) {
	config.add("fileName", dv::ConfigOption::fileSaveOption("Where to save the output CSV file.", "csv,txt"));

	config.setPriorityOptions({"fileName"});
}

ExportCsv::ExportCsv() :
	sampleLineSize(fmt::format("{:d},{:d},{:d},{:d}\n", std::numeric_limits<int64_t>::max(),
		std::numeric_limits<int16_t>::max(), std::numeric_limits<int16_t>::max(), 1)
			.size()) {
	path = boost::filesystem::path(config.getString("fileName"));

	if (path.empty() || path.parent_path().empty() || path.filename().empty()) {
		throw std::runtime_error("Output file path not specified.");
	}

	if (path.extension().empty()) {
		throw std::runtime_error("File extension not specified.");
	}

	if (path.extension().string() != ".csv" && path.extension().string() != ".txt") {
		throw std::runtime_error(
			"File extension is not CSV/TXT, given wrong extension is: " + path.extension().string());
	}

	if (!boost::filesystem::exists(path.parent_path()) || !boost::filesystem::is_directory(path.parent_path())) {
		throw std::runtime_error(
			"Output directory doesn't exist or is not a directory: " + path.parent_path().string());
	}

	file = boost::nowide::fopen(path.string().c_str(), "wb");

	if (file == nullptr) {
		throw std::runtime_error("Output file could not be opened, please check the given path.");
	}

	// Header text.
	std::string header = fmt::format("# Input: {} sizeX: {} sizeY: {}\ntimestamp,x,y,polarity\n",
		inputs.getEventInput("events").getOriginDescription(), inputs.getEventInput("events").sizeX(),
		inputs.getEventInput("events").sizeY());
	std::fwrite(header.c_str(), sizeof(char), header.size(), file);
}

ExportCsv::~ExportCsv() noexcept {
	std::fclose(file);
}

void ExportCsv::run() {
	auto inEvent = inputs.getEventInput("events").events();

	fileContentsToBeWritten.reserve(inEvent.size() * sampleLineSize);

	for (const auto &event : inEvent) {
		fileContentsToBeWritten
			+= fmt::format("{:d},{:d},{:d},{:d}\n", event.timestamp(), event.x(), event.y(), event.polarity());
	}

	std::fwrite(fileContentsToBeWritten.c_str(), sizeof(char), fileContentsToBeWritten.size(), file);
	fileContentsToBeWritten.clear();
}
