/*
 * Copyright (C) 2025 Dominik Drexler
 *
 * 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/>.
 */

#include "tyr/formalism/formalism.hpp"

#include <gtest/gtest.h>

namespace b = tyr::buffer;
namespace f = tyr::formalism;
namespace fp = tyr::formalism::planning;

namespace tyr::tests
{

TEST(TyrTests, TyrFormalismView)
{
    const auto num_objects = 3;
    auto repository = fp::Repository(num_objects);
    auto predicate_builder = Data<f::Predicate<f::FluentTag>>();
    auto object_builder = Data<f::Object>();
    auto variable_builder = Data<f::Variable>();
    auto atom_builder = Data<fp::Atom<f::FluentTag>>();

    // Create a unique predicate
    predicate_builder.name = "predicate";
    predicate_builder.arity = 2;
    canonicalize(predicate_builder);
    auto [predicate, predicate_success] = repository.get_or_create(predicate_builder);

    // Create object and variable
    object_builder.name = "a";
    canonicalize(object_builder);
    auto [object, object_success] = repository.get_or_create(object_builder);

    // Create atom
    atom_builder.terms.clear();
    atom_builder.predicate = predicate.get_index();
    atom_builder.terms.push_back(Data<f::Term>(object.get_index()));
    atom_builder.terms.push_back(Data<f::Term>(f::ParameterIndex(0)));
    canonicalize(atom_builder);
    auto [atom, atom_success] = repository.get_or_create(atom_builder);

    // Recurse through proxy
    auto atom_predicate = atom.get_predicate();
    auto atom_terms = atom.get_terms();

    EXPECT_EQ(atom_predicate.get_name(), "predicate");
    EXPECT_EQ(atom_predicate.get_arity(), 2);
    visit(
        [&](auto&& arg)
        {
            using Alternative = std::decay_t<decltype(arg)>;

            if constexpr (std::is_same_v<Alternative, fp::ObjectView>)
                EXPECT_EQ(arg.get_index(), object.get_index());
            else
                FAIL() << "Expected ObjectView for first term, got a different proxy type";
        },
        atom_terms[0].get_variant());
    visit(
        [&](auto&& arg)
        {
            using Alternative = std::decay_t<decltype(arg)>;

            if constexpr (std::is_same_v<Alternative, f::ParameterIndex>)
                EXPECT_EQ(arg, f::ParameterIndex(0));
            else
                FAIL() << "Expected VariableView for first term, got a different proxy type";
        },
        atom_terms[1].get_variant());
}

}