#include "Formula.hpp"
#include <cctype>
#include <cmath>
#include <OgreLogManager.h>

Formula::Formula(const Ogre::String &expr)
	: m_expression(expr)
{
	m_valid = !expr.empty();
}

double Formula::evaluate(const std::unordered_map<std::string, double> &vars) const
{
	if (!m_valid || m_expression.empty())
		return 0.0;

	Parser p;
	p.s = m_expression.c_str();
	p.vars = &vars;
	try {
		return p.parseExpression();
	} catch (...) {
		return 0.0;
	}
}

double Formula::evaluate(int level) const
{
	std::unordered_map<std::string, double> vars;
	vars["level"] = static_cast<double>(level);
	return evaluate(vars);
}

double Formula::evaluate(int level, double current) const
{
	std::unordered_map<std::string, double> vars;
	vars["level"] = static_cast<double>(level);
	vars["current"] = current;
	return evaluate(vars);
}

// ---------------------------------------------------------------------------
// Parser implementation
// ---------------------------------------------------------------------------

void Formula::Parser::skipWhitespace()
{
	while (*s == ' ' || *s == '\t')
		s++;
}

bool Formula::Parser::consume(char c)
{
	skipWhitespace();
	if (*s == c) {
		s++;
		return true;
	}
	return false;
}

double Formula::Parser::parseExpression()
{
	double value = parseTerm();
	while (true) {
		skipWhitespace();
		if (consume('+')) {
			value += parseTerm();
		} else if (consume('-')) {
			value -= parseTerm();
		} else {
			break;
		}
	}
	return value;
}

double Formula::Parser::parseTerm()
{
	double value = parsePower();
	while (true) {
		skipWhitespace();
		if (consume('*')) {
			value *= parsePower();
		} else if (consume('/')) {
			double rhs = parsePower();
			if (rhs != 0.0)
				value /= rhs;
			else
				value = 0.0;
		} else {
			break;
		}
	}
	return value;
}

double Formula::Parser::parsePower()
{
	double value = parseUnary();
	while (true) {
		skipWhitespace();
		if (consume('^')) {
			value = std::pow(value, parseUnary());
		} else {
			break;
		}
	}
	return value;
}

double Formula::Parser::parseUnary()
{
	skipWhitespace();
	if (consume('+'))
		return parseUnary();
	if (consume('-'))
		return -parseUnary();
	return parsePrimary();
}

double Formula::Parser::parsePrimary()
{
	skipWhitespace();

	// Number
	if (std::isdigit(*s) || (*s == '.' && std::isdigit(*(s + 1)))) {
		char *end = nullptr;
		double val = std::strtod(s, &end);
		s = end;
		return val;
	}

	// Parenthesized expression
	if (consume('(')) {
		double val = parseExpression();
		skipWhitespace();
		if (!consume(')'))
			return 0.0;
		return val;
	}

	// Identifier (variable or function)
	if (std::isalpha(*s) || *s == '_') {
		const char *start = s;
		while (std::isalnum(*s) || *s == '_')
			s++;
		std::string name(start, static_cast<size_t>(s - start));

		skipWhitespace();
		if (consume('(')) {
			// Function call
			std::vector<double> args;
			if (!consume(')')) {
				args.push_back(parseExpression());
				while (consume(',')) {
					args.push_back(parseExpression());
				}
				skipWhitespace();
				if (!consume(')'))
					return 0.0;
			}

			if (name == "floor" && args.size() >= 1)
				return std::floor(args[0]);
			if (name == "ceil" && args.size() >= 1)
				return std::ceil(args[0]);
			if (name == "min" && args.size() >= 2)
				return args[0] < args[1] ? args[0] : args[1];
			if (name == "max" && args.size() >= 2)
				return args[0] > args[1] ? args[0] : args[1];
			if (name == "clamp" && args.size() >= 3) {
				if (args[0] < args[1])
					return args[1];
				if (args[0] > args[2])
					return args[2];
				return args[0];
			}
			return 0.0;
		}

		// Variable lookup
		if (vars) {
			auto it = vars->find(name);
			if (it != vars->end())
				return it->second;
		}
		return 0.0;
	}

	return 0.0;
}
