//! \~\file piunits_base.h
//! \~\ingroup Units
//! \~\brief
//! \~english Shared unit conversion infrastructure
//! \~russian Общая инфраструктура преобразования единиц измерения
/*
    PIP - Platform Independent Primitives
    Unit conversions
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//! \~english Units base classes
//! \~russian Базовые классы единиц измерения
//! \defgroup Units Units
//! \~\brief
//! \~english Unit conversions
//! \~russian Преобразование единиц измерения
//!
//! \~\details
//! \~english \section cmake_module_Units Building with CMake
//! \~russian \section cmake_module_Units Сборка с использованием CMake
//!
//! \~\code
//! find_package(PIP REQUIRED)
//! target_link_libraries([target] PIP)
//! \endcode
//!
//! \~english \par Common
//! \~russian \par Общее
//!
//! \~english
//! These files provides unit conversion framework.
//!
//! \~russian
//! Эти файлы обеспечивают фреймворк преобразования единиц.
//!
//! \~\authors
//! \~english
//! Ivan Pelipenko peri4ko@yandex.ru;
//! \~russian
//! Иван Пелипенко peri4ko@yandex.ru;
//!

#ifndef PIUNITS_BASE_H
#define PIUNITS_BASE_H

#include "pitranslator.h"


//! \~\ingroup Units
//! \~\brief
//! \~english Starts declaration of a unit family class in namespace %PIUnits::Class.
//! \~russian Начинает объявление класса семейства единиц в пространстве имен %PIUnits::Class.
#define DECLARE_UNIT_CLASS_BEGIN(Name, StartIndex)                              \
	namespace PIUnits {                                                         \
	namespace Class {                                                           \
	class PIP_EXPORT Name                                                       \
		: public Internal::ClassBase                                            \
		, public Internal::Registrator<Name> {                                  \
	private:                                                                    \
		friend class Internal::Registrator<Name>;                               \
		constexpr static int typeStart = StartIndex;                            \
		PIString name(int type) const override;                                 \
		PIString unit(int type) const override;                                 \
		PIString valueToString(double v, char format, int prec) const override; \
		double convert(double v, int from, int to) const override;              \
		bool supportPrefixes(int type) const override;                          \
		bool supportPrefixesNon3(int type) const override;                      \
		bool supportPrefixesGreater(int type) const override;                   \
		bool supportPrefixesSmaller(int type) const override;                   \
																				\
	public:                                                                     \
		PIString className() const override { return piTr(#Name, "PIUnits"); }  \
		uint classID() const override {                                         \
			static uint ret = PIStringAscii(#Name).hash();                      \
			return ret;                                                         \
		}


//! \~\ingroup Units
//! \~\brief
//! \~english Completes unit family class declaration and registers it globally.
//! \~russian Завершает объявление класса семейства единиц и регистрирует его глобально.
#define DECLARE_UNIT_CLASS_END(Name)          \
	}                                         \
	;                                         \
	}                                         \
	}                                         \
	STATIC_INITIALIZER_BEGIN                  \
		PIUnits::Class::Name::registerSelf(); \
	STATIC_INITIALIZER_END


//! \~\ingroup Units
//! \~\brief
//! \~english Namespace containing unit metadata and conversion helpers.
//! \~russian Пространство имен с метаданными и вспомогательными средствами преобразования единиц.
namespace PIUnits {

//! \~\ingroup Units
//! \~english Returns unit family name for type identifier "type".
//! \~russian Возвращает имя семейства единиц для идентификатора типа "type".
PIP_EXPORT PIString className(int type);

//! \~\ingroup Units
//! \~english Returns localized unit name for type identifier "type".
//! \~russian Возвращает локализованное имя единицы для идентификатора типа "type".
PIP_EXPORT PIString name(int type);

//! \~\ingroup Units
//! \~english Returns localized unit symbol for type identifier "type".
//! \~russian Возвращает локализованное обозначение единицы для идентификатора типа "type".
PIP_EXPORT PIString unit(int type);

//! \~\ingroup Units
//! \~\brief
//! \~english Namespace containing unit family descriptors.
//! \~russian Пространство имен с описателями семейств единиц.
namespace Class {

//! \~\ingroup Units
//! \~english Common invalid unit type value.
//! \~russian Общее значение недопустимого типа единицы.
enum {
	Invalid = -1 /** \~english Invalid unit type \~russian Недопустимый тип единицы */
};

