#include <v_lib.h>
#include "psy.h"
#include "monitor.h"
#include "psys.h"
#include "util.h"
#include "phasesp.h"

/*
 * -----------------------------------------------------------------------
 * PSY output stuff
 */
typedef struct psy_output_s
{
	u32_t	   framenum;
	vboolean	noflush;
	vhandle_t   outfile;
}   psy_output_t, *psy_output_p;

V_LOCAL vboolean V_CALL
psy_save_frame(particle_system_p ps)
{
	vec_t curtime;
	u32_t i,limit = ps->num_particles-1;
	vec_p phase = ps->cur_phase, p, v;
	psy_output_p ctx = (psy_output_p)ps->monitoring_context;
	filePrintf(ctx->outfile,"frame %u\n{\n",ctx->framenum++);
	for(i=0;i<limit;i++)
	{
		vec_t *p = PS_POS(phase,i);
		vec_t *v = PS_VEL(phase,i);
		filePrintf(ctx->outfile,"\t{%g,%g,%g},{%g,%g,%g},\n",
			p[0],p[1],p[2],
			v[0],v[1],v[2]);
	}
	p = PS_POS(phase,i);
	v = PS_VEL(phase,i);
	filePrintf(ctx->outfile,"\t{%g,%g,%g},{%g,%g,%g}\n}\n",
		p[0],p[1],p[2],
		v[0],v[1],v[2]);
	curtime = timerTime(ps->rt_timer);
	if(curtime < ps->solution_realtime_limit)
	{
		conlPrintf(LOG_TRACE,
			"psy_save_frame(): saved frame %u "
			"(time %g,realtime %g,rt_limit %g)\n",
			ctx->framenum,ps->cur_time,curtime,ps->solution_realtime_limit);
		if(!ctx->noflush)
			fileControl(ctx->outfile,pctl_flush,NULL);
	}
	else
	{
		conlPrintf(LOG_TRACE,
			"psy_save_frame(): last frame %u (realtime %g,rt_limit %g)\n",
			ctx->framenum,curtime,ps->solution_realtime_limit);
		return false;
	}
	return true;
}

V_LOCAL vboolean V_CALL
psy_end_monitoring(particle_system_p ps)
{
	psy_output_p ctx = (psy_output_p)ps->monitoring_context;
	fileClose(ctx->outfile);
	memFree(ctx);
	return true;
}

vboolean
init_psy_context(particle_system_p ps)
{
	char outfile_name[_MAX_PATH] = "";
	psy_output_p ctx;
	vboolean noflush = false;
	vhandle_t sc = lexParseString(ps->monitor_setup,"");
	while(lexNext(sc))
	{
		if(!strcmp(lexLast(sc),"filename"))
		{
			strCopy(outfile_name,lexNext(sc),_MAX_PATH);
			continue;
		}
		if(!strcmp(lexLast(sc),"noflush"))
		{
			noflush = true;
			continue;
		}
		conlPrintf(LOG_WARNING,"init_psy_context(): unknown token '%s'.\n",
			lexLast(sc));
	}
	lexDestroy(sc);
	ctx = (psy_output_p)memAlloc(sizeof(psy_output_t));
	memZero(ctx,sizeof(psy_output_t));
	ctx->outfile = fileOpenWrite(outfile_name);
	if(ctx->outfile == LIBV_INVALID_HANDLE)
	{
		conlPrintf(LOG_ERROR,"init_psy_context(): unable to open '%s'.\n",
			outfile_name);
		memFree(ctx);
		return false;
	}
	ctx->framenum = 0;
	ctx->noflush = noflush;
	ps->monitor = psy_save_frame;
	ps->monitoring_context = ctx;
	ps->end_monitoring = psy_end_monitoring;
	return true;
}

/* from ms_wr */
extern void
ms_create_spheres(vhandle_t out,particle_system_p ps);

vboolean
psy_save_pos(particle_system_p ps, char *filename, vboolean msform)
{
	u32_t i;
	vhandle_t outfile = fileOpenWrite(filename);
	if(!outfile)
		return false;
	filePrintf(outfile,"num_particles;%u\n",ps->num_particles);
	filePrintf(outfile,"cur_time;%g\n",ps->cur_time);
	filePrintf(outfile,"p[0];p[1];p[2];v[0];v[1];v[2];radius\n");
	if(!msform)
	{
		/* plane output */
		for(i=0;i<ps->num_particles;i++)
		{
			vec_t *p = PS_POS(ps->cur_phase,i);
			vec_t *v = PS_VEL(ps->cur_phase,i);
			filePrintf(outfile,"%g;%g;%g;%g;%g;%g;%g\n",
				p[0],p[1],p[2],
				v[0],v[1],v[2],
				ps->particle_radius[i]);
		}
	}
	else
	{
		/* spheres for 3dsmax */
		ms_create_spheres(outfile, ps);
	}
	fileClose(outfile);
	return true;
}

vboolean
psy_load_pos(particle_system_p ps, char *filename)
{
	s32_t psread;
	u32_t i;
	vhandle_t lex = lexParseFile(filename,";");
	if(!lex)
		return false;
	if(!lexNextIs(lex,"num_particles") || !lexNextIs(lex,";"))
		goto fail;
	if(!lexNextInteger(lex, &psread))
		goto fail;
	/* BUGBUG: numparticles should be less than 2^31 */
	if(psread < 0 || (u32_t)psread != ps->num_particles)
	{
		puts("1");
		conPrintf("psy_load_pos(%s): psread %i != ps->num_particles %u.\n",
			filename, psread, ps->num_particles);
		lexDestroy(lex);
		return false;
	}
	if(!lexNextIs(lex,"cur_time") || !lexNextIs(lex,";"))
		goto fail;
	if(!lexNextFloat(lex, &ps->cur_time))
		goto fail;
	/* skip next line */
	for(i = 0;i < 13;i++)
		lexNext(lex);
	for(i=0;i < ps->num_particles;i++)
	{
		vec_t *p = PS_POS(ps->phase,i);
		vec_t *v = PS_VEL(ps->phase,i);
		if( !lexNextFloat(lex, &p[0]) || !lexNextIs(lex,";") ||
			!lexNextFloat(lex, &p[1]) || !lexNextIs(lex,";") ||
			!lexNextFloat(lex, &p[2]) || !lexNextIs(lex,";") ||
			!lexNextFloat(lex, &v[0]) || !lexNextIs(lex,";") ||
			!lexNextFloat(lex, &v[1]) || !lexNextIs(lex,";") ||
			!lexNextFloat(lex, &v[2]) || !lexNextIs(lex,";") ||
			!lexNextFloat(lex, &ps->particle_radius[i]))
		{
			goto fail;
		}
	}
	lexDestroy(lex);
	return true;
fail:;
	conPrintf("psy_load_pos(%s): parse error at line %i.\n",
		filename, lexGetInteger(lex, LEX_CURRENT_LINE));
	lexDestroy(lex);
	return false;
}
