/*	Public domain	*/

struct sg_view;

#define SG_FACET_NAME_MAX 64
#define SG_EDGE_NAME_MAX  32

typedef struct sg_vertex {
	M_Vector2 st;                       /* Texture Coordinates (T2F) */
	M_Color c;                          /* Vertex Color (C4F) */
	M_Vector3 n;                        /* Normal Vector (N3F) */
	M_Vector3 v;                        /* Vertex Position (V3F) */
	Uint flags;
#define SG_VERTEX_SELECTED    0x01          /* Vertex is selected */
#define SG_VERTEX_HIGHLIGHTED 0x02          /* Vertex is highlighted */
#define SG_VERTEX_STRIDE (sizeof(Uint) + sizeof(Uint))
	Uint8 _pad[12];
} SG_Vertex;

typedef struct sg_edge {
	Uint flags;
#define SG_EDGE_SAVED       0x01            /* For the save operation */
#define SG_EDGE_SELECTED    0x02            /* Edge is selected */
#define SG_EDGE_HIGHLIGHTED 0x04            /* Edge is highlighted */
	int v;                              /* Incident vertex */
	struct sg_facet *_Nullable f;       /* Incident facet */
	struct sg_edge *_Nonnull oe;        /* Opposite halfedge */
	AG_SLIST_ENTRY(sg_edge) edges;
} SG_Edge;
typedef struct sg_edge_ent {
	AG_SLIST_HEAD_(sg_edge) edges;      /* Edges in bucket */
} SG_EdgeEnt;

typedef struct sg_facet {
	SG_Edge *_Nullable e[4];            /* Polygon Edges */
	Uint n;                             /* No. of Edges (3 or 4) */
	Uint flags;
#define SG_FACET_SELECTED    0x01           /* Facet is selected */
#define SG_FACET_HIGHLIGHTED 0x02           /* Facet is highlighted */
	struct sg_facet *_Nonnull of;       /* Opposite facet */
	struct sg_object *_Nonnull obj;     /* Back pointer to object */
	AG_SLIST_ENTRY(sg_facet) facets;    /* In facet list */
	AG_TAILQ_ENTRY(sg_facet) bsp;       /* In BSP tree */
} SG_Facet;

typedef struct sg_facet_ent {
	AG_SLIST_HEAD_(sg_facet) facets;    /* Facets in bucket */
} SG_FacetEnt;

typedef struct sg_bsp_node {
	M_Plane P;                          /* Plane */
	SG_Facet *_Nullable facets;         /* Facets */
	Uint               nFacets;
	int                 tag;            /* User tag */
	AG_TAILQ_HEAD_(sg_bsp_node) front;  /* Front subnodes */
	AG_TAILQ_HEAD_(sg_bsp_node) back;   /* Back subnodes */
	AG_TAILQ_ENTRY(sg_bsp_node) bsp;    /* In parent node */
} SG_BSPNode;

typedef struct sg_object {
	struct sg_node _inherit;            /* SG_Node -> SG_Object */
	Uint flags;
#define SG_OBJECT_STATIC      0x01          /* Geometry is unchanging */
#define SG_OBJECT_NODUPVERTEX 0x02          /* Check for duplicate vertices */
	Uint               nVtx;
	SG_Vertex *_Nonnull vtx;            /* Vertex array */
	SG_EdgeEnt *_Nonnull edgeTbl;       /* Edge table */
	Uint                nEdgeTbl;
	Uint                 nFacetTbl;
	SG_FacetEnt *_Nonnull facetTbl;     /* Facet table */
	SG_Texture *_Nullable tex;          /* Associated texture */
	SG_BSPNode *_Nullable bsp;          /* Root BSP node */
	Uint8 _pad[8];
} SG_Object;

typedef enum sg_extrude_mode {
	SG_EXTRUDE_REGION,                  /* Create 2n edges and n+1 faces */
	SG_EXTRUDE_EDGES,                   /* Create 2n edges and n faces */
	SG_EXTRUDE_VERTICES                 /* Create n edges and no faces */
} SG_ExtrudeMode;

#define   SGOBJECT(o)        ((SG_Object *)(o))
#define  SGcOBJECT(o)        ((const SG_Object *)(o))
#define  SG_OBJECT_ISA(o)    (((AGOBJECT(o)->cid & 0xffff0000) >> 16) == 0x7A05)
#define  SG_OBJECT_SELF()    SGOBJECT(  AG_OBJECT(0,         "SG_Node:SG_Object:*") )
#define  SG_OBJECT_PTR(n)    SGOBJECT(  AG_OBJECT((n),       "SG_Node:SG_Object:*") )
#define  SG_OBJECT_NAMED(n)  SGOBJECT(  AG_OBJECT_NAMED((n), "SG_Node:SG_Object:*") )
#define SG_cOBJECT_SELF()   SGcOBJECT( AG_cOBJECT(0,         "SG_Node:SG_Object:*") )
#define SG_cOBJECT_PTR(n)   SGcOBJECT( AG_cOBJECT((n),       "SG_Node:SG_Object:*") )
#define SG_cOBJECT_NAMED(n) SGcOBJECT( AG_cOBJECT_NAMED((n), "SG_Node:SG_Object:*") )

