// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

#ifndef FENNEC_METAPROGRAMMING_FLOAT_H
#define FENNEC_METAPROGRAMMING_FLOAT_H

#include <bit>
#include <iosfwd>

inline void float_h()
{
	std::ofstream out("float.h");

	out << "// =====================================================================================================================" << std::endl;
	out << "//  fennec, a free and open source game engine" << std::endl;
	out << "//  Copyright © 2025  Medusa Slockbower" << std::endl;
	out << "//" << std::endl;
	out << "//  This program is free software: you can redistribute it and/or modify" << std::endl;
	out << "//  it under the terms of the GNU General Public License as published by" << std::endl;
	out << "//  the Free Software Foundation, either version 3 of the License, or" << std::endl;
	out << "//  (at your option) any later version." << std::endl;
	out << "//" << std::endl;
	out << "//  This program is distributed in the hope that it will be useful," << std::endl;
	out << "//  but WITHOUT ANY WARRANTY; without even the implied warranty of" << std::endl;
	out << "//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the" << std::endl;
	out << "//  GNU General Public License for more details." << std::endl;
	out << "//" << std::endl;
	out << "//  You should have received a copy of the GNU General Public License" << std::endl;
	out << "//  along with this program.  If not, see <https://www.gnu.org/licenses/>." << std::endl;
	out << "// =====================================================================================================================" << std::endl;

	out << "" << std::endl;

	out << "///" << std::endl;
	out << "/// \\file float.h" << std::endl;
	out << "/// \\brief metaprogramming floating point type info" << std::endl;
	out << "///" << std::endl;
	out << "///" << std::endl;
	out << "/// \\details this file is automatically generated for the current build environment" << std::endl;
	out << "///" << std::endl;
	out << "/// \\copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))" << std::endl;
	out << "///" << std::endl;
	out << "///" << std::endl;

	out << "" << std::endl;

	out << "#ifndef FENNEC_LANG_FLOAT_H" << std::endl;
	out << "#define FENNEC_LANG_FLOAT_H" << std::endl;

	out << "" << std::endl;

	out << "#include <fennec/lang/bits.h>" << std::endl;

	out << "" << std::endl;

	// MAYBE TODO: Generate info without C++ STDLIB to compile natively on platforms without support

	out << "#undef FLT_HAS_INFINITY" << std::endl;
	out << "#undef FLT_HAS_QUIET_NAN" << std::endl;
	out << "#undef FLT_HAS_SIGNALING_NAN" << std::endl;
	out << "#undef FLT_HAS_DENORM" << std::endl;
	out << "#undef FLT_HAS_DENORM_LOSS" << std::endl;
	out << "#undef FLT_ROUNDS" << std::endl;
	out << "#undef FLT_IS_IEC559" << std::endl;
	out << "#undef FLT_MANT_DIG" << std::endl;
	out << "#undef FLT_DIG" << std::endl;
	out << "#undef FLT_DECIMAL_DIG" << std::endl;
	out << "#undef FLT_RADIX" << std::endl;
	out << "#undef FLT_MIN_EXP" << std::endl;
	out << "#undef FLT_MAX_EXP" << std::endl;
	out << "#undef FLT_MIN_10_EXP" << std::endl;
	out << "#undef FLT_MAX_10_EXP" << std::endl;
	out << "#undef FLT_TRAPS" << std::endl;
	out << "#undef FLT_TINYNESS_BEFORE" << std::endl;
	out << "#undef FLT_MIN" << std::endl;
	out << "#undef FLT_MAX" << std::endl;
	out << "#undef FLT_EPSILON" << std::endl;
	out << "#undef FLT_INF" << std::endl;
	out << "#undef FLT_QUIET_NAN" << std::endl;
	out << "#undef FLT_SIGNALING_NAN" << std::endl;
	out << "#undef FLT_DENORM_MIN" << std::endl;
	out << "#undef FLT_ROUND_ERR" << std::endl;

	out << "" << std::endl;

	out << "#define FLT_HAS_INFINITY "      << std::dec << std::numeric_limits<float>::has_infinity      << std::endl;
	out << "#define FLT_HAS_QUIET_NAN "     << std::dec << std::numeric_limits<float>::has_quiet_NaN     << std::endl;
	out << "#define FLT_HAS_SIGNALING_NAN " << std::dec << std::numeric_limits<float>::has_signaling_NaN << std::endl;
	out << "#define FLT_HAS_DENORM "        << std::dec << std::numeric_limits<float>::has_denorm        << std::endl;
	out << "#define FLT_HAS_DENORM_LOSS "   << std::dec << std::numeric_limits<float>::has_denorm_loss   << std::endl;
	out << "#define FLT_ROUNDS "            << std::dec << std::numeric_limits<float>::round_style       << std::endl;
	out << "#define FLT_IS_IEC559 "         << std::dec << std::numeric_limits<float>::is_iec559         << std::endl;
	out << "#define FLT_MANT_DIG "          << std::dec << std::numeric_limits<float>::digits            << std::endl;
	out << "#define FLT_DIG "               << std::dec << std::numeric_limits<float>::digits10          << std::endl;
	out << "#define FLT_DECIMAL_DIG "       << std::dec << std::numeric_limits<float>::max_digits10      << std::endl;
	out << "#define FLT_RADIX "             << std::dec << std::numeric_limits<float>::radix             << std::endl;
	out << "#define FLT_MIN_EXP "           << std::dec << std::numeric_limits<float>::min_exponent      << std::endl;
	out << "#define FLT_MAX_EXP "           << std::dec << std::numeric_limits<float>::max_exponent      << std::endl;
	out << "#define FLT_MIN_10_EXP "        << std::dec << std::numeric_limits<float>::min_exponent10    << std::endl;
	out << "#define FLT_MAX_10_EXP "        << std::dec << std::numeric_limits<float>::max_exponent10    << std::endl;
	out << "#define FLT_TRAPS "             << std::dec << std::numeric_limits<float>::traps             << std::endl;
	out << "#define FLT_TINYNESS_BEFORE "   << std::dec << std::numeric_limits<float>::tinyness_before   << std::endl;

	out << "#define FLT_MIN "           << "fennec::bit_cast<float>(0x" << std::hex << std::bit_cast<int>(std::numeric_limits<float>::min()          ) << ")" << std::endl;
	out << "#define FLT_MAX "           << "fennec::bit_cast<float>(0x" << std::hex << std::bit_cast<int>(std::numeric_limits<float>::max()          ) << ")" << std::endl;
	out << "#define FLT_EPSILON "       << "fennec::bit_cast<float>(0x" << std::hex << std::bit_cast<int>(std::numeric_limits<float>::epsilon()      ) << ")" << std::endl;
	out << "#define FLT_INF "           << "fennec::bit_cast<float>(0x" << std::hex << std::bit_cast<int>(std::numeric_limits<float>::infinity()     ) << ")" << std::endl;
	out << "#define FLT_QUIET_NAN "     << "fennec::bit_cast<float>(0x" << std::hex << std::bit_cast<int>(std::numeric_limits<float>::quiet_NaN()    ) << ")" << std::endl;
	out << "#define FLT_SIGNALING_NAN " << "fennec::bit_cast<float>(0x" << std::hex << std::bit_cast<int>(std::numeric_limits<float>::signaling_NaN()) << ")" << std::endl;
	out << "#define FLT_DENORM_MIN "    << "fennec::bit_cast<float>(0x" << std::hex << std::bit_cast<int>(std::numeric_limits<float>::denorm_min()   ) << ")" << std::endl;
	out << "#define FLT_ROUND_ERR "     << "fennec::bit_cast<float>(0x" << std::hex << std::bit_cast<int>(std::numeric_limits<float>::round_error()  ) << ")" << std::endl;

	out << "" << std::endl;

	out << "#undef DBL_HAS_INFINITY" << std::endl;
	out << "#undef DBL_HAS_QUIET_NAN" << std::endl;
	out << "#undef DBL_HAS_SIGNALING_NAN" << std::endl;
	out << "#undef DBL_HAS_DENORM" << std::endl;
	out << "#undef DBL_HAS_DENORM_LOSS" << std::endl;
	out << "#undef DBL_ROUNDS" << std::endl;
	out << "#undef DBL_IS_IEC559" << std::endl;
	out << "#undef DBL_MANT_DIG" << std::endl;
	out << "#undef DBL_DIG" << std::endl;
	out << "#undef DBL_DECIMAL_DIG" << std::endl;
	out << "#undef DBL_RADIX" << std::endl;
	out << "#undef DBL_MIN_EXP" << std::endl;
	out << "#undef DBL_MAX_EXP" << std::endl;
	out << "#undef DBL_MIN_10_EXP" << std::endl;
	out << "#undef DBL_MAX_10_EXP" << std::endl;
	out << "#undef DBL_TRAPS" << std::endl;
	out << "#undef DBL_TINYNESS_BEFORE" << std::endl;
	out << "#undef DBL_MIN" << std::endl;
	out << "#undef DBL_MAX" << std::endl;
	out << "#undef DBL_EPSILON" << std::endl;
	out << "#undef DBL_INF" << std::endl;
	out << "#undef DBL_QUIET_NAN" << std::endl;
	out << "#undef DBL_SIGNALING_NAN" << std::endl;
	out << "#undef DBL_DENORM_MIN" << std::endl;
	out << "#undef DBL_ROUND_ERR" << std::endl;

	out << "" << std::endl;

	out << "#define DBL_HAS_INFINITY "      << std::dec << std::numeric_limits<double>::has_infinity      << std::endl;
	out << "#define DBL_HAS_QUIET_NAN "     << std::dec << std::numeric_limits<double>::has_quiet_NaN     << std::endl;
	out << "#define DBL_HAS_SIGNALING_NAN " << std::dec << std::numeric_limits<double>::has_signaling_NaN << std::endl;
	out << "#define DBL_HAS_DENORM "        << std::dec << std::numeric_limits<double>::has_denorm        << std::endl;
	out << "#define DBL_HAS_DENORM_LOSS "   << std::dec << std::numeric_limits<double>::has_denorm_loss   << std::endl;
	out << "#define DBL_ROUNDS "            << std::dec << std::numeric_limits<double>::round_style       << std::endl;
	out << "#define DBL_IS_IEC559 "         << std::dec << std::numeric_limits<double>::is_iec559         << std::endl;
	out << "#define DBL_MANT_DIG "          << std::dec << std::numeric_limits<double>::digits            << std::endl;
	out << "#define DBL_DIG "               << std::dec << std::numeric_limits<double>::digits10          << std::endl;
	out << "#define DBL_DECIMAL_DIG "       << std::dec << std::numeric_limits<double>::max_digits10      << std::endl;
	out << "#define DBL_RADIX "             << std::dec << std::numeric_limits<double>::radix             << std::endl;
	out << "#define DBL_MIN_EXP "           << std::dec << std::numeric_limits<double>::min_exponent      << std::endl;
	out << "#define DBL_MAX_EXP "           << std::dec << std::numeric_limits<double>::max_exponent      << std::endl;
	out << "#define DBL_MIN_10_EXP "        << std::dec << std::numeric_limits<double>::min_exponent10    << std::endl;
	out << "#define DBL_MAX_10_EXP "        << std::dec << std::numeric_limits<double>::max_exponent10    << std::endl;
	out << "#define DBL_TRAPS "             << std::dec << std::numeric_limits<double>::traps             << std::endl;
	out << "#define DBL_TINYNESS_BEFORE "   << std::dec << std::numeric_limits<double>::tinyness_before   << std::endl;

	out << "#define DBL_MIN "           << "fennec::bit_cast<double>(0x" << std::hex << std::bit_cast<long long>(std::numeric_limits<double>::min()          ) << "ll)" << std::endl;
	out << "#define DBL_MAX "           << "fennec::bit_cast<double>(0x" << std::hex << std::bit_cast<long long>(std::numeric_limits<double>::max()          ) << "ll)" << std::endl;
	out << "#define DBL_EPSILON "       << "fennec::bit_cast<double>(0x" << std::hex << std::bit_cast<long long>(std::numeric_limits<double>::epsilon()      ) << "ll)" << std::endl;
	out << "#define DBL_INF "           << "fennec::bit_cast<double>(0x" << std::hex << std::bit_cast<long long>(std::numeric_limits<double>::infinity()     ) << "ll)" << std::endl;
	out << "#define DBL_QUIET_NAN "     << "fennec::bit_cast<double>(0x" << std::hex << std::bit_cast<long long>(std::numeric_limits<double>::quiet_NaN()    ) << "ll)" << std::endl;
	out << "#define DBL_SIGNALING_NAN " << "fennec::bit_cast<double>(0x" << std::hex << std::bit_cast<long long>(std::numeric_limits<double>::signaling_NaN()) << "ll)" << std::endl;
	out << "#define DBL_DENORM_MIN "    << "fennec::bit_cast<double>(0x" << std::hex << std::bit_cast<long long>(std::numeric_limits<double>::denorm_min()   ) << "ll)" << std::endl;
	out << "#define DBL_ROUND_ERR "     << "fennec::bit_cast<double>(0x" << std::hex << std::bit_cast<long long>(std::numeric_limits<double>::round_error()  ) << "ll)" << std::endl;

	out << "" << std::endl;

	out << "#endif // FENNEC_LANG_FLOAT_H" << std::endl;

	out.close();

	return;
}

#endif //FLOAT_H
