#include <v_lib.h>
#include <time.h>
#include "config.h"
#include "phasesp.h"
#include "ctrl.h"
#include "psys.h"
#include "icond.h"
#include "util.h"
#include "monitor.h"

extern vboolean nofilelog;

V_LOCAL u8_p *connmatAlloc(u32_t size)
{
	u8_p *rows;
	u32_t i;
	rows = (u8_p*)memAllocZ(size * sizeof(u8_p) + size * size);
	rows[0] = (u8_p)(&rows[size]);
	for(i = 1;i < size;i++)
		rows[i] = rows[i-1] + size;
	return rows;
}

V_LOCAL	particle_system_p
psAlloc(u32_t num_particles)
{
	particle_system_p ps = NULL;
	ps = memAllocZ(sizeof(particle_system_t));
	ps->num_particles = num_particles;
	ps->particle_radius = memAllocVecs(num_particles);
	ps->particle_density = memAllocVecs(num_particles);
	ps->particle_mass = memAllocVecs(num_particles);
	ps->particle_mass_delta = memAllocVecs(num_particles);
	ps->particle_reciprocal_mass = memAllocVecs(num_particles);
	ps->particle_6_pi_eta_r = memAllocVecs(num_particles);
	ps->sums = memAllocVecs(num_particles);
	ps->dist = matAlloc(num_particles, num_particles);
	ps->surf_dist = matAlloc(num_particles, num_particles);
	ps->mon_dist = matAlloc(num_particles, num_particles);
	ps->phase = memAllocVecs(num_particles * PHASESPACE_DIM);
	ps->force = (vec3_t*)memAllocZ(num_particles * sizeof(vec3_t));
	ps->tangent_forces = false;
	ps->dglue = memAllocVecs(num_particles);
	ps->percolation_distance = memAllocVecs(num_particles);
	ps->hglue = 0;
	ps->contact = matAlloc(num_particles, num_particles);
	ps->saves_per_second = 10;
	ps->solution_RK_tolerance = 1e-2;
	ps->solution_RK_timeout = 10;
	ps->num_nearest = NUM_NEAREST;
	ps->cuboid_subdiv[0] = CUBOID_SUBDIV_X;
	ps->cuboid_subdiv[1] = CUBOID_SUBDIV_Y;
	ps->cuboid_subdiv[2] = CUBOID_SUBDIV_Z;
	ps->solution_maximal_step = MAXFLOAT;
	ps->binary_interaction_max_dist = MAXFLOAT;
	ps->connmatrix = connmatAlloc(num_particles);
	vpsInit(&ps->monroutines);
	ps->rt_timer = timerCreate();
	strCopy(ps->script_name,"[null]",_MAX_PATH);
	vpsInit(&ps->float_controllers);
	vpsInit(&ps->vec3_controllers);
	ps->random_seed = timeTickCount();
	ps->rhandle = ranCreateGenerator(ps->random_seed);
	ps->soft_particles = false;
	ps->sp_max_recursions = 32;
	ps->sp_repulsion_factor = 1;
	ps->isolated = isetAlloc(num_particles);
	ps->clusters = isetsAlloc(num_particles, num_particles);
	return ps;
}

