BareGit
#include "macro_engine.h"
#include "parser.h"
#include <sstream>
#include <stdexcept>
#include <iostream>
#include <variant>

namespace macrodown
{

namespace
{

// Helper to split a string by delimiter
std::vector<std::string> split(const std::string& s, char delimiter)
{
    std::vector<std::string> tokens;
    std::string token;
    std::istringstream tokenStream(s);
    while(std::getline(tokenStream, token, delimiter))
    {
        // Trim whitespace
        size_t first = token.find_first_not_of(" \t");
        if(first == std::string::npos) continue;
        size_t last = token.find_last_not_of(" \t");
        tokens.push_back(token.substr(first, (last - first + 1)));
    }
    return tokens;
}

// Helper to replace all occurrences of a substring
std::string replace_all(std::string str, const std::string& from, const std::string& to)
{
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos)
    {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length();
    }
    return str;
}

} // namespace

Evaluator::Evaluator()
{
    // Register intrinsic %def macro
    defineIntrinsic("def", [this](const std::vector<std::string>& args) -> std::string
    {
        if(args.size() < 3)
        {
            return ""; 
        }
        
        std::string name = args[0];
        std::string arg_list_str = args[1];
        std::string body = args[2];
        
        std::vector<std::string> arg_names = split(arg_list_str, ',');
        
        this->define(name, arg_names, body);
        return ""; 
    });
}

void Evaluator::define(const std::string& name, const std::vector<std::string>& args, const std::string& body)
{
    macros_[name] = MacroDefinition(name, args, body);
}

void Evaluator::defineIntrinsic(const std::string& name, MacroCallback callback)
{
    macros_[name] = MacroDefinition(name, callback);
}

std::string Evaluator::evaluate(const Node& node)
{
    return std::visit([this](auto&& arg) -> std::string
    {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, Text>)
        {
            return arg.content;
        }
        else if constexpr (std::is_same_v<T, Macro>)
        {
            return this->evaluateMacro(arg);
        }
        else if constexpr (std::is_same_v<T, Group>)
        {
            std::string result;
            for(const auto& child : arg.children)
            {
                result += this->evaluate(*child);
            }
            return result;
        }
        return "";
    }, node.data);
}

std::string Evaluator::evaluateMacro(const Macro& macro)
{
    auto it = macros_.find(macro.name);
    if(it == macros_.end())
    {
        std::string result = "%" + macro.name;
        for(const auto& arg : macro.arguments)
        {
            result += "{" + evaluate(*arg) + "}"; 
        }
        return result;
    }

    const MacroDefinition& def = it->second;

    std::vector<std::string> evaluated_args;
    for(const auto& arg : macro.arguments)
    {
        evaluated_args.push_back(evaluate(*arg));
    }

    if(def.is_intrinsic)
    {
        return def.callback(evaluated_args);
    }
    else
    {
        std::string body = def.body;
        
        for(size_t i = 0; i < def.arg_names.size(); ++i)
        {
            std::string placeholder = "%" + def.arg_names[i];
            std::string value = (i < evaluated_args.size()) ? evaluated_args[i] : "";
            body = replace_all(body, placeholder, value);
        }
        
        auto nodes = Parser::parse(body);
        
        std::string result;
        for(const auto& n : nodes)
        {
            result += evaluate(*n);
        }
        return result;
    }
}

} // namespace macrodown