/*******************************************************************************
***  This file is part of Dux Rei Nummariae.                                 ***
***                                                                          ***
***  Copyright (C) 2017, 2018, 2019, 2020, 2021                              ***
***  John Schneiderman <Licensing _AT_ Schneiderman _DOT_ me>                ***
***                                                                          ***
***  This program is free software: you can redistribute it and/or modify it ***
***  under the terms of the GNU Affero General Public License as published   ***
***  by the Free Software Foundation, either version 3 of the License, or    ***
***  (at your option) any later version.                                     ***
***                                                                          ***
***  This program is distributed in the hope that it will be useful, but     ***
***  WITHOUT ANY WARRANTY; without even the implied warranty of              ***
***  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    ***
***  See the GNU Affero General Public License for more details.             ***
***                                                                          ***
***  You should have received a copy of the GNU Affero General Public License***
***  along with this program. If not, see <http://www.gnu.org/licenses/>.    ***
*******************************************************************************/
#ifndef DRN_CONVERSION_CONVERSIONMAP_H_
#define DRN_CONVERSION_CONVERSIONMAP_H_

#include <pecunia/Codes.h>

#include <chrono>
#include <map>
#include <set>

#include "conversion_export.h"


namespace drn
{
namespace conversion
{

/**
 * @brief Provides the multiplication ratio to convert to a given currency code.
 */
struct DRN_CONVERSION_EXPORT ConversionRatio
{
	/**
	 * @brief Full initilaisation constructor.
	 *
	 * @param convertTo The currency code that the ratio converts to.
	 * @param ratio The currency ratio that performs the conversion.
	 */
	ConversionRatio(const pecunia::currency::Iso4217Codes& convertTo, const double ratio = 0.0);
	/**
	 * @brief Determines if two conversion ratio objects are equivalent by their currency code.
	 *
	 * @param other The other object to compare against the current object.
	 *
	 * @return Gives true when both conversion to currency codes are the same, false else-wise.
	 */
	bool operator==(const ConversionRatio& other) const;
	/**
	 * @brief Determines if two conversion ratio objects are not equivalent by their currency code.
	 *
	 * @param other The other object to compare against the current object.
	 *
	 * @return Gives true when both conversion to currency codes are not the same, false else-wise.
	 */
	bool operator!=(const ConversionRatio& other) const;
	/**
	 * @brief Determines if the current object is less than the supplied conversion ratio object.
	 *
	 * @param other The other object to compare against the current object.
	 *
	 * @return Gives true when the currency code of the current object is less than the supplied.
	 */
	bool operator<(const ConversionRatio& other) const;

	/// @brief The currency code the ratio converts to.
	pecunia::currency::Iso4217Codes convertTo_;
	/// @brief The ratio that converts to the currency.
	double ratio_;
};
/**
 * @brief A container for mapping from one currency code to the conversion ratio of another.
 */
class DRN_CONVERSION_EXPORT ConversionMap :
	std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>>
{
	std::chrono::system_clock::time_point lastFetched_;

public:
	using std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>>::at;
	using std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>>::begin;
	using std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>>::cbegin;
	using std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>>::cend;
	using std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>>::clear;
	using std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>>::end;
	using std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>>::size;

	/**
	 * @brief Initialisation constructor populated with the unknown currency code conversion rate.
	 */
	ConversionMap();
	/**
	 * @brief Initialisation constructor populated with the supplied conversion rates.
	 *
	 * @param lastFetched The point at which the conversions were last fetched.
	 * @param ratios The mapping of rations from one currency to another.
	 */
	ConversionMap(
		std::chrono::system_clock::time_point lastFetched,
		std::map<pecunia::currency::Iso4217Codes, std::set<ConversionRatio>> ratios
	);
	/**
	 * @brief Member access for when the conversion were last set.
	 *
	 * @return The time-point the fetch occurred.
	 */
	const std::chrono::system_clock::time_point& lastFetched() const;
	/**
	 * @brief Determines if any currency conversion ratio exist for a given currency.
	 *
	 * @param from The currency code converting from the given currency.
	 *
	 * @return Gives true when any currency conversion ratio exits, false else-wise.
	 */
	bool hasFrom(const pecunia::currency::Iso4217Codes& from) const;
	/**
	 * @brief Determines if the currency conversion ratio exists.
	 *
	 * @param from The currency code converting from the given currency.
	 * @param to The currency code to convert to the given currency.
	 *
	 * @return Gives true when the currency conversion ratio exits to go from a supplied code to
	 * another, false else-wise.
	 */
	bool hasConversion(
		const pecunia::currency::Iso4217Codes& from,
		const pecunia::currency::Iso4217Codes& to
	) const;
	/**
	 * @brief Inserts or updates a conversion ratio entry for a given currency to another.
	 *
	 * @exception Error When the supplied from and to currency codes are the same, but the
	 * conversion ratio is not an identity.
	 *
	 * @param from The currency code the ratio is converting from.
	 * @param to The currency code the ratio is converting to.
	 * @param ratio The ratio, when multiplied, converts to the given currency.
	 */
	void setConversion(
		const pecunia::currency::Iso4217Codes& from,
		const pecunia::currency::Iso4217Codes& to,
		const double ratio
	);
};

}}

#endif