/* Internal shorthands */
#ifdef _AGAR_SG_INTERNAL
# define OBJ_V(o,idx)        (SGOBJECT(o)->vtx[idx].v)
# define OBJ_N(o,idx)        (SGOBJECT(o)->vtx[idx].n)
# define OBJ_ST(o,idx)       (SGOBJECT(o)->vtx[idx].st)
# define FACET_V(o,fct,edge) (SGOBJECT(o)->vtx[(fct)->e[edge]->v].v)
# define FACET_N(o,fct,edge) (SGOBJECT(o)->vtx[(fct)->e[edge]->v].n)
# define FACET_S(o,fct,edge) (SGOBJECT(o)->vtx[(fct)->e[edge]->v].s)
# define FACET_T(o,fct,edge) (SGOBJECT(o)->vtx[(fct)->e[edge]->v].t)
# define EDGE_V(o,edge)      (SGOBJECT(o)->vtx[(edge)->v].v)
# define EDGE_N(o,edge)      (SGOBJECT(o)->vtx[(edge)->v].n)
# define EDGE_S(o,edge)      (SGOBJECT(o)->vtx[(edge)->v].s)
# define EDGE_T(o,edge)      (SGOBJECT(o)->vtx[(edge)->v].t)
# define LFACE(e) ((e)->f)      /* Halfedge traversal to left face */
# define RFACE(e) ((e)->oe->f)  /* Halfedge traversal to right face */
#endif /* _AGAR_SG_INTERNAL */

#define SG_FOREACH_EDGE(edge, i, so) for ((i) = 0, (edge) = NULL; (i) < (so)->nEdgeTbl; (i)++) AG_SLIST_FOREACH(edge, &(so)->edgeTbl[(i)].edges, edges)
#define SG_FOREACH_FACET(facet, i, so) for ((i) = 0, (facet) = NULL; (i) < (so)->nFacetTbl; (i)++) AG_SLIST_FOREACH(facet, &(so)->facetTbl[(i)].facets, facets)

/* Begin generated block */
__BEGIN_DECLS
extern DECLSPEC SG_NodeClass sgObjectClass;
 struct ag_console;
struct ag_menu_item;
extern DECLSPEC SG_Object *_Nonnull SG_ObjectNew(void *_Nullable, const char *_Nullable);
extern DECLSPEC void SG_ObjectSetTexture(void *_Nonnull, SG_Texture *_Nullable);
extern DECLSPEC SG_Texture *_Nullable SG_ObjectGetTexture(void *_Nonnull);
extern DECLSPEC Uint8 *_Nullable SG_ObjectEdgeMatrix(SG_Object *_Nonnull, Uint *_Nonnull);
extern DECLSPEC Uint8 *_Nullable SG_ObjectFacetMatrix(SG_Object *_Nonnull, Uint *_Nonnull);
extern DECLSPEC int SG_ObjectCheckConnectivity(void *_Nonnull, struct ag_console *_Nonnull);
extern DECLSPEC int SG_ObjectNormalize(void *_Nonnull);
extern DECLSPEC Uint SG_ObjectConvQuadsToTriangles(void *_Nonnull);
extern DECLSPEC void SG_ObjectFreeGeometry(void *_Nonnull);
extern DECLSPEC void SG_ObjectMenuInstance(void *_Nonnull, struct ag_menu_item *_Nonnull, struct sg_view *_Nonnull);
extern DECLSPEC void SG_VertexInit(SG_Vertex *_Nonnull);
extern DECLSPEC int SG_VertexNew(void *_Nonnull, const M_Vector3);
extern DECLSPEC int SG_VertexNewv(void *_Nonnull, const M_Vector3 *_Nonnull);
extern DECLSPEC int SG_VertexNewvn(void *_Nonnull, const M_Vector3 *_Nonnull, const M_Vector3 *_Nonnull);
extern DECLSPEC int SG_VertexNew2(void *_Nonnull, M_Real,M_Real);
extern DECLSPEC int SG_VertexNew3(void *_Nonnull, M_Real,M_Real,M_Real);
extern DECLSPEC int SG_VertexNewCopy(void *_Nonnull, const SG_Vertex *_Nonnull);
extern DECLSPEC int SG_EdgeRehash(void *_Nonnull, Uint);
extern DECLSPEC int SG_FacetRehash(void *_Nonnull, Uint);
extern DECLSPEC SG_Edge *_Nonnull SG_Edge2(void *_Nonnull, int,int);
extern DECLSPEC void SG_EdgeGetName(SG_Edge *_Nonnull, char *_Nonnull, AG_Size);
extern DECLSPEC SG_Facet *_Nonnull SG_FacetFromTri3(void *_Nonnull, int,int,int);
extern DECLSPEC SG_Facet *_Nonnull SG_FacetFromQuad4(void *_Nonnull, int,int,int,int);
extern DECLSPEC int SG_FacetExtrude(void *_Nonnull, SG_Facet *_Nonnull, M_Vector3, SG_ExtrudeMode);
extern DECLSPEC void SG_FacetDelete(SG_Facet *_Nonnull);
extern DECLSPEC void SG_FacetGetName(SG_Facet *_Nonnull, char *_Nonnull, AG_Size);
extern DECLSPEC M_Real SG_FacetArea(SG_Object *_Nonnull, SG_Facet *_Nonnull);
extern DECLSPEC M_Real SG_FacetAreaSigned(SG_Object *_Nonnull, SG_Facet *_Nonnull);
extern DECLSPEC M_Vector3 SG_FacetCentroid(SG_Object *_Nonnull, SG_Facet *_Nonnull);

