/*
    PIP - Platform Independent Primitives
    Unit prefix
    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/>.
*/

#include "piunits_prefix.h"

#include "piliterals_string.h"
#include "pimathbase.h"
#include "pitranslator.h"
#include "piunits_base.h"

// quetta 	Q 	10^30 	1000000000000000000000000000000
// ronna 	R 	10^27 	1000000000000000000000000000
// yotta 	Y 	10^24 	1000000000000000000000000
// zetta 	Z 	10^21 	1000000000000000000000
// exa		E 	10^18 	1000000000000000000
// peta 	P 	10^15 	1000000000000000
// tera 	T 	10^12 	1000000000000
// giga 	G 	10^9 	1000000000
// mega 	M 	10^6 	1000000
// kilo 	k 	10^3 	1000
// hecto 	h 	10^2 	100
// deca 	da 	10^1 	10
// — 	— 	100 	1 	—
// deci 	d 	10^−1 	0.1
// centi 	c 	10^−2 	0.01
// milli 	m 	10^−3 	0.001
// micro 	μ 	10^−6 	0.000001
// nano 	n 	10^−9 	0.000000001
// pico 	p 	10^−12 	0.000000000001
// femto 	f 	10^−15 	0.000000000000001
// atto 	a 	10^−18 	0.000000000000000001
// zepto 	z 	10^−21 	0.000000000000000000001
// yocto 	y 	10^−24 	0.000000000000000000000001
// ronto 	r 	10^−27 	0.000000000000000000000000001


PIString PIUnits::Prefix::name(int prefix) {
	return instance().getPrefix(prefix).name;
}


PIString PIUnits::Prefix::prefix(int prefix) {
	return instance().getPrefix(prefix).prefix;
}


PIString PIUnits::Prefix::valueToString(double v, void * type_class, int type, char format, int prec) {
	auto * tc = reinterpret_cast<PIUnits::Class::Internal::ClassBase *>(type_class);
	auto p =
		instance().getPrefixForValue(v, tc->supportPrefixesNon3(type), tc->supportPrefixesGreater(type), tc->supportPrefixesSmaller(type));
	return PIString::fromNumber(v / p.divider, format, prec) + " "_a + p.prefix;
}


double PIUnits::Prefix::multiplier(int prefix) {
	return instance().getPrefix(prefix).divider;
}


PIUnits::Prefix::Prefix() {
	def_prefix = {"", "", 0, 1., false};
	// clang-format off
	prefixes   = {

        {Deca,   {"deca"_tr  ("PIUnits"), "da"_tr("PIUnits") , 1  , pow10(1.  ), true }},
        {Hecto,  {"hecto"_tr ("PIUnits"), "h"_tr ("PIUnits") , 2  , pow10(2.  ), true }},
        {Kilo,   {"kilo"_tr  ("PIUnits"), "k"_tr ("PIUnits") , 3  , pow10(3.  ), false}},
        {Mega,   {"mega"_tr  ("PIUnits"), "M"_tr ("PIUnits") , 6  , pow10(6.  ), false}},
        {Giga,   {"giga"_tr  ("PIUnits"), "G"_tr ("PIUnits") , 9  , pow10(9.  ), false}},
        {Tera,   {"tera"_tr  ("PIUnits"), "T"_tr ("PIUnits") , 12 , pow10(12. ), false}},
        {Peta,   {"peta"_tr  ("PIUnits"), "P"_tr ("PIUnits") , 15 , pow10(15. ), false}},
        {Exa,    {"exa"_tr   ("PIUnits"), "E"_tr ("PIUnits") , 18 , pow10(18. ), false}},
        {Zetta,  {"zetta"_tr ("PIUnits"), "Z"_tr ("PIUnits") , 21 , pow10(21. ), false}},
        {Yotta,  {"yotta"_tr ("PIUnits"), "Y"_tr ("PIUnits") , 24 , pow10(24. ), false}},
        {Ronna,  {"ronna"_tr ("PIUnits"), "R"_tr ("PIUnits") , 27 , pow10(27. ), false}},
        {Quetta, {"quetta"_tr("PIUnits"), "Q"_tr ("PIUnits") , 30 , pow10(30. ), false}},

        {Deci,   {"deci"_tr  ("PIUnits"), "d"_tr ("PIUnits") , -1 , pow10(-1. ), true }},
        {Centi,  {"centi"_tr ("PIUnits"), "c"_tr ("PIUnits") , -2 , pow10(-2. ), true }},
        {Milli,  {"milli"_tr ("PIUnits"), "m"_tr ("PIUnits") , -3 , pow10(-3. ), false}},
        {Micro,  {"micro"_tr ("PIUnits"), "u"_tr ("PIUnits") , -6 , pow10(-6. ), false}},
        {Nano,   {"nano"_tr  ("PIUnits"), "n"_tr ("PIUnits") , -9 , pow10(-9. ), false}},
        {Pico,   {"pico"_tr  ("PIUnits"), "p"_tr ("PIUnits") , -12, pow10(-12.), false}},
        {Femto,  {"femto"_tr ("PIUnits"), "f"_tr ("PIUnits") , -15, pow10(-15.), false}},
        {Atto,   {"atto"_tr  ("PIUnits"), "a"_tr ("PIUnits") , -18, pow10(-18.), false}},
        {Zepto,  {"zepto"_tr ("PIUnits"), "z"_tr ("PIUnits") , -21, pow10(-21.), false}},
        {Yocto,  {"yocto"_tr ("PIUnits"), "y"_tr ("PIUnits") , -24, pow10(-24.), false}},
        {Ronto,  {"ronto"_tr ("PIUnits"), "r"_tr ("PIUnits") , -27, pow10(-27.), false}},
    };
    // clang-format on
    auto it    = prefixes.makeIterator();
    while (it.next()) {
        prefixes_by_pow[it.value().pow] = &it.value();
    }
    prefixes_by_pow[0] = &def_prefix;
}


const PIUnits::Prefix::P PIUnits::Prefix::getPrefixForValue(double v, bool use_non3, bool use_greater, bool use_smaller) const {
	auto it       = prefixes_by_pow.makeIterator();
	const P * ret = &def_prefix;
	while (it.next()) {
		if (it.value()->pow < 0 && !use_smaller) continue;
		if (it.value()->pow > 0 && !use_greater) continue;
		if (it.value()->non3 && !use_non3) continue;
		if (v < it.value()->divider) return *ret;
		ret = it.value();
	}
	return def_prefix;
}


const PIUnits::Prefix::P PIUnits::Prefix::getPrefix(int p) const {
	return prefixes.value(p, def_prefix);
}


PIUnits::Prefix & PIUnits::Prefix::instance() {
	static Prefix ret;
	return ret;
}