void
psFree(particle_system_p ps)
{
	if(!ps)
		return;
	if(ps->num_particles && ps->phase && ps->final_filename[0])
	{
		u32_t i;
		char buf[_MAX_PATH];
		vhandle_t out;
		vector_p v;
		logFormatString(buf,"%s/final_state/%s.pos",ps->outdir,ps->final_filename);
		out = fileOpenWrite(buf);
		if(LIBV_INVALID_HANDLE != out)
		{
			for(i = 0;i < ps->num_particles;i++)
			{
				vec_p x = PS_POS(ps->phase, i);
				filePrintf(out,"%g\t%g\t%g\n",x[0],x[1],x[2]);
			}
			fileClose(out);
		}
		v = vecAlloc(ps->num_particles * PHASESPACE_DIM);
		memCopy(v->elements,ps->phase,ps->num_particles * PHASESPACE_DIM * sizeof(vec_t));
		logFormatString(buf,"%s/final_state/%s.vector",ps->outdir,ps->final_filename);
		out = fileOpenWrite(buf);
		if(LIBV_INVALID_HANDLE != out)
		{
			vecWrite(out,v);
			fileClose(out);
		}
		memFree(v);
	}
	if(LIBV_INVALID_HANDLE != ps->rt_timer)
		timerDestroy(ps->rt_timer);
	if(ps->particle_radius)
		memFree(ps->particle_radius);
	if(ps->particle_density)
		memFree(ps->particle_density);
	if(ps->particle_mass)
		memFree(ps->particle_mass);
	if(ps->particle_mass_delta)
		memFree(ps->particle_mass_delta);
	if(ps->particle_reciprocal_mass)
		memFree(ps->particle_reciprocal_mass);
	if(ps->particle_6_pi_eta_r)
		memFree(ps->particle_6_pi_eta_r);
	if(ps->phase)
		memFree(ps->phase);
	if(ps->dglue)
		memFree(ps->dglue);
	if(ps->percolation_distance)
		memFree(ps->percolation_distance);
	if(ps->force)
		memFree(ps->force);
	if(ps->contact)
		memFree(ps->contact);
	if(ps->dist)
		memFree(ps->dist);
	if(ps->surf_dist)
		memFree(ps->surf_dist);
	if(ps->sums)
		memFree(ps->sums);
	if(ps->mon_dist)
		memFree(ps->mon_dist);
	if(ps->connmatrix)
		memFree(ps->connmatrix);
	vpsClear(&ps->monroutines);
	if(LIBV_INVALID_HANDLE != ps->kinetics_outfile)
		fileClose(ps->kinetics_outfile);
	if(LIBV_INVALID_HANDLE != ps->rhandle)
		ranDestroyGenerator(ps->rhandle);
	clearControllersCache(ps);
	isetFree(ps->isolated);
	isetsFree(ps->clusters, ps->num_particles);
	memFree(ps);
}

V_LOCAL vboolean
psLoadDistribution(vhandle_t lex,vhandle_t rhandle,vec_p dst,u32_t numvals,vec_p refvals)
{
	CALLER_NAME(psLoadDistribution)
	u32_t i;
	vec_t farg1, farg2;
	if(lexNextFloat(lex,&farg1))
	{
		for(i = 0;i < numvals;i++)
			dst[i] = farg1;
	}
	else if(lexLastWas(lex,"uniform"))
	{
		if(!lexNextFloat(lex,&farg1) || !lexNextFloat(lex,&farg2))
		{
			SYN_EXPECTED(lex,"two floating point boundaries");
			goto err_exit;
		}
		vecsUniform(rhandle,dst,numvals,farg1,farg2);
	}
	else if(lexLastWas(lex,"gaussian"))
	{
		vector_p v;
		if(!lexNextFloat(lex,&farg1) || !lexNextFloat(lex,&farg2))
		{
			SYN_EXPECTED(lex,"floating point mean and stdev");
			goto err_exit;
		}
		v  = vecAlloc(numvals);
		ranNextGaussianVector(rhandle,v,farg1,farg2);
		memCopy(dst,v->elements,numvals * sizeof(vec_t));
		memFree(v);
	}
	else if(lexLastWas(lex,"dirac"))
	{
		if(!vecsDirac(rhandle,lex,dst,numvals))
			goto err_exit;
	}
	else if(lexLastWas(lex,"heaviside"))
	{
		if(!vecsHeaviside(rhandle,lex,dst,numvals))
			goto err_exit;
	}
	else if(lexLastWas(lex,"scale_by"))
	{
		if(!refvals)
		{
			PARSE_ERROR(lex,"scale_by is not allowed here");
			goto err_exit;
		}
		if(!lexNextFloat(lex,&farg1))
		{
			SYN_EXPECTED(lex,"floating point scale");
			goto err_exit;
		}
		for(i = 0;i < numvals;i++)
			dst[i] = refvals[i] * farg1;
	}
	else
	{
		PARSE_ERROR(lex,"unknown type of distribution");
		goto err_exit;
	}
	LEX_CHECK_SEMICOLON(lex);
	return true;
err_exit:;
	return false;
}

