The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "defines.h"
#include "helper.h"

#ifndef aTHX_
#define aTHX_
#endif

#include "SDLx/Controller/Interface.h"

AV *acceleration_cb( SDLx_Interface *obj, float t )
{
	SV *tmpsv;
	AV *array;
	int i;
	int count;
    SDLx_State *copyState;
	dSP;
	if( !(SvROK(obj->acceleration) && (tmpsv = obj->acceleration) ) )
		croak( "Interface doesn't not contain an acceleration callback" );

	array = newAV();
	copyState = (SDLx_State *)safemalloc( sizeof(SDLx_State) );
	copy_state( copyState, obj->current );
	copyState->owned = 0;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	XPUSHs( sv_2mortal(newSVnv(t)) );
	XPUSHs( sv_2mortal( obj2bag( sizeof(SDLx_State *), (void *)copyState, "SDLx::Controller::State" ) ) );
	PUTBACK;

	count = call_sv( obj->acceleration, G_ARRAY );

	SPAGAIN;
/*	warn( "state %p, state->x %f", copyState, ((SDLx_State *)copyState)->x ); */
	for( i = 0; i < count; i++ )
		av_push( array, newSVnv(POPn) );

/*	warn ("before obj->current->x %f", obj->current->x); */
	copy_state( obj->current, copyState );
/*	warn ("after obj->current->x %f", obj->current->x); */
	PUTBACK;
	FREETMPS;
	LEAVE;

	return array;
}

void evaluate(SDLx_Interface *obj, SDLx_Derivative *out, SDLx_State *initial, float t)
{
	AV *accel;
	SV *temp;
	out->dx        = initial->v_x;
	out->dy        = initial->v_y;
	out->drotation = initial->ang_v;
	accel          = acceleration_cb(obj, t);

	temp           = av_pop(accel);
	out->dv_x      = sv_nv(temp);
	SvREFCNT_dec(temp);

	temp           = av_pop(accel);
	out->dv_y      = sv_nv(temp);
	SvREFCNT_dec(temp);

	temp           = av_pop(accel);
	out->dang_v    = sv_nv(temp);
	SvREFCNT_dec(temp);

	SvREFCNT_dec((SV *)accel);
}

void evaluate_dt(SDLx_Interface *obj, SDLx_Derivative *out, SDLx_State *initial, float t, float dt, SDLx_Derivative *d)
{
	SDLx_State state;
	AV *accel;
	SV *temp;

	state.x        = initial->x        + d->dx        * dt;
	state.y        = initial->y        + d->dy        * dt;
	state.rotation = initial->rotation + d->drotation * dt;

	state.v_x      = initial->v_x      + d->dv_x      * dt;
	state.v_y      = initial->v_y      + d->dv_y      * dt;
	state.ang_v    = initial->ang_v    + d->dang_v    * dt;

	out->dx        = state.v_x;
	out->dy        = state.v_y;
	out->drotation = state.ang_v;

	accel          = acceleration_cb(obj, t+dt);

	temp           = av_pop(accel);
	out->dv_x      = sv_nv(temp);
	SvREFCNT_dec(temp);

	temp           = av_pop(accel);
	out->dv_y      = sv_nv(temp);
	SvREFCNT_dec(temp);

	temp           = av_pop(accel);
	out->dang_v    = sv_nv(temp);
	SvREFCNT_dec(temp);

	SvREFCNT_dec((SV *)accel);
}

void integrate( SDLx_Interface *object, float t, float dt)
{
	SDLx_State *state;
	SDLx_Derivative *a;
	SDLx_Derivative *b;
	SDLx_Derivative *c;
	SDLx_Derivative *d;

	state = object->current;
	a     = (SDLx_Derivative *)safemalloc( sizeof(SDLx_Derivative) );
	b     = (SDLx_Derivative *)safemalloc( sizeof(SDLx_Derivative) );
	c     = (SDLx_Derivative *)safemalloc( sizeof(SDLx_Derivative) );
	d     = (SDLx_Derivative *)safemalloc( sizeof(SDLx_Derivative) );

	 evaluate(object, a, state, t);
	 evaluate_dt(object, b, state, t, dt*0.5f, a);
	 evaluate_dt(object, c, state, t, dt*0.5f, b);
	 evaluate_dt(object, d, state, t, dt, c);

	{
		const float dxdt        = 1.0f/6.0f * (a->dx        + 2.0f * (b->dx        + c->dx)        + d->dx);
		const float dv_xdt      = 1.0f/6.0f * (a->dv_x      + 2.0f * (b->dv_x      + c->dv_x)      + d->dv_x);
		const float dydt        = 1.0f/6.0f * (a->dy        + 2.0f * (b->dy        + c->dy)        + d->dy);
		const float dv_ydt      = 1.0f/6.0f * (a->dv_y      + 2.0f * (b->dv_y      + c->dv_y)      + d->dv_y);
		const float drotationdt = 1.0f/6.0f * (a->drotation + 2.0f * (b->drotation + c->drotation) + d->drotation);
		const float dv_angdt    = 1.0f/6.0f * (a->dang_v    + 2.0f * (b->dang_v    + c->dang_v)    + d->dang_v);

		state->x        = state->x        + dxdt        * dt;
		state->v_x      = state->v_x      + dv_xdt      * dt;
		state->y        = state->y        + dydt        * dt;
		state->v_y      = state->v_y      + dv_ydt      * dt;
		state->rotation = state->rotation + drotationdt * dt;
		state->ang_v    = state->ang_v    + dv_angdt    * dt;
	}

	safefree(a);
	safefree(b);
	safefree(c);
	safefree(d);
}

