#include "state.hpp"
#include "character_actor.hpp"
#include "planner.hpp"
#include "utils/godot_macros.h"

namespace godot::goap {
State::~State() {
    if(unlikely(this->type == STATE_ANIMATE))
        delete this->animate;
}

State State::new_move_to(Node3D *node) {
    return {
        .type = State::Type::STATE_MOVE_TO,
        .move_to = node
    };
}

State State::new_animate(StringName animation) {
    return {
        .type = State::Type::STATE_ANIMATE,
        .animate = new StringName(animation)
    };
}

State State::new_activate(Node *node) {
    return {
        .type = State::Type::STATE_ACTIVATE,
        .activate = node
    };
}

State State::new_invalid() {
    return { .type = State::Type::STATE_TYPE_MAX };
}

bool State::is_complete(CharacterActor *context) const {
    switch(this->type) {
    default:
        return true;
    case STATE_MOVE_TO:
        return context->get_global_position().is_equal_approx(this->move_to->get_global_position());
    case STATE_ANIMATE:
        return false; // TODO: replace this with checks for animation completion
    case STATE_ACTIVATE:
        return false; // TODO: replace this with checks for object activation
    }
}

void StateArgs::_bind_methods() {
#define CLASSNAME StateArgs
    GDPROPERTY(argument_property, Variant::STRING_NAME);
}

State StateArgs::construct(Planner *context) const {
    return { .type = State::STATE_TYPE_MAX };
}

void StateArgs::set_argument_property(StringName var) { this->argument_property = var; }
StringName StateArgs::get_argument_property() const { return this->argument_property; }

void MoveStateArgs::_bind_methods() {}

State MoveStateArgs::construct(Planner *context) const {
    Node3D *node = Object::cast_to<Node3D>(context->get_world_property(this->argument_property));
    return State::new_move_to(node);
}

void AnimateStateArgs::_bind_methods() {}

State AnimateStateArgs::construct(Planner *context) const {
    return State::new_animate(context->get_world_property(this->argument_property));
}

void ActivateStateArgs::_bind_methods() {}

State ActivateStateArgs::construct(Planner *context) const {
    Node *node = Object::cast_to<Node>(context->get_world_property(this->argument_property));
    return State::new_activate(node);
}
}