//! \~\ingroup Units
//! \~\brief
//! \~english Internal registry and base interfaces for unit families.
//! \~russian Внутренний реестр и базовые интерфейсы для семейств единиц.
class PIP_EXPORT Internal {
public:
	//! \~\ingroup Units
	//! \~\brief
	//! \~english Base interface implemented by every unit family descriptor.
	//! \~russian Базовый интерфейс, реализуемый каждым описателем семейства единиц.
	class PIP_EXPORT ClassBase {
	public:
		//! \~english Returns stable identifier of the unit family.
		//! \~russian Возвращает стабильный идентификатор семейства единиц.
		virtual uint classID() const                                                    = 0;

		//! \~english Returns localized family name.
		//! \~russian Возвращает локализованное имя семейства.
		virtual PIString className() const                                              = 0;

		//! \~english Returns localized name of unit type "type".
		//! \~russian Возвращает локализованное имя типа единицы "type".
		virtual PIString name(int type) const                                           = 0;

		//! \~english Returns localized symbol of unit type "type".
		//! \~russian Возвращает локализованное обозначение типа единицы "type".
		virtual PIString unit(int type) const                                           = 0;

		//! \~english Formats numeric value "v" without unit symbol.
		//! \~russian Форматирует числовое значение "v" без обозначения единицы.
		virtual PIString valueToString(double v, char format = 'g', int prec = 5) const = 0;

		//! \~english Converts numeric value "v" from unit "from" to unit "to".
		//! \~russian Преобразует числовое значение "v" из единицы "from" в единицу "to".
		virtual double convert(double v, int from, int to) const                        = 0;

		//! \~english Returns whether unit type "type" supports automatic SI prefixes.
		//! \~russian Возвращает, поддерживает ли тип единицы "type" автоматические SI-префиксы.
		virtual bool supportPrefixes(int type) const { return true; }

		//! \~english Returns whether non-multiple-of-three prefixes are allowed for unit type "type".
		//! \~russian Возвращает, разрешены ли немножественные трем префиксы для типа единицы "type".
		virtual bool supportPrefixesNon3(int type) const { return false; }

		//! \~english Returns whether prefixes larger than the base unit are allowed for type "type".
		//! \~russian Возвращает, разрешены ли префиксы больше базовой единицы для типа "type".
		virtual bool supportPrefixesGreater(int type) const { return true; }

		//! \~english Returns whether prefixes smaller than the base unit are allowed for type "type".
		//! \~russian Возвращает, разрешены ли префиксы меньше базовой единицы для типа "type".
		virtual bool supportPrefixesSmaller(int type) const { return true; }

		//! \~english Returns all registered unit type identifiers of this family.
		//! \~russian Возвращает все зарегистрированные идентификаторы типов этой семьи единиц.
		const PIVector<int> & allTypes() const { return types; }

	protected:
		PIVector<int> types;
	};

	//! \~\ingroup Units
	//! \~\brief
	//! \~english Helper that registers every type declared by unit family "P".
	//! \~russian Вспомогательный класс, регистрирующий все типы, объявленные семейством единиц "P".
	template<typename P>
	class Registrator {
	public:
		//! \~english Registers unit family "P" in shared lookup tables.
		//! \~russian Регистрирует семейство единиц "P" в общих таблицах поиска.
		static void registerSelf() {
			auto * uc = new P();
			for (int t = P::typeStart; t < P::_LastType; ++t) {
				uc->types << t;
				Internal::typeClasses[t] = uc;
			}
			if (!Internal::allTypeClasses.contains(uc)) Internal::allTypeClasses << uc;
		}
	};

	//! \~english Maps unit type identifiers to owning family descriptors.
	//! \~russian Сопоставляет идентификаторы типов единиц с описателями их семейств.
	static PIMap<int, ClassBase *> typeClasses;

	//! \~english Stores all registered unit family descriptors.
	//! \~russian Хранит все зарегистрированные описатели семейств единиц.
	static PIVector<ClassBase *> allTypeClasses;

	//! \~english Fallback string for unknown unit types.
	//! \~russian Строка-заглушка для неизвестных типов единиц.
	static const PIString unknown;
};


} // namespace Class

//! \~\ingroup Units
//! \~english Returns all registered unit family descriptors.
//! \~russian Возвращает все зарегистрированные описатели семейств единиц.
PIP_EXPORT PIVector<Class::Internal::ClassBase *> allClasses();

} // namespace PIUnits


#endif
