#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "vector3.h"

// arithmetic operations
INLINE_MODE vector3* vector3_add(vector3 *dest, const vector3 *v1, const vector3 *v2)
{
	dest->x = v1->x + v2->x;
	dest->y = v1->y + v2->y;
	dest->z = v1->z + v2->z;
	return dest;
}

INLINE_MODE vector3* vector3_subtract(vector3 *dest, const vector3 *minuend, const vector3 *subtrahend)
{
	dest->x = minuend->x - subtrahend->x;
	dest->y = minuend->y - subtrahend->y;
	dest->z = minuend->z - subtrahend->z;
	return dest;
}

INLINE_MODE vector3* vector3_multiply(vector3 *dest, const vector3 *v, const v3float n)
{
	dest->x = v->x * n;
	dest->y = v->y * n;
	dest->z = v->z * n;
	return dest;
}

INLINE_MODE vector3* vector3_divide(vector3 *dest, const vector3 *v,const  v3float n)
{
	dest->x = v->x / n;
	dest->y = v->y / n;
	dest->z = v->z / n;
	return dest;
}

INLINE_MODE vector3 *vector3_scalar_add(vector3 *dest, const vector3 *v, const v3float s)
{
	dest->x = v->x + s;
	dest->y = v->y + s;
	dest->z = v->z + s;
	return dest;
}

INLINE_MODE vector3 *vector3_scalar_sub(vector3 *dest, const vector3 *v, const v3float s)
{
	dest->x = v->x - s;
	dest->y = v->y - s;
	dest->z = v->z - s;
	return dest;
}

INLINE_MODE v3float vector3_dot(const vector3 *a, const vector3 *b)
{
	return a->x*b->x + a->y*b->y + a->z*b->z;
}

INLINE_MODE vector3* vector3_cross(vector3 *dest, const vector3 *a, const vector3 *b)
{
	dest->x = a->y*b->z - a->z*b->y;
	dest->y = a->z*b->x - a->x*b->z;
	dest->z = a->x*b->y - a->y*b->x;
	
	return dest;
}

// vector3 unit operations
INLINE_MODE v3float vector3_length(const vector3 *a)
{
	return sqrt(a->x*a->x + a->y*a->y + a->z*a->z);
}

INLINE_MODE vector3* vector3_normalize(vector3 *a)
{
	v3float normalizeLength;
	normalizeLength = vector3_length(a);
	
	if(normalizeLength <= EPSILON)
	{
		printf("cannot normalize degenerate vector3\n");
		return a;
	}
	
	vector3_divide(a, a, normalizeLength);
	return a;
}

INLINE_MODE vector3* vector3_invert(vector3 *dest, const vector3 *v)
{
	dest->x = -v->x;
	dest->y = -v->y;
	dest->z = -v->z;
	return dest;
}

INLINE_MODE void vector3_print(const vector3 *v)
{
	printf("%.2f %.2f %.2f\n", v->x, v->y, v->z);
}

// vector3 creation
INLINE_MODE vector3* vector3_copy(vector3 *dest, const vector3 *source)
{
	dest->x = source->x;
	dest->y = source->y;
	dest->z = source->z;
	return dest;
}

INLINE_MODE vector3* vector3_random(vector3 *v)
{
	do {
		v->x = (double)rand()/RAND_MAX*2-1;
		v->y = (double)rand()/RAND_MAX*2-1;
		v->z = (double)rand()/RAND_MAX*2-1;
	} while (v->x*v->x + v->y*v->y + v->z*v->z > 1.0);
	return v;
}

INLINE_MODE vector3* vector3_make3f(vector3 *v, const v3float x, const v3float y, const v3float z)
{
	v->x = x;
	v->y = y;
	v->z = z;
	return v;
}

INLINE_MODE vector3* vector3_make2v(vector3 *v, const vector3 *to, const vector3 *from)
{
	v->x = to->x - from->x;
	v->y = to->y - from->y;
	v->z = to->z - from->z;
	return v;
}

// vector3 combination operations
INLINE_MODE v3float vector3_distance(const vector3 *a, const vector3 *b)
{
	return sqrt( pow(a->x - b->x, 2) + pow(a->y - b->y, 2) + 
				pow(a->z - b->z, 2));
}

INLINE_MODE double vector3_distancesq(vector3 *a, vector3 *b)
{
	return pow(a->x - b->x, 2) + pow(a->y - b->y, 2) + 
				pow(a->z - b->z, 2);
}

INLINE_MODE v3float vector3_angle(const vector3 *a, const vector3 *b) 
{
	return acos(vector3_dot(a,b) / vector3_length(a) / vector3_length(b));
}

INLINE_MODE vector3* vector3_reflect(vector3 *dest, const vector3 *incoming, const vector3 *normal)
{
	v3float dp;
	dp = 2*vector3_dot(normal, incoming);

	dest->x = incoming->x - dp*normal->x;
	dest->y = incoming->y - dp*normal->y;
	dest->z = incoming->z - dp*normal->z;
	
	return dest;
}

void vector3_test()
{
	vector3 zero = {0,0,0};
	vector3 one = {1,1,1};
	vector3 y = {0,1,0};
	vector3 half = {0.5,0.5,0.5};
	vector3 a;
	
	vector3_invert(&a, &one);
	vector3_subtract(&a, &one, &a);
	vector3_add(&a, &a, &one);
	vector3_print(&a);
	
	vector3_multiply(&a, &one, 0.5);
	vector3_divide(&a, &a, 2);
	vector3_print(&a);
	
	vector3_reflect(&a, &one, &y);
	vector3_print(&a);
	
	vector3_scalar_sub(&a, &zero, -0.5);
	vector3_scalar_add(&a, &a, 0.5);
	vector3_print(&a);
	
	vector3_cross(&a, &one, &y);
	vector3_print(&a);
	
	srand(3);
	vector3_random(&a);
	vector3_print(&a);
	
	printf("%.2f %.2f\n", 
		   vector3_dot(&half, &y), vector3_angle(&half, &y));
	
	printf("%.2f %.2f\n", 
		   vector3_distance(&one, &y), vector3_distancesq(&one, &y));
	
	vector3_copy(&a, &one);
	printf("%.2f %.2f\n", 
		   vector3_length(&one), vector3_length(vector3_normalize(&a)) );
}

int main(int argc, char **argv)
{
	vector3_test();
}