vboolean
psLoadBoundary(vhandle_t lex,particle_system_p ps)
{
	CALLER_NAME(psLoadBoundary)
	char *name = strDup(lexNext(lex));
	lexPush(lex,LEX_POSITION_BIT);
	if(!name)
		goto err_exit;
	LEX_CHECK_SEMICOLON(lex);
	lexSetInteger(lex,LEX_CURRENT_LINE,1);
	if(!lexSearchNamedEntry(lex,"boundary",name))
	{
		PARSE_ERROR_2ARG(lex,"unknown boundary: ",name);
		goto err_exit;
	}
	LEX_CHECK_NEXT(lex,open_brace);
	while(lexNext(lex))
	{
		if(lexLastWas(lex,close_brace))
		{
			memFree(name);
			lexPop(lex,LEX_POSITION_BIT);
			return true;
		}
		if(lexLastWas(lex,"plane"))
		{
			u32_t nbp = ps->num_boundary_planes;
			if(nbp == MAX_BOUNDARY_PLANES)
			{
				PARSE_ERROR(lex,"too many boundary planes");
				goto err_exit;
			}
			if(!vecsParse(lex,ps->boundary_plane[nbp],3))
			{
				SYN_EXPECTED(lex,"vec3 plane_normal");
				break;
			}
			if(!lexNextFloat(lex,&(ps->boundary_plane[nbp][3])))
			{
				SYN_EXPECTED(lex,"float plane_dist");
				break;
			}
			ps->plane_interaction[nbp] = fcGetById(lex,ps,lexNext(lex));
			if(!ps->plane_interaction[nbp])
				goto err_exit;
			vec3Unit(ps->boundary_plane[nbp],ps->boundary_plane[nbp]);
			ps->num_boundary_planes++;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,"sphere"))
		{
			u32_t nbs = ps->num_boundary_spheres;
			if(nbs == MAX_BOUNDARY_SPHERES)
			{
				PARSE_ERROR(lex,"too many boundary spheres");
				goto err_exit;
			}
			if(!vecsParse(lex,ps->boundary_sphere[nbs],3))
			{
				SYN_EXPECTED(lex,"vec3 center");
				break;
			}
			if(!lexNextFloat(lex,&(ps->boundary_sphere[nbs][3])))
			{
				SYN_EXPECTED(lex,"float radius");
				break;
			}
			ps->sphere_interaction[nbs] = fcGetById(lex,ps,lexNext(lex));
			if(!ps->sphere_interaction[nbs])
				goto err_exit;
			ps->num_boundary_spheres++;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		PARSE_ERROR(lex,"unknown type of boundary");
		goto err_exit;
	}
	SYN_UNEXPECTED_EOF(lex);
err_exit:;
	lexPop(lex,LEX_POSITION_BIT);
	if(name)
		memFree(name);
	return false;
}

