/*
 * FIXME
 */

#define	NUM_NEAREST		4

#define	SUBDIVX	3
#define	SUBDIVY	3
#define	SUBDIVZ	3

vhandle_t out_av_and_dev;

void hack_init(void)
{
	static char *head = "Time;R4av;R4std;Nnav;Nnstd;Nsav;Nsstd;V2av;V2std;Sav;Sstd;Zav;Zstd";
	out_av_and_dev = fileOpenWrite("out/av_and_dev.csv");
	filePuts(out_av_and_dev, head);
}

void hack_done(void)
{
	fileClose(out_av_and_dev);
}

void print_float(vhandle_t out,vec_t val)
{
	char str[256], *ptr;
	sprintf(str,"%g",val);
	for(ptr=str;*ptr;ptr++)
		if(*ptr == '.')
			*ptr = ',';
	fileWrite(out,str,ptr-str);
}

void av_and_std_dev(vector_p X,u32_t len)
{
	u32_t i;
	vec_t av, std_dev, d;
	av = 0;
	for(i=0;i<len;i++)
		av += X->elements[i];
	av /= len;
	std_dev = 0;
	for(i=0;i<len;i++)
	{
		d = X->elements[i] - av;
		std_dev += d * d;
	}
	std_dev = sqrt(std_dev / (vec_t)(len-1));
	print_float(out_av_and_dev,av);
	fileWrite(out_av_and_dev,";",1);
	print_float(out_av_and_dev,std_dev);
}

void find_mins(matrix_p src)
{
	vec_t temp;
	u32_t r, c, k, idx;
	for(r=0;r<src->rows;r++)
	{
		for(k=0;k<NUM_NEAREST;k++)
		{
			idx = k;
			for(c=k+1;c<src->cols;c++)
				if(src->cell[r][c] < src->cell[r][idx])
					idx = c;
			temp = src->cell[r][idx];
			src->cell[r][idx] = src->cell[r][k];
			src->cell[r][k] = temp;
		}
	}
}

void av_nearset_dists(particle_system_p ps)
{
	u32_t i, j, count;
	vec3_t delta;
	vec_t s;
	matrix_p dists;
	vector_p sums;
	dists = matAlloc(ps->num_particles,ps->num_particles);
	sums = vecAlloc(ps->num_particles);
	/* compute all distances */
	for(i=0;i<ps->num_particles;i++)
	{
		/* fixme: as today, this was already done in main code */
		dists->cell[i][i] = MAXFLOAT;
		for(j=i+1;j<ps->num_particles;j++)
		{
			vec3Sub(delta,PS_POS(ps->cur_phase,i),PS_POS(ps->cur_phase,j));
			dists->cell[i][j] = dists->cell[j][i] = 
				vec3Abs(delta) - ps->particle_radius[i] - ps->particle_radius[j];
		}
	}
	/* move 1st NUM_NEAREST minimals to 1st cols */
	find_mins(dists);
	/* compute average by particles */
	for(i=0;i<ps->num_particles;i++)
	{
		s = 0;
		for(j=0;j<NUM_NEAREST;j++)
			s += dists->cell[i][j];
		s /= NUM_NEAREST;
		sums->elements[i] = s;
	}
	av_and_std_dev(sums,ps->num_particles);
	fileWrite(out_av_and_dev,";",1);
	/* and by neighbours */
/*
	for(j=0;j<NUM_NEAREST;j++)
	{
		s = 0;
		for(i=0;i<ps->num_particles;i++)
			s += dists->cell[i][j];
		s /= ps->num_particles;
		sums->elements[j] = s;
	}
	av_and_std_dev(sums,NUM_NEAREST);
	fileWrite(out_av_and_dev,";",1);
*/
	/* compute number of particles that are closer to current than radius of the particle itself */
	for(i=0;i<ps->num_particles;i++)
	{
		vec_t ref = ps->particle_radius[i]; /* former: 2 * ps->glue */
		count = 0;
		for(j=0;j<ps->num_particles;j++)
			if(dists->cell[i][j] < ref)
				count++;
		sums->elements[i] = count;
	}
	av_and_std_dev(sums,ps->num_particles);
	memFree(dists);
	memFree(sums);
}

