/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2022, 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>

using namespace mrpt::io;
using namespace std;

static_assert(
	!std::is_copy_constructible_v<CFileInputStream> &&
		!std::is_copy_assignable_v<CFileInputStream>,
	"Copy Check");

CFileInputStream::CFileInputStream(const string& fileName) : m_if()
{
	MRPT_START

	// Try to open the file:
	// Open for input:
	if (!open(fileName))
		THROW_EXCEPTION_FMT(
			"Error trying to open file: '%s'", fileName.c_str());

	MRPT_END
}

/*---------------------------------------------------------------
							Constructor
 ---------------------------------------------------------------*/
CFileInputStream::CFileInputStream() : m_if() {}
/*---------------------------------------------------------------
							open
 ---------------------------------------------------------------*/
bool CFileInputStream::open(const string& fileName)
{
	// Try to open the file:
	// Open for input:
	m_if.open(fileName.c_str(), ios_base::binary | ios_base::in);
	m_filename = fileName;
	return m_if.is_open();
}

/*---------------------------------------------------------------
							close
 ---------------------------------------------------------------*/
void CFileInputStream::close()
{
	if (m_if.is_open()) m_if.close();
	m_filename.clear();
}

/*---------------------------------------------------------------
							Destructor
 ---------------------------------------------------------------*/
CFileInputStream::~CFileInputStream() { close(); }
/*---------------------------------------------------------------
							Read
			Reads bytes from the stream into Buffer
 ---------------------------------------------------------------*/
size_t CFileInputStream::Read(void* Buffer, size_t Count)
{
	if (!m_if.is_open()) return 0;

	m_if.read(static_cast<char*>(Buffer), Count);
	return m_if.fail() ? 0 : Count;
}

/*---------------------------------------------------------------
							Write
			Writes a block of bytes to the stream.
 ---------------------------------------------------------------*/
size_t CFileInputStream::Write(
	[[maybe_unused]] const void* Buffer, [[maybe_unused]] size_t Count)
{
	THROW_EXCEPTION("Trying to write to a read file stream.");
}

/*---------------------------------------------------------------
							Seek
	Method for moving to a specified position in the streamed resource.
	 See documentation of CStream::Seek
 ---------------------------------------------------------------*/
uint64_t CFileInputStream::Seek(int64_t Offset, CStream::TSeekOrigin Origin)
{
	if (!m_if.is_open()) return 0;

	ifstream::off_type offset = Offset;
	ifstream::seekdir way;

	switch (Origin)
	{
		case sFromBeginning: way = ios_base::beg; break;
		case sFromCurrent: way = ios_base::cur; break;
		case sFromEnd: way = ios_base::end; break;
		default: THROW_EXCEPTION("Invalid value for 'Origin'");
	}

	m_if.seekg(offset, way);

	return getPosition();
}

/*---------------------------------------------------------------
						getTotalBytesCount
 ---------------------------------------------------------------*/
uint64_t CFileInputStream::getTotalBytesCount() const
{
	if (!fileOpenCorrectly()) return 0;

	auto& f = const_cast<std::ifstream&>(m_if);

	const uint64_t previousPos = f.tellg();
	f.seekg(0, ios_base::end);
	uint64_t fileSize = f.tellg();
	f.seekg(previousPos, ios_base::beg);
	return fileSize;
}

/*---------------------------------------------------------------
						getPosition
 ---------------------------------------------------------------*/
uint64_t CFileInputStream::getPosition() const
{
	auto& f = const_cast<std::ifstream&>(m_if);
	if (m_if.is_open()) return f.tellg();
	else
		return 0;
}

/*---------------------------------------------------------------
						fileOpenCorrectly
 ---------------------------------------------------------------*/
bool CFileInputStream::fileOpenCorrectly() const { return m_if.is_open(); }
/*---------------------------------------------------------------
						readLine
 ---------------------------------------------------------------*/
bool CFileInputStream::readLine(string& str)
{
	str = string();	 // clear() is not defined in VC6
	if (!m_if.is_open()) return false;

	std::getline(m_if, str);
	return !m_if.fail() && !m_if.eof();
}

/*---------------------------------------------------------------
						checkEOF
 ---------------------------------------------------------------*/
bool CFileInputStream::checkEOF()
{
	if (!m_if.is_open()) return true;
	return m_if.eof();
}

void CFileInputStream::clearError()
{
	if (m_if.is_open()) m_if.clear();
}

std::string CFileInputStream::getStreamDescription() const
{
	return mrpt::format(
		"mrpt::io::CFileInputStream for file '%s'", m_filename.c_str());
}
