/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2023, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#include "io-precomp.h"	 // Precompiled headers
//
#include <mrpt/core/exceptions.h>
#include <mrpt/io/CFileInputStream.h>
#include <mrpt/io/CFileOutputStream.h>
#include <mrpt/io/vector_loadsave.h>
#include <mrpt/system/os.h>

using namespace mrpt;
using namespace mrpt::io;
using namespace std;
namespace os = mrpt::system::os;

bool mrpt::io::loadBinaryFile(
	std::vector<uint8_t>& out_data, const std::string& fileName)
{
	try
	{
		CFileInputStream fi(fileName);
		size_t N = fi.getTotalBytesCount();

		out_data.resize(N);
		if (N)
		{
			size_t NN = fi.Read(&out_data[0], N);
			return NN == N;
		}
		else
			return true;
	}
	catch (...)
	{
		return false;
	}
}

bool mrpt::io::vectorToBinaryFile(
	const std::vector<uint8_t>& vec, const std::string& fileName)
{
	try
	{
		mrpt::io::CFileOutputStream of(fileName);
		if (!vec.empty()) of.Write(&vec[0], sizeof(vec[0]) * vec.size());
		return true;
	}
	catch (...)
	{
		return false;
	}
}

bool mrpt::io::loadTextFile(
	std::vector<std::string>& o, const std::string& fileName)
{
	o.clear();
	std::ifstream f(fileName);
	if (!f.is_open()) return false;
	std::string s;
	while (std::getline(f, s))
		o.emplace_back(std::move(s));
	return true;
}

std::string mrpt::io::file_get_contents(const std::string& fileName)
{
	// Credits: https://stackoverflow.com/a/2602258/1631514
	// Note: Add "binary" to make sure the "tellg" file size matches the actual
	// number of read bytes afterwards:
	std::ifstream t(fileName, ios::binary);
	if (!t.is_open())
		THROW_EXCEPTION_FMT(
			"file_get_contents(): Error opening for read file `%s`",
			fileName.c_str());

	t.seekg(0, std::ios::end);
	std::size_t size = t.tellg();
	std::string buffer(size, ' ');
	t.seekg(0);
	t.read(&buffer[0], size);
	return buffer;
}

bool mrpt::io::vectorToTextFile(
	const vector<float>& vec, const string& fileName, bool append, bool byRows)
{
	FILE* f = os::fopen(fileName.c_str(), append ? "at" : "wt");
	if (!f) return false;

	for (float it : vec)
		os::fprintf(f, byRows ? "%e " : "%e\n", it);

	if (byRows) os::fprintf(f, "\n");

	os::fclose(f);
	return true;  // All ok.
}

bool mrpt::io::vectorToTextFile(
	const vector<double>& vec, const string& fileName, bool append, bool byRows)
{
	FILE* f = os::fopen(fileName.c_str(), append ? "at" : "wt");
	if (!f) return false;

	for (double it : vec)
		os::fprintf(f, byRows ? "%e " : "%e\n", it);

	if (byRows) os::fprintf(f, "\n");

	os::fclose(f);
	return true;  // All ok.
}

bool mrpt::io::vectorToTextFile(
	const vector<int>& vec, const string& fileName, bool append, bool byRows)
{
	FILE* f = os::fopen(fileName.c_str(), append ? "at" : "wt");
	if (!f) return false;

	for (int it : vec)
		os::fprintf(f, byRows ? "%i " : "%i\n", it);

	if (byRows) os::fprintf(f, "\n");

	os::fclose(f);
	return true;  // All ok.
}

bool mrpt::io::vectorToTextFile(
	const vector<size_t>& vec, const string& fileName, bool append, bool byRows)
{
	FILE* f = os::fopen(fileName.c_str(), append ? "at" : "wt");
	if (!f) return false;

	for (unsigned long it : vec)
		os::fprintf(f, byRows ? "%u " : "%u\n", static_cast<unsigned int>(it));

	if (byRows) os::fprintf(f, "\n");

	os::fclose(f);
	return true;  // All ok.
}

bool mrpt::io::vectorNumericFromTextFile(
	std::vector<double>& vec, const std::string& fileName, bool byRows)
{
	FILE* f = os::fopen(fileName.c_str(), "r");
	if (!f) return false;

	double number = 0;

	while (!feof(f))
	{
		size_t readed = fscanf(f, byRows ? "%lf" : "%lf\n", &number);
		if ((!byRows) || (readed == 1)) vec.push_back(number);
	}

	return true;
}