MODULE = SDLx::Controller::Interface    PACKAGE = SDLx::Controller::Interface    PREFIX = objx_

SDLx_Interface *
objx_make( CLASS, ... )
	char *CLASS
	CODE:
		RETVAL               = (SDLx_Interface *)safemalloc( sizeof(SDLx_Interface) );
		RETVAL->previous     = (SDLx_State *)safemalloc( sizeof(SDLx_State) );
		RETVAL->current      = (SDLx_State *)safemalloc( sizeof(SDLx_State) );
		RETVAL->acceleration = newSViv(-1);

		RETVAL->current->x        = 0;
		RETVAL->current->y        = 0;
		RETVAL->current->v_x      = 0;
		RETVAL->current->v_y      = 0;
		RETVAL->current->rotation = 0;
		RETVAL->current->ang_v    = 0;
		RETVAL->current->owned    = 1;
		RETVAL->previous->owned   = 1;

		if(items > 1) (RETVAL->current)->x        = SvIV(ST(1));
		if(items > 2) (RETVAL->current)->y        = SvIV(ST(2));
		if(items > 3) (RETVAL->current)->v_x      = SvIV(ST(3));
		if(items > 4) (RETVAL->current)->v_y      = SvIV(ST(4));
		if(items > 5) (RETVAL->current)->rotation = SvIV(ST(5));
		if(items > 6) (RETVAL->current)->ang_v    = SvIV(ST(6));

		copy_state(RETVAL->previous, RETVAL->current);
	OUTPUT:
		RETVAL

void
objx_set_acceleration(obj, callback)
	SDLx_Interface *obj
	SV *callback
	PREINIT:
		SV *tmpsv;
	CODE:
		tmpsv = NULL;
		if( !(SvROK(callback) && (tmpsv = (SV*)SvRV(callback)) && SvTYPE(tmpsv) == SVt_PVCV ) )
			croak( "Acceleration callback needs to be a code ref, %p", callback );
		obj->acceleration = SvRV( newRV_inc(callback) );

AV *
objx_acceleration(obj, t)
	SDLx_Interface* obj
	float t
	CODE:
		RETVAL = acceleration_cb(obj, t);
		sv_2mortal((SV*)RETVAL);
	OUTPUT:
		RETVAL

SDLx_State *
objx_interpolate(obj, alpha)
	SDLx_Interface *obj
	float alpha
	PREINIT:
		SDLx_State *out;
		char *CLASS = "SDLx::Controller::State";
	CODE:
		out = (SDLx_State *)safemalloc(sizeof(SDLx_State ));
		interpolate( obj,out, alpha);
		out->owned = 0; /* condition free */
		RETVAL     = out;
	OUTPUT:
		RETVAL

SDLx_State *
objx_current ( obj, ... )
	SDLx_Interface *obj
	PREINIT:
		char * CLASS = "SDLx::Controller::State";
	CODE:
		RETVAL = obj->current;
	OUTPUT:
		RETVAL

SDLx_State *
objx_previous ( obj, ... )
	SDLx_Interface *obj
	PREINIT:
		char *CLASS = "SDLx::Controller::State";
	CODE:
		RETVAL = obj->previous;
	OUTPUT:
		RETVAL

void
objx_update(obj, t, dt)
	SDLx_Interface *obj
	float t
	float dt
	CODE:
		copy_state( obj->previous, obj->current);
		integrate( obj, t, dt );

void
objx_DESTROY( obj )
	SDLx_Interface *obj
	CODE:
		SvREFCNT_dec(obj->acceleration);
		safefree(obj->previous);
		safefree(obj->current);
		safefree(obj);