#ifndef SCONVCOL_H
#define SCONVCOL_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <assert.h>

#include "vectorial/simd4x4f.h"

typedef struct sch_edge sch_edge;
typedef struct sch_vert sch_vert;
typedef struct sch_face sch_face;
typedef struct sch_hull sch_hull;
typedef struct sch_plane sch_plane;

struct sch_edge {
	sch_vert* vert;
	sch_edge* twin;
	sch_face* face;
	sch_edge* next;
};

struct sch_vert {
	simd4f p;
	sch_edge* edge;
};

struct sch_face {
	sch_edge* edge;
};

struct sch_hull {
	int num_faces;
	sch_face* faces;
};

struct sch_plane {
	simd4f p;
	simd4f n;
};

float sch_plane_distance (sch_plane* plane, simd4f* v);

void sch_edge_get_dir (sch_edge* edge, simd4f* dir);

void sch_hull_get_plane (sch_hull* hull, int index, sch_plane *out_plane);

void sch_hull_get_support (sch_hull* hull, simd4f n, simd4f* out_vert);

void sch_create_face (int num_vert, simd4f* vertices, sch_face* out_face);

void sch_create_hull (int num_faces, sch_face* faces,
		sch_hull* out_hull);

//
// srender Implementation
//

#ifdef SCONVCOL_IMPLEMENTATION

float sch_plane_distance (sch_plane* plane, simd4f* v) {
	return simd4f_dot3_scalar(simd4f_sub (*v, plane->p), plane->n);
}

void sch_create_face (int num_vert, simd4f* vertices, sch_face* out_face) {
	assert(out_face != NULL);
	assert(out_face->edge == NULL);

	int i = 0;
	sch_edge* f_edges = malloc(sizeof(sch_edge) * num_vert);
	sch_vert* f_verts = malloc(sizeof(sch_vert) * num_vert);

	while (i < num_vert) {
		sch_vert* vert = &f_verts[i];
		sch_edge* edge = &f_edges[i];

		edge->twin = NULL;
		edge->vert = vert;
		edge->face = out_face;
		edge->next = &f_edges[(i + 1) % num_vert];

		vert->edge = edge;
		vert->p = vertices[i];

		i++;
	}

	out_face->edge = &f_edges[0];
}

void sch_edge_get_dir (sch_edge* edge, simd4f* out_dir) {
	*out_dir = simd4f_sub (edge->next->vert->p, edge->vert->p);
	float recip_len = 1. / sqrt(simd4f_dot3_scalar (*out_dir, *out_dir));
	*out_dir = simd4f_mul (*out_dir, simd4f_splat(recip_len));
}

void sch_create_hull (int num_faces, sch_face* faces,
		sch_hull* out_hull) {
	assert (out_hull != NULL);
	assert (out_hull->num_faces == NULL);

	out_hull->num_faces = num_faces;
	out_hull->faces = faces;
}

void sch_hull_get_plane (sch_hull* hull, int index, sch_plane* out_plane) {
	assert (hull != NULL);
	assert (index >= 0 && index < hull->num_faces);
	assert (out_plane != NULL);

	// TODO move plane calculation to create hull?
	sch_face* face = &hull->faces[index];
	sch_edge* edge0 = face->edge;
	sch_edge* edge1 = edge0->next;
	simd4f dir0;
	simd4f dir1;
	sch_edge_get_dir(edge0, &dir0);
	sch_edge_get_dir(edge1, &dir1);

	out_plane->p = edge0->vert->p;
	out_plane->n = simd4f_cross3 (dir0, dir1);
}

#endif /* SCONVCOL_IMPLEMENTATION */

#ifdef __cplusplus
}
#endif

#endif /* SCONVCOL_H */