vboolean
icondDistribute(vhandle_t lex,particle_system_p ps)
{
	CALLER_NAME(icondDistribute)
	char *name = strDup(lexNext(lex));
	vec_t min_dist = 0;
	vboolean min_dist_defined = false;
	vboolean got_pos = false;
	lexPush(lex,LEX_POSITION_BIT);
	if(!name)
		goto err_exit;
	LEX_CHECK_SEMICOLON(lex);
	lexSetInteger(lex,LEX_CURRENT_LINE,1);
	if(!lexSearchNamedEntry(lex,"domain",name))
	{
		PARSE_ERROR_2ARG(lex,"unknown domain: ",name);
		goto err_exit;
	}
	LEX_CHECK_NEXT(lex,open_brace);
	while(lexNext(lex))
	{
		if(lexLastWas(lex,close_brace))
		{
			memFree(name);
			lexPop(lex,LEX_POSITION_BIT);
			return got_pos;
		}
		if(lexLastWas(lex,"minimal_distance"))
		{
			if(min_dist_defined)
			{
				PARSE_ERROR(lex,"redefinition of minimal_distance");
				goto err_exit;
			}
			if(!lexNextFloat(lex,&min_dist))
			{
				SYN_EXPECTED(lex,"float minimal_distance");
				goto err_exit;
			}
			min_dist_defined = true;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,"box"))
		{
			vec3_t mins, maxs;
			if(!min_dist_defined)
			{
				PARSE_ERROR(lex,"minimal_distance must preceed volume definition");
				goto err_exit;
			}
			if(got_pos)
			{
				PARSE_ERROR(lex,"redefinition of domain");
				goto err_exit;
			}
			if(!vecsParse(lex,mins,3))
			{
				SYN_EXPECTED(lex,"vec3 box mins");
				goto err_exit;
			}
			if(!vecsParse(lex,maxs,3))
			{
				SYN_EXPECTED(lex,"vec3 box maxs");
				goto err_exit;
			}
			LEX_CHECK_SEMICOLON(lex);
			conlPrintf(LOG_INFO,"%s(): distributing particles in box...",caller_name);
			fflush(stdout);
			if(!distribute_in_box(ps->rhandle,mins,maxs,ps->num_particles,ps->particle_radius,min_dist,ps->phase,DS_TIMEOUT,DS_COVERAGE))
			{
				goto err_exit;
			}
			conPrintf(" done.\n");
			got_pos = true;
			continue;
		}
		if(lexLastWas(lex,"sphere"))
		{
			vec3_t center;
			vec_t radius;
			if(!min_dist_defined)
			{
				PARSE_ERROR(lex,"minimal_distance must preceed volume definition");
				goto err_exit;
			}
			if(got_pos)
			{
				PARSE_ERROR(lex,"redefinition of domain");
				goto err_exit;
			}
			if(!vecsParse(lex,center,3))
			{
				SYN_EXPECTED(lex,"vec3 center");
				goto err_exit;
			}
			if(!lexNextFloat(lex,&radius))
			{
				SYN_EXPECTED(lex,"float radius");
				goto err_exit;
			}
			LEX_CHECK_SEMICOLON(lex);
			conlPrintf(LOG_INFO,"%s(): distributing particles in sphere...",caller_name);
			fflush(stdout);
			if(!distribute_in_sphere(ps->rhandle,center,radius,ps->num_particles,ps->particle_radius,min_dist,ps->phase,DS_TIMEOUT,DS_COVERAGE))
			{
				goto err_exit;
			}
			conPrintf(" done.\n");
			got_pos = true;
			continue;
		}
		if(lexLastWas(lex,"cylinder"))
		{
			vec3_t center;
			vec_t radius, height;
			if(!min_dist_defined)
			{
				PARSE_ERROR(lex,"minimal_distance must preceed volume definition");
				goto err_exit;
			}
			if(got_pos)
			{
				PARSE_ERROR(lex,"redefinition of domain");
				goto err_exit;
			}
			if(!vecsParse(lex,center,3))
			{
				SYN_EXPECTED(lex,"vec3 center");
				goto err_exit;
			}
			if(!lexNextFloat(lex,&radius))
			{
				SYN_EXPECTED(lex,"float radius");
				goto err_exit;
			}
			if(!lexNextFloat(lex,&height))
			{
				SYN_EXPECTED(lex,"float height");
				goto err_exit;
			}
			LEX_CHECK_SEMICOLON(lex);
			conlPrintf(LOG_INFO,"%s(): distributing particles in cylinder...",caller_name);
			fflush(stdout);
			if(!distribute_in_cylinder(ps->rhandle,center,radius,height,ps->num_particles,ps->particle_radius,min_dist,ps->phase,DS_TIMEOUT,DS_COVERAGE))
			{
				goto err_exit;
			}
			conPrintf(" done.\n");
			got_pos = true;
			continue;
		}
		PARSE_ERROR(lex,"unknown element of domain");
		goto err_exit;
	}
	SYN_UNEXPECTED_EOF(lex);