static __inline__ Uint
SG_HashEdge(SG_Object *_Nonnull so, int v1, int v2)
{
	return ((31*v1 + 31*v2) % so->nEdgeTbl);
}

static __inline__ Uint
SG_HashTriangle(SG_Object *_Nonnull so, int v1, int v2, int v3)
{
	return (31*v1 + 31*v2 + 31*v3) % so->nFacetTbl;
}

static __inline__ Uint
SG_HashQuad(SG_Object *_Nonnull so, int v1, int v2, int v3, int v4)
{
	return (31*v1 + 31*v2 + 31*v3 + 31*v4) % so->nFacetTbl;
}

static __inline__ Uint
SG_HashFacet(SG_Object *_Nonnull so, const SG_Facet *_Nonnull f)
{
	if (f->n == 3) {
		return SG_HashTriangle(so,
		    f->e[0]->v,
		    f->e[1]->v,
		    f->e[2]->v);
	} else {
		return SG_HashQuad(so,
		    f->e[0]->v,
		    f->e[1]->v,
		    f->e[2]->v,
		    f->e[3]->v);
	}
}

static __inline__ SG_Edge *_Nonnull 
SG_FindEdge(void *_Nonnull obj, int v1, int v2)
{
	SG_Object *so = obj;
	SG_EdgeEnt *ent = &so->edgeTbl[SG_HashEdge(so, v1,v2)];
	SG_Edge *e;

	AG_SLIST_FOREACH(e, &ent->edges, edges) {
		if (e->v == v1 && e->oe->v == v2)
			break;
	}
	return (e);
}

static __inline__ SG_Facet *_Nonnull 
SG_FindTriangle(void *_Nonnull obj, int v1, int v2, int v3)
{
	SG_Object *so = obj;
	SG_FacetEnt *fe = &so->facetTbl[SG_HashTriangle(so,v1,v2,v3)];
	SG_Facet *f;

	AG_SLIST_FOREACH(f, &fe->facets, facets) {
		if (f->e[0]->v == v1 &&
		    f->e[1]->v == v2 &&
		    f->e[2]->v == v3)
			break;
	}
	return (f);
}

static __inline__ SG_Facet *_Nonnull 
SG_FindQuad(void *_Nonnull obj, int v1, int v2, int v3, int v4)
{
	SG_Object *so = obj;
	SG_FacetEnt *fe = &so->facetTbl[SG_HashQuad(so,v1,v2,v3,v4)];
	SG_Facet *f;

	AG_SLIST_FOREACH(f, &fe->facets, facets) {
		if (f->e[0]->v == v1 &&
		    f->e[1]->v == v2 &&
		    f->e[2]->v == v3 &&
		    f->e[3]->v == v4)
			break;
	}
	return (f);
}
/*
 * Compute the normal for a given polygon facet.
 * The object must be locked.
 */

static __inline__ M_Vector3
SG_FacetNormal(SG_Object *_Nonnull so, const SG_Facet *_Nonnull f)
{
	M_Vector3 v0, v1, v2;

	v1 = so->vtx[f->e[2]->v].v;		
	v0 = so->vtx[f->e[1]->v].v;
	v2 = so->vtx[f->e[0]->v].v;
	return M_VecNormCross3(M_VecSub3(v0,v1),
	                       M_VecSub3(v0,v2));
}
#ifdef _AGAR_SG_INTERNAL

static __inline__ void
GL_VertexTN(const SG_Vertex *_Nonnull vtx)
{
	GL_TexCoord2(vtx->st.x, vtx->st.y);
	GL_Normal3v(&vtx->n);
	GL_Color4v(&vtx->c);
	GL_Vertex3v(&vtx->v);
}
#endif 
__END_DECLS
/* Close generated block */