void v2_av(particle_system_p ps)
{
	u32_t i;
	vector_p sums;
	sums = vecAlloc(ps->num_particles);
	for(i=0;i<ps->num_particles;i++)
		sums->elements[i] = vec3Dot(PS_VEL(ps->cur_phase,i),PS_VEL(ps->cur_phase,i));
	av_and_std_dev(sums,ps->num_particles);
	memFree(sums);
}

void S_av(particle_system_p ps)
{
	u32_t i, j;
	vector_p sums;
	sums = vecAlloc(ps->num_particles);
	for(i=0;i < ps->num_particles;i++)
	{
		sums->elements[i] = 0;
		for(j=0;j < ps->num_particles;j++)
		{
			if(i == j)
				continue;
			sums->elements[i] += ps->contact->cell[i][j];
		}
		sums->elements[i] /= 4 * M_PI * ps->particle_radius[i];
	}
	av_and_std_dev(sums,ps->num_particles);
	memFree(sums);
}

void Z_av(particle_system_p ps)
{
	u32_t i;
	vector_p Z;
	Z = vecAlloc(ps->num_particles);
	for(i=0;i < ps->num_particles;i++)
	{
		Z->elements[i] = PS_POS(ps->cur_phase,i)[2];
	}
	av_and_std_dev(Z,ps->num_particles);
	memFree(Z);
}

vboolean pt_inside(vec_p pt,vec3_t mins,vec3_t maxs)
{
	int i;
	for(i=0;i<3;i++)
		if(pt[i] < mins[i] || pt[i] > maxs[i])
			return false;
	return true;
}

void h_coeff(particle_system_p ps)
{
	vector_p sums;
	vec_p phase = ps->cur_phase;
	unsigned i, j, k, l, m, count;
	unsigned maxc = SUBDIVX * SUBDIVY * SUBDIVZ;
	vec3_t mins, maxs, ext, dim, p1, p2;
	sums = vecAlloc(maxc);
	bboxClear(mins,maxs);
	for(i=0;i<ps->num_particles;i++)
		bboxAddPoint(mins,maxs,PS_POS(phase,i));
	vec3Sub(ext,maxs,mins);
	dim[0] = ext[0] / SUBDIVX;
	dim[1] = ext[1] / SUBDIVY;
	dim[2] = ext[2] / SUBDIVZ;
	count = 0;
	for(i=0;i<SUBDIVX;i++)
		for(j=0;j<SUBDIVY;j++)
		{
			for(k=0;k<SUBDIVZ;k++)
			{
				p1[0] = mins[0] + dim[0] * i;
				p1[1] = mins[1] + dim[1] * j;
				p1[2] = mins[2] + dim[2] * k;
				vec3Add(p2,p1,dim);
				m = 0;
				for(l=0;l<ps->num_particles;l++)
					if(pt_inside(PS_POS(phase,l),p1,p2))
						m++;
				sums->elements[count] = m;
				count++;
			}
		}
	av_and_std_dev(sums,maxc);
	memFree(sums);
}

/*
#define	DELTA_T		0.1
vec_t last_time = -DELTA_T;
*/

u32_t ms_skipcount = 100000;
u32_t global_skipcount = 100000;

extern vboolean hack_draw;
extern void ms_save_frame_hack(particle_system_p ps);

vboolean monitor_extra(particle_system_p ps)
{
/*
	if((ps->cur_time - last_time) < DELTA_T)
		return true;
	last_time = ps->cur_time;
*/
	global_skipcount++;
	if(global_skipcount >= ps->global_skips)
	{
		global_skipcount = 0;
		print_float(out_av_and_dev, ps->cur_time);
		fileWrite(out_av_and_dev,";",1);
		av_nearset_dists(ps);
		fileWrite(out_av_and_dev,";",1);
		h_coeff(ps);
		fileWrite(out_av_and_dev,";",1);
		v2_av(ps);
		fileWrite(out_av_and_dev,";",1);
		S_av(ps);
		fileWrite(out_av_and_dev,";",1);
		Z_av(ps);
		fileWrite(out_av_and_dev,"\n",1);
		/* printf("Time: %g\n",ps->cur_time); */
		ms_skipcount++;
		if(ms_skipcount >= ps->ms_draw_skips)
		{
			ms_skipcount = 0;
			hack_draw = true; /* FIXME */
			ms_save_frame_hack(ps);
		}
	}
	return true;
}

/*
 * end of hack
 */
