#ifndef TRANSFORMABLE_H
#define TRANSFORMABLE_H

#include "scene.h"
#include "utils/mirror.h"
#include "utils/typeclass_helpers.h"
#include "raylib.h"
#include "raymath.h"

struct Transformable;

typedef struct ITransformable {
    SceneNode *node;
    Transform (*const get_transform)(void*);
    void (*const set_transform)(void*, Transform);
    Transform (*const get_global_transform)(void*);
    void (*const set_global_transform)(void*, Transform);
    void (*const force_update)(void*);
    unsigned char (*const get_dirty_bit)(void*);
    void (*const clear_dirty_bit)(void*);
    struct Transformable (*const get_parent_transformable)(void*);
} ITransformable;

typedef struct Transformable {
    ITransformable const *tc;
    void *data;
    IMirror const *mirror;
    ISceneNodeEntity const *scene_node_entity;
} Transformable;

extern Transform TransformIdentity();
extern Matrix TransformGetMatrix(Transform self);

extern Vector3 TransformPosition(Transform self, Vector3 local_pos);
extern Vector3 TransformDirection(Transform self, Vector3 local_direction);
extern Vector3 TransformScale(Transform self, Vector3 local_scale);
extern Quaternion TransformRotation(Transform self, Quaternion local_rotation);
extern Transform TransformTransform(Transform self, Transform other);

extern Vector3 InverseTransformPosition(Transform self, Vector3 global_pos);
extern Vector3 InverseTransformDirection(Transform self, Vector3 global_direction);
extern Vector3 InverseTransformScale(Transform self, Vector3 global_scale);
extern Quaternion InverseTransformRotation(Transform self, Quaternion quat);
extern Transform InverseTransformTransform(Transform self, Transform other);

#define MATRIX_RIGHT(self_) ((Vector3){self_.m0, self_.m1, self_.m2})
#define MATRIX_UP(self_) ((Vector3){self_.m4, self_.m5, self_.m6})
#define MATRIX_FORWARD(self_) ((Vector3){self_.m8, self_.m9, self_.m10})

#define VECTOR3_RIGHT   ((Vector3){1.f, 0.f, 0.f})
#define VECTOR3_UP      ((Vector3){0.f, 1.f, 0.f})
#define VECTOR3_FORWARD ((Vector3){0.f, 0.f, 1.f})

//! Defines the variables required to implement Transformable. Put at the start of a struct
#define ExtendsTransformable Transform transform; Transform global_transform; Transformable parent_transformable; unsigned char dirty_bit

//! Define transformable getter-setters for type T
//! Requires member variables: Transform transform; Transform global_transform; Transformable parent_transformable; unsigned char dirty_bit;
//! Macro implements the interface functions itself to ensure consistent behaviour.
#define impl_Transformable_for(T)\
static Transform T##_get_transform_GEN_(T* self) { return self->transform; }\
static void T##_set_transform_GEN_(T* self, Transform value) { self->transform = value; self->dirty_bit = 1; } /*!< sets dirty bit */\
static Transform T##_get_global_transform_GEN_(T* self) { return self->global_transform; }\
static void T##_set_global_transform_GEN_(T* self, Transform value) {\
    if(tc_is_null(self->parent_transformable)) {\
        self->transform = value;\
    } else {\
        if(self->parent_transformable.tc->get_dirty_bit(self->parent_transformable.data))\
            self->parent_transformable.tc->force_update(self->parent_transformable.data);\
        Transform parent_global = self->parent_transformable.tc->get_global_transform(self->parent_transformable.data);\
        self->transform = InverseTransformTransform(parent_global, value); /* Use parent transform to transform the local transform to the new global transform */\
        self->dirty_bit = 1;\
    }\
}\
static void T##_force_update_GEN_(T *self) {\
    if(tc_is_null(self->parent_transformable)) {\
        self->global_transform = self->transform;\
    } else {\
        Transform parent_global = self->parent_transformable.tc->get_global_transform(self->parent_transformable.data);\
        self->global_transform = TransformTransform(parent_global, self->transform);\
    }\
}\
static unsigned char T##_get_dirty_bit_GEN_(T *self) { return self->dirty_bit; }\
static void T##_clear_dirty_bit_GEN_(T *self) { self->dirty_bit = 0; }\
static Transformable T##_get_parent_transformable_GEN_(T *self) { return self->parent_transformable; }\
Transformable T##_as_Transformable(T *x) {\
    static ITransformable const tc = {\
        .get_transform            = (Transform (*const)(void*)) T##_get_transform_GEN_,\
        .set_transform            = (void (*const)(void*,Transform)) T##_set_transform_GEN_,\
        .get_global_transform     = (Transform (*const)(void*)) T##_get_global_transform_GEN_,\
        .set_global_transform     = (void (*const)(void*,Transform)) T##_set_global_transform_GEN_,\
        .force_update             = (void (*const)(void*)) T##_force_update_GEN_,\
        .get_dirty_bit            = (unsigned char (*const)(void*)) T##_get_dirty_bit_GEN_,\
        .clear_dirty_bit          = (void (*const)(void*)) T##_clear_dirty_bit_GEN_,\
        .get_parent_transformable = (Transformable (*const)(void*)) T##_get_parent_transformable_GEN_\
    };\
    return (Transformable){ .tc = &tc, .data = x, .scene_node_entity = T##_as_SceneNodeEntity(x).tc, .mirror = T##_as_Mirror(x).tc };\
}

#endif // !TRANSFORMABLE_H