err_exit:;
	lexPop(lex,LEX_POSITION_BIT);
	if(name)
		memFree(name);
	return false;
}

particle_system_p
psLoad(vhandle_t lex, char *target)
{
	CALLER_NAME(psLoad)
	time_t t;
	struct tm *tm;
	char id[_MAX_PATH];
	s32_t iarg;
	vec_t farg1;
	particle_system_p ps = NULL;
	vboolean got_radius = false, got_density = false, got_distribution_domain = false;
	conlPrintf(LOG_INFO,"%s(): loading particle system \"%s\".\n", caller_name, target);
	strCopy(id, target, _MAX_PATH);
	lexPush(lex,LEX_POSITION_BIT);
	lexSetInteger(lex,LEX_CURRENT_LINE,1);
	if(!lexSearchNamedEntry(lex, "particle_system", id))
	{
		PARSE_ERROR_2ARG(lex,"unable to find particle system ", id);
		goto err_exit;
	}
	if(!lexNextInteger(lex,&iarg) || iarg < 2)
	{
		SYN_EXPECTED(lex,"integer num_particles >= 2");
		goto err_exit;
	}
	conlPrintf(LOG_INFO,"%s(): particle system found, %d particles\n", caller_name, iarg);
	LEX_CHECK_NEXT(lex,open_brace);
	ps = psAlloc((u32_t)iarg);
	if(!ps)
	{
		conlPrintf(LOG_ERROR,"%s(): unable to allocate memory for particle system with %d particles.\n",
		   caller_name, iarg);
		goto err_exit;
	}
	strCopy(ps->name, id, _MAX_PATH);
	strCopy(ps->script_name, lexSourceName(lex), _MAX_PATH);
	t = time(NULL);
	tm = localtime(&t);
	strftime(ps->tmpname,_MAX_PATH,"%Y-%m-%d-%Hh-%Mm-%Ss",tm);
	logFormatString(ps->outdir,"%s-%s",ps->name,ps->tmpname);
	if(!nofilelog)
	{
		logFormatString(ps->tmpname,"%s/psmod.log",ps->outdir);
		logStartFileLog(ps->tmpname, true);
	}
	while(lexNext(lex))
	{
		if(lexLastWas(lex,"random_seed"))
		{
			if(!lexNextInteger(lex,&iarg) || iarg < 1)
			{
				SYN_EXPECTED(lex,"integer random_seed > 0");
				goto err_exit;
			}
			LEX_CHECK_SEMICOLON(lex);
			ps->random_seed = (u32_t)iarg;;
			ranDestroyGenerator(ps->rhandle);
			ps->rhandle = ranCreateGenerator(ps->random_seed);
			continue;
		}
		if(lexLastWas(lex,"radius"))
		{
			if(got_radius)
			{
				PARSE_ERROR(lex,"redefinition of radius");
				goto err_exit;
			}
			if(!psLoadDistribution(lex,ps->rhandle,ps->particle_radius,ps->num_particles,NULL))
				goto err_exit;
			got_radius = true;
			continue;
		}
		if(lexLastWas(lex,"density"))
		{
			if(got_density)
			{
				PARSE_ERROR(lex,"redefinition of density");
				goto err_exit;
			}
			if(!psLoadDistribution(lex,ps->rhandle,ps->particle_density,ps->num_particles,NULL))
				goto err_exit;
			got_density = true;
			continue;
		}
		if(lexLastWas(lex,"domain"))
		{
			if(got_distribution_domain)
			{
				PARSE_ERROR(lex,"redefinition of distribution domain");
				goto err_exit;
			}
			if(!got_radius)
			{
				PARSE_ERROR(lex,particle_radius_unknown);
				goto err_exit;
			}
			if(!icondDistribute(lex, ps))
				goto err_exit;
			got_distribution_domain = true;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,"shells"))
		{
			vboolean dglue_defined = false;
			vboolean pd_defined = false;
			vec_p revfals = got_radius ? ps->particle_radius : NULL;
			LEX_CHECK_NEXT(lex,open_brace);
			while(lexNext(lex))
			{
				if(lexLastWas(lex,close_brace))
					break;
				if(lexLastWas(lex,"tangent_forces"))
				{
					if(!lexNextBoolean(lex,&ps->tangent_forces))
						goto err_exit;
					LEX_CHECK_SEMICOLON(lex);
					continue;
				}
				if(lexLastWas(lex,"percolation"))
				{
					if(!lexNextBoolean(lex,&ps->percolation))
						goto err_exit;
					LEX_CHECK_SEMICOLON(lex);
					continue;
				}
				if(lexLastWas(lex,"shell_thickness"))
				{
					if(!psLoadDistribution(lex,ps->rhandle,ps->dglue,ps->num_particles,revfals))
						goto err_exit;
					dglue_defined = true;
					continue;
				}
				if(lexLastWas(lex,"percolation_distance"))
				{
					if(!psLoadDistribution(lex,ps->rhandle,ps->percolation_distance,ps->num_particles,revfals))
						goto err_exit;
					pd_defined = true;
					continue;
				}
				if(lexLastWas(lex,"shell_viscosity"))
				{
					if(!lexNextFloat(lex,&farg1))
					{
						SYN_EXPECTED(lex,"float shell_viscosity");
						break;
					}
					LEX_CHECK_SEMICOLON(lex);
					ps->hglue = farg1;
					continue;
				}
				PARSE_ERROR(lex,"unknown token");
				goto err_exit;
			}
			if(!lexLast(lex))
			{
				SYN_UNEXPECTED_EOF(lex);
				goto err_exit;
			}
			if(ps->percolation && !pd_defined)
			{
				PARSE_ERROR(lex,"ps->percolation == true, but percolation_distance is undefined");
				goto err_exit;
			}
			if(ps->tangent_forces && !dglue_defined)
			{
				PARSE_ERROR(lex,"ps->tangent_forces == true, but shell_thickness is undefined");
				goto err_exit;
			}
			continue;
		}
		if(lexLastWas(lex,"boundary"))
		{
			if(!psLoadBoundary(lex,ps))
				goto err_exit;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,"pairwise"))
		{
			if(ps->binary_interaction)
			{
				PARSE_ERROR(lex,"redefinition of binary interaction");
				goto err_exit;
			}
			ps->binary_interaction = fcGetById(lex,ps,lexNext(lex));
			if(!ps->binary_interaction)
				goto err_exit;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,"environment"))
		{
			LEX_CHECK_NEXT(lex,open_brace);
			while(lexNext(lex))
			{
				if(lexLastWas(lex,close_brace))
					break;
				if(lexLastWas(lex,"density"))
				{
					if(!lexNextFloat(lex,&ps->environment_density))
					{
						SYN_EXPECTED(lex,"floating point environment density");
						goto err_exit;
					}
					LEX_CHECK_SEMICOLON(lex);
					continue;
				}
				if(lexLastWas(lex,"viscosity"))
				{
					if(!lexNextFloat(lex,&ps->env_eta))
					{
						SYN_EXPECTED(lex,"floating point environment viscosity");
						goto err_exit;
					}
					LEX_CHECK_SEMICOLON(lex);
					continue;
				}
				if(lexLastWas(lex,"velocity"))
				{
					ps->env_velocity = vcGetById(lex,ps,lexNext(lex));
					if(!ps->env_velocity)
						goto err_exit;
					LEX_CHECK_SEMICOLON(lex);
					continue;
				}
				PARSE_ERROR(lex,"unknown token");
				goto err_exit;
			}
			if(!lexLast(lex))
			{
				SYN_UNEXPECTED_EOF(lex);
				goto err_exit;
			}
			continue;			
		}
		if(lexLastWas(lex,"unary_force"))
		{
			if(ps->unary_force)
			{
				PARSE_ERROR(lex,"redefinition of unary_force");
				goto err_exit;
			}
			ps->unary_force = vcGetById(lex,ps,lexNext(lex));
			if(!ps->unary_force)
				goto err_exit;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,"unary_accel"))
		{
			if(ps->unary_accel)
			{
				PARSE_ERROR(lex,"redefinition of unary_accel");
				goto err_exit;
			}
			ps->unary_accel = vcGetById(lex,ps,lexNext(lex));
			if(!ps->unary_accel)
				goto err_exit;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,"monitor"))
		{
			void_p ptr;
			monroutine_entry_t entry;
			char_p name = lexNext(lex);
			if(NULL == name)
			{
				SYN_EXPECTED(lex, "string monitor_routine_name");
				goto err_exit;
			}
			entry = monitorEntryForName(name);
			if(NULL == entry)
			{
				PARSE_ERROR(lex,"unknown name of monitor routine");
				goto err_exit;
			}
			FUNC2PTR(ptr, entry);
			vpsPush(&ps->monroutines, ptr);
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,"load_phase")) /* FIXME not working properly */
		{
			vhandle_t in;
			u32_t i;
			if(!lexNext(lex))
			{
				SYN_UNEXPECTED_EOF(lex);
				goto err_exit;
			}
			strCopy(ps->initial_filename, lexLast(lex), _MAX_PATH);
			in = lexParseFile(ps->initial_filename,"");
			if(LIBV_INVALID_HANDLE != in)
			{
				lexEnable(lex,LEX_ONE_CHAR_COMMENTS);
				lexSetInteger(lex,LEX_COMMENT_CHAR,'#');
				for(i = 0;i < ps->num_particles;i++)
				{
					vec_p v = PS_POS(ps->phase, i);
					if(lexNextFloat(in,v) && lexNextFloat(in,v + 1) && lexNextFloat(in,v + 2))
						continue;
					SYN_EXPECTED(in,"float");
					lexDestroy(in);
					break;
				}
			}
			lexDestroy(in);
			got_distribution_domain = true;
			LEX_CHECK_SEMICOLON(lex);
			continue;
		}
		if(lexLastWas(lex,close_brace))
			break;
		PARSE_ERROR(lex,"unknown token");
		goto err_exit;
	}
	if(!lexNext(lex))
	{
		SYN_UNEXPECTED_EOF(lex);
		goto err_exit;
	}
	if(got_radius && got_distribution_domain && got_density)
	{
		/* setup reciprocal masses and viscousity coefficients */
		u32_t i;
		vec_t r, k1 = (vec_t)4.0 / (vec_t)3.0 * M_PI, vol;
		vec_t k2 = 6 * M_PI * ps->env_eta;
		for(i = 0;i < ps->num_particles;i++)
		{
			r = ps->particle_radius[i];
			if(r < 1e-16) /* smaller than 0.1fm */
			{
				PARSE_ERROR(lex,"particle(s) radius too small");
				goto err_exit;
			}
			vol = k1 * r * r * r;
			ps->particle_mass[i] = vol * ps->particle_density[i];
			ps->particle_mass_delta[i] = vol * (ps->particle_density[i] - ps->environment_density);
			ps->particle_reciprocal_mass[i] = (vec_t)1.0 / ps->particle_mass[i];
			ps->particle_6_pi_eta_r[i] = k2 * ps->particle_radius[i];
		}
		ps->cur_phase = ps->phase;
		if(!ps->binary_interaction)
			WARNING_GENERIC("no binary interaction defined.\n");
		lexPop(lex,LEX_POSITION_BIT);
		return ps;
	}
err_exit:;
	if(ps)
	{
		ps->final_filename[0] = 0;
		psFree(ps);
	}
	lexPop(lex,LEX_POSITION_BIT);
	return NULL;
}
