#include <flecs.h>
#include <stdio.h>

typedef struct Data {
    int x;
} Data;

typedef struct Test {
    Data* ptr;
} Test;

ECS_COMPONENT_DECLARE(Test);

ECS_CTOR(Test, ptr, {
    ptr->ptr = ecs_os_malloc(sizeof(Data));
    ptr->ptr->x = 0; // Default value
    printf("ctor: %d\n", ptr->ptr->x);
})
ECS_DTOR(Test, ptr, {
    printf("dtor: %d\n", ptr->ptr->x);
    ecs_os_free(ptr->ptr);
})
ECS_MOVE(Test, dst, src, {
    printf("move: %d->%d\n", src->ptr->x, dst->ptr->x);
    dst->ptr = src->ptr;
    // Clear the pointer in src since it's been moved tp dst and we
    // don't want to free it when `src` ges destroyed
    src->ptr = NULL;
})

void testRemoved(ecs_iter_t *it) {
    Test *test = ecs_field(it, Test, 1);
    for (int i = 0; i < it->count; i++) {
        printf("on_remove: %d\n", test[i].ptr->x);
    }
}
void testSet(ecs_iter_t *it) {
    Test *test = ecs_field(it, Test, 1);
    for (int i = 0; i < it->count; i++) {
        printf("on_set: %d\n", test[i].ptr->x);
    }
}

void ecs_copy_illegal(void *dst, const void *src, int32_t c, const ecs_type_info_t *ti) {
    ecs_abort(ECS_INVALID_OPERATION, "invalid copy assignment for %s", ti->name);
}

int main(int argc, char *argv[]) {
    ecs_world_t *ecs = ecs_init();
    ECS_COMPONENT_DEFINE(ecs, Test);

    ecs_set_hooks(ecs, Test, {
        .ctor = ecs_ctor(Test),
        .dtor = ecs_dtor(Test),
        .move = ecs_move(Test),
        .copy = ecs_copy_illegal, // Prevent Test from being copied so that they never share the same pointer

        .on_remove = testRemoved,
        .on_set = testSet
    });

    ecs_entity_t entity = ecs_new_id(ecs);
    Test* test = ecs_emplace(ecs, entity, Test); // Emplace the component, this doesn't call the ctor allowing us to initialize the data in the table directly
    Data* ptr = ecs_os_malloc(sizeof(Data));
    ptr->x = 1;
    test->ptr = ptr;
    ecs_modified(ecs, entity, Test); // Tell the ECS that we are done with modifying the component

    // Modify the data in the component without doing any copy/move
    test = ecs_get_mut(ecs, entity, Test);
    test->ptr->x = 2;
    ecs_modified(ecs, entity, Test);

    ecs_remove(ecs, entity, Test); // testRemoved called for {2}

    // Since the component was previously removed this will call the constructor to initialize the
    // default value in the table, and then return that.
    test = ecs_get_mut(ecs, entity, Test);
    test->ptr->x = 3;
    ecs_modified(ecs, entity, Test);

    ecs_defer_begin(ecs);
    ecs_remove(ecs, entity, Test);

    test = ecs_get_mut(ecs, entity, Test);
    test->ptr->x = 4;
    ecs_modified(ecs, entity, Test);

    ecs_defer_end(ecs);

    ecs_fini(ecs); // testRemoved called for {4}
}

