/* +------------------------------------------------------------------------+
   |                     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          |
   +------------------------------------------------------------------------+ */
#pragma once

#include <mrpt/serialization/CSchemeArchiveBase.h>
#include <mrpt/serialization/CSerializable.h>

#include <memory>
#include <optional>
#include <sstream>
#include <string>

namespace mrpt::serialization
{
/** Base template class for schema-capable "archives", e.g. JSON, YAML,
 * from which to (de)serialize objects.
 *
 * See \ref mrpt_serialization_grp for examples of use.
 * \ingroup mrpt_serialization_grp
 * \note Original version by https://github.com/rachit173 for GSoC 2018.
 */
template <typename SCHEME_CAPABLE>
class CSchemeArchive : public mrpt::serialization::CSchemeArchiveBase_impl
{
	std::optional<SCHEME_CAPABLE> m_own_val;

   public:
	/** Ctor that creates an own SCHEME_CAPABLE object. */
	CSchemeArchive() : m_own_val{SCHEME_CAPABLE()}, m_val{m_own_val.value()} {}
	/** Ctor that uses user-providen SCHEME_CAPABLE object. */
	CSchemeArchive(SCHEME_CAPABLE& val) : m_val(val) {}
	// Virtual assignment operators
	mrpt::serialization::CSchemeArchiveBase& operator=(
		const int32_t val) override
	{
		m_val = val;
		return *m_parent;
	}
	mrpt::serialization::CSchemeArchiveBase& operator=(
		const uint32_t val) override
	{
		m_val = val;
		return *m_parent;
	}
	mrpt::serialization::CSchemeArchiveBase& operator=(
		const int64_t val) override
	{
		m_val = typename SCHEME_CAPABLE::Int64(val);
		return *m_parent;
	}
	mrpt::serialization::CSchemeArchiveBase& operator=(
		const uint64_t val) override
	{
		m_val = typename SCHEME_CAPABLE::UInt64(val);
		return *m_parent;
	}
	mrpt::serialization::CSchemeArchiveBase& operator=(const float val) override
	{
		m_val = val;
		return *m_parent;
	}
	mrpt::serialization::CSchemeArchiveBase& operator=(
		const double val) override
	{
		m_val = val;
		return *m_parent;
	}
	mrpt::serialization::CSchemeArchiveBase& operator=(
		const std::string val) override
	{
		m_val = val;
		return *m_parent;
	}
	mrpt::serialization::CSchemeArchiveBase& operator=(const bool val) override
	{
		m_val = val;
		return *m_parent;
	}

	explicit operator int32_t() const override { return m_val.asInt(); }
	explicit operator uint32_t() const override { return m_val.asUInt(); }
	explicit operator int64_t() const override { return m_val.asInt64(); }
	explicit operator uint64_t() const override { return m_val.asUInt64(); }
	explicit operator float() const override { return m_val.asFloat(); }
	explicit operator double() const override { return m_val.asDouble(); }
	explicit operator bool() const override { return m_val.asBool(); }
	explicit operator std::string() const override { return m_val.asString(); }
	mrpt::serialization::CSchemeArchiveBase& operator=(
		const mrpt::serialization::CSerializable& obj) override
	{
		ReadObject(*m_parent, obj);
		return *m_parent;
	}
	void readTo(mrpt::serialization::CSerializable& obj) override
	{
		WriteObject(*m_parent, obj);
		return;
	}

	mrpt::serialization::CSchemeArchiveBase operator[](size_t idx) override
	{
		return mrpt::serialization::CSchemeArchiveBase(
			std::make_unique<CSchemeArchive<SCHEME_CAPABLE>>(m_val[(int)idx]));
	}
	mrpt::serialization::CSchemeArchiveBase operator[](std::string str) override
	{
		return mrpt::serialization::CSchemeArchiveBase(
			std::make_unique<CSchemeArchive<SCHEME_CAPABLE>>(
				m_val[std::string(str)]));
	}

	std::ostream& writeToStream(std::ostream& out) const override
	{
		out << m_val;
		return out;
	}
	std::istream& readFromStream(std::istream& in) override
	{
		in >> m_val;
		return in;
	}

   private:
	SCHEME_CAPABLE& m_val;
};

/** Returns an archive for reading/writing in JSON format.
 * This feature requires compiling MRPT with jsoncpp support.
 * See \ref mrpt_serialization_grp for examples of use.
 * \ingroup mrpt_serialization_grp */
CSchemeArchiveBase archiveJSON();

}  // namespace mrpt::serialization
