#pragma once

#include <stdlib.h>
#include <stdint.h>
#include "defs.h"

/** @file
 * @defgroup Vectors Vectors
 * @addtogroup Vectors
 * @{
 * Vectors are multi value structures that can be used when doing some types of
 * maths.
 */

S_BEGIN_DECLS

/**
 * A Vector of three standard int values.
 *
 * A Vec3_32 is considerer a primitive type and as such has no ref/unref memory
 * management and no vec_free function. You will have to do that yourself.
 *
 * @sa vec3_new
 * @sa Vec3_16
 */
typedef struct
Vec3_32 {
  sint a;
  sint b;
  sint c;
} Vec3_32;

/**
 * A Vector of three 16 bit int values.
 *
 * @sa vec3_16_new
 * @sa Vec3
 */
typedef struct
Vec3_16 {
  sshort a;
  sshort b;
  sshort c;
} Vec3_16;

/**
 *
 * A Vec3_f is a vector of three 32bit precision floating point values.
 */
typedef struct
Vec3_f {
  sfloat a;
  sfloat b;
  sfloat c;
} Vec3_f;

#ifndef __MSC_VER
/**
 * Add Vectors together, resulting in a new vector.
 *
 * The type of vector that is returned depends on the types that were put in.
 *
 * Due to the reflective way of these operations, many may just be inverses of
 * others. IE: `vec3_add (Vec3_32 *, Vec3_16 *)` is the same as
 * `vec3_add (Vec3_16 *, Vec3_32 *)`.
 */
#define vec3_add(x, y) _Generic((x, y)\
                                 Vec3_32 *: _Generic((y)\
                                                 Vec3_32 *: vec3_32_add_vec3_32\
                                                 Vec3_16 *: vec3_32_add_vec3_16_32\
                                                 Vec3_f *: vec3_32_add_vec3_f\
                                                 )\
                                 Vec3_16 *: _Generic((y)\
                                                    Vec3_32 *: vec3_16_add_vec3_32\
                                                    Vec3_16 *: vec3_16_add_vec3_16\
                                                    Vec3_f *: vec3_16_add_vec3_f\
                                                    )\
                                 Vec3_f *: _Generic((y)\
                                                   Vec3_32 *: vec3_f_add_vec3_32\
                                                   Vec3_16 *: vec3_f_add_vec3_16\
                                                   Vec3_f *: vec3_f_add_vec3_f\
                                                   )\
                                 )(x,y)


/**
 * A generic macro fo create vectors
 */
#define vec3_new(x, y) _Generic((x,y)\
                                 sint: _Generic ((y)\
                                                 sint: vec3_32_new\
                                                 )\
                                 sshort: _Generic ((y)\
                                                   sshort: vec3_16_new\
                                                   )\
                                 sfloat: _Generic ((y)\
                                                   sfloat: vec_f_new\
                                 )(x, y)

#endif /* __MSC_VER */

#define vec_free(x) free (x)



/** @brief
 * allocates and initialises a Vec3_32 pointer.
 *
 * @sa Vec3
 */
S_EXPORTED
Vec3_32 *
vec3_32_new (sint a,
             sint b,
             sint c);

/**
 * Add Vector a to Vector
 */
S_EXPORTED
Vec3_32 *
vec3_32_add_Vec3_32 (Vec3_32 * a,
                     Vec3_32 * b);

S_EXPORTED
Vec3_32 *
vec3_32_add_vec3_16 (Vec3_32 * a,
                     Vec3_16 * b);


/**
 * @brief
 * allocates and initialises a Vec3_16 pointer.
 * 
 * @sa Vec3_16
 */
S_EXPORTED
Vec3_16 *
vec3_16_new (sshort a,
             sshort b,
             sshort c);

S_EXPORTED
Vec3_16 *
vec3_16_add_vec3_16 (Vec3_16 * a,
                     Vec3_16 * b);

S_EXPORTED
Vec3_32 *
vec3_16_add_vec3(Vec3_16 * a,
                 Vec3_32 * b);


/**
 * Allocates and initialises a Vec3_f pointer.
 */
S_EXPORTED
Vec3_f *
vec3_f_new (sfloat a,
            sfloat b,
            sfloat c);

S_EXPORTED
Vec3_f *
vec3_f_add_vec3_f (Vec3_f * a,
                   Vec3_f * b);

S_EXPORTED
Vec3_f *
vec3_f_add_Vec3_32 (Vec3_f * a,
                   Vec3_32 * b);

S_EXPORTED
Vec3_f *
vec3_f_add_vec3_16 (Vec3_f * a,
                    Vec3_16 * b);

S_EXPORTED
Vec3_f *
vec3_add_vec3_f (Vec3_32 * a,
                 Vec3_f * b);

S_EXPORTED
Vec3_f *
vec3_16_add_vec3_f (Vec3_16 * a,
                    Vec3_f * b);

/** @} */


S_END_DECLS
