// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  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_FORMAT_DETAIL_FORMAT_H
#define FENNEC_FORMAT_DETAIL_FORMAT_H

#include <fennec/memory/pointers.h>
#include <fennec/format/formatter.h>
#include <fennec/format/format_arg.h>

namespace fennec::detail
{

// Impl interface for templated polymorphism fuckery
struct _format_argimpl {
	_format_argimpl() {};
	virtual ~_format_argimpl() {};
	virtual string format(const format_arg& fmt) = 0;
	virtual bool   is_integer() = 0;
	virtual int64_t int_value() = 0;
};

// Polymorphic template specialization
template<typename T>
struct _format_arg : _format_argimpl {
	formatter<T> fmtr;
	const T& val;
	_format_arg(const T& arg) : val(arg) {
	}
	virtual ~_format_arg() = default;
	string format(const format_arg& fmt) override {
		return fennec::forward<string>(fmtr(fmt, val));
	}

	bool is_integer() override {
		return is_integral_v<T> or is_convertible_v<T, int64_t>;
	}
	virtual int64_t int_value() override {
		if constexpr(is_integral_v<T>) {
			return val;
		} else if constexpr(is_convertible_v<T, int64_t>) {
			return val;
		} else {
			return -1;
		}
	}
};

// Polymorphic template specialization for x/r/xr value references
template<typename T>
struct _format_arg<T&> : _format_arg<T> {
	_format_arg(const T& arg) : _format_arg<T>(arg) {
	}
};

// Polymorphic template specialization for x/r/xr value references
template<typename T>
struct _format_arg<const T> : _format_arg<T> {
	_format_arg(const T& arg) : _format_arg<T>(arg) {
	}
};

// Polymorphic template specialization for x/r/xr value references
template<typename T>
struct _format_arg<const T&> : _format_arg<T> {
	_format_arg(const T& arg) : _format_arg<T>(arg) {
	}
};

// Containing array for format args
template<size_t N>
struct _format_argarray {
	array<unique_ptr<_format_argimpl>, N> args;
	template<typename...ArgsT>
	_format_argarray(ArgsT&&...args)
		: args { unique_ptr<_format_argimpl>(new _format_arg<ArgsT>(fennec::forward<ArgsT>(args)))... } {
	}

	string format(size_t i, const format_arg& fmt) {
		return args[i]->format(fmt);
	}

	bool is_integer(size_t i) {
		return args[i]->is_integer();
	}

	int64_t int_value(size_t i) {
		return args[i]->int_value();
	}
};

// checks if character is a valid format type
constexpr bool _isfmt_t(char c) {
	switch (c) {
		default: return false;
		case 's': case '?': // strings
		case 'c':           // char
		case 'd':           // decimal
		case 'b': case 'B': // binary
		case 'o':           // octal
		case 'x': case 'X': // hex
		case 'a': case 'A': // float hex
		case 'e': case 'E': // scientific notation
		case 'f': case 'F': // fixed precision
		case 'g': case 'G': // general precision
			return true;
	}
}

// checks if character is a valid format int type
constexpr bool _isfmt_i(char c) {
	switch (c) {
		default: return false;
		case 'd':           // decimal
		case 'b': case 'B': // binary
		case 'o':           // octal
		case 'x': case 'X': // hex
			return true;
	}
}

// checks if character is a valid format float type
constexpr bool _isfmt_f(char c) {
	switch (c) {
		default: return false;
		case 'a': case 'A': // float hex
		case 'e': case 'E': // scientific notation
		case 'f': case 'F': // fixed precision
		case 'g': case 'G': // general precision
			return true;
	}

}

}

#endif // FENNEC_FORMAT_DETAIL_FORMAT_H
