#ifndef CORE_VECTOR_H
#define CORE_VECTOR_H

#include "core/Types.h"

#define VECTOR_OP2(name) name *r, const name *a
#define VECTOR_OP3(name) VECTOR_OP2(name), const name* b

#define VECTOR_TYPE(type, Type) \
    typedef union {             \
        type data[2];           \
        struct {                \
            type x, y;          \
        };                      \
    } Type##2;                  \
    typedef union {             \
        type data[3];           \
        struct {                \
            type x, y, z;       \
        };                      \
        Type##2 xy;             \
    } Type##3;                  \
    typedef union {             \
        type data[4];           \
        struct {                \
            type x, y, z, w;    \
        };                      \
        struct {                \
            Type##2 xy, zw;     \
        };                      \
        Type##3 xyz;            \
    } Type##4

VECTOR_TYPE(float, Vector);
VECTOR_TYPE(int, IntVector);

#define DEFINE_VECTOR(N, name, sname, type)                        \
    name* addSet##sname(VECTOR_OP2(name));                         \
    name* add##sname(VECTOR_OP3(name));                            \
    name* subSet##sname(VECTOR_OP2(name));                         \
    name* sub##sname(VECTOR_OP3(name));                            \
    name* mulSet##sname(VECTOR_OP2(name));                         \
    name* mul##sname(VECTOR_OP3(name));                            \
    name* divSet##sname(VECTOR_OP2(name));                         \
    name* div##sname(VECTOR_OP3(name));                            \
    name* mulSet##sname##F(name* r, type f);                       \
    name* mul##sname##F(name* r, const name* a, type f);           \
    name* divSet##sname##F(name* r, type f);                       \
    name* div##sname##F(name* r, const name* a, type f);           \
    name* invertSet##sname(name* r);                               \
    name* invert##sname(name* r, const name* a);                   \
    size_t toString##sname(const name* a, char* buffer, size_t n);

#define DEFINE_FVECTOR(N, name)                  \
    float dotV##N(const name* a, const name* b); \
    float squareLengthV##N(const name* a);       \
    float lengthV##N(const name* a);             \
    name* normalizeV##N(name* r);

DEFINE_VECTOR(2, Vector2, V2, float)
DEFINE_VECTOR(3, Vector3, V3, float)
DEFINE_VECTOR(4, Vector4, V4, float)
DEFINE_FVECTOR(2, Vector2)
DEFINE_FVECTOR(3, Vector3)
DEFINE_FVECTOR(4, Vector4)
DEFINE_VECTOR(2, IntVector2, IV2, int)
DEFINE_VECTOR(3, IntVector3, IV3, int)
DEFINE_VECTOR(4, IntVector4, IV4, int)

Vector3* angles(Vector3* r, float lengthAngle, float widthAngle);
Vector3* cross(VECTOR_OP3(Vector3));

#define DEFINE_VECTOR_CONVERSION(a, nameA, b, nameB) \
    a* convert##nameB(a* r, const b* c);             \
    b* convert##nameA(b* r, const a* c)

DEFINE_VECTOR_CONVERSION(Vector2, V2, IntVector2, IV2);
DEFINE_VECTOR_CONVERSION(Vector3, V3, IntVector3, IV3);
DEFINE_VECTOR_CONVERSION(Vector4, V4, IntVector4, IV4);

#define VECTOR2 ((Vector2){0})
#define VECTOR3 ((Vector3){0})
#define VECTOR4 ((Vector4){0})
#define INT_VECTOR2 ((IntVector2){0})
#define INT_VECTOR3 ((IntVector3){0})
#define INT_VECTOR4 ((IntVector4){0})

#define SELECT_VECTOR(_1, _2, _3, _4, name, ...) name
#define V(...) \
    ((SELECT_VECTOR(__VA_ARGS__, Vector4, Vector3, Vector2, 0)){{__VA_ARGS__}})
#define IV(...)                                                           \
    ((SELECT_VECTOR(__VA_ARGS__, IntVector4, IntVector3, IntVector2, 0)){ \
        {__VA_ARGS__}})

#endif