#include <math.h>
#include <v_lib.h>

#define	ROWS	64
#define	COLS	64

static void matrix_minmax(matrix_p m,vec_t *min,vec_t *max)
{
	u32_t r, c;
	matMin(m,&r,&c);
	*min = m->cell[r][c];
	matMax(m,&r,&c);
	*max = m->cell[r][c];
}

void save_grd(matrix_p m,char *fname)
{
	u32_t i, j;
	vec3_t mins = {0,0,0}, maxs = {50,50,1};
	vhandle_t out = fileOpenWrite(fname);
	filePuts(out,"DSAA\n");
	filePrintf(out,"%d %d\n",m->rows,m->cols);
	matrix_minmax(m, mins+2, maxs+2);
	for(i=0;i<3;i++)
		filePrintf(out,"%g %g\n",mins[i],maxs[i]);
	for(i=0;i<m->rows;i++)
	{
		for(j=0;j<m->cols;j++)
			filePrintf(out,"%g ",m->cell[i][j]);
		filePuts(out,"\n");
	}
	fileClose(out);
}

void save(matrix_p m,char *fname)
{
	unsigned addr, dr, r, c;
	vec_t scale, min, max;
	u8_p buf = memAlloc(COLS * ROWS * 4);
	matrix_minmax(m, &min, &max);
	if(max > min)
	{
		scale = 255 / (max - min);
	}
	else
	{
		scale = 255;
		min = 0;
	}
	for(r=0;r<m->rows;r++)
	{
		dr = r * COLS;
		for(c=0;c<m->cols;c++)
		{
			addr = (dr + c) << 2;
			buf[addr] = (u8_t)((m->cell[r][c] - min) * scale);
			buf[addr+1] = buf[addr];
			buf[addr+2] = buf[addr];
		}
	}
	bmpSave(fname, ROWS, COLS, buf);
	memFree(buf);
}

#define	I0		2

void step(matrix_p m, matrix_p next, matrix_p Mu, matrix_p C)
{
	u32_t r, c;
	vec_t dx, dy;
	vec_t dI;
	static vec_t I[1024];
	V_ASSERT(m->cols < 1024);
	dx = 1.0 / m->cols;
	dy = 1.0 / m->rows;
	/* boundary nodes */
	for(c = 0;c < m->cols;c++)
	{
		next->cell[0][c] = m->cell[0][c];
		next->cell[m->rows-1][c] = m->cell[m->rows-1][c];
		I[c] = I0;
	}
	for(r = 0;r < m->rows;r++)
	{
		next->cell[r][0] = m->cell[r][0];
		next->cell[r][m->cols-1] = m->cell[r][m->cols-1];
	}
	/* inner nodes */
	for(r = 1;r < m->rows - 1;r++)
	{
		for(c = 1;c < m->cols - 1;c++)
		{
			dI = I[c] * Mu->cell[r][c] * dy;
			next->cell[r][c] = 0.25 *
				C->cell[r][c] * (m->cell[r-1][c] + m->cell[r+1][c] + 
				 m->cell[r][c-1] + m->cell[r][c+1]) + dI;
			I[c] -= dI;
		}
	}
}

vec_t maxdiff(matrix_p m1, matrix_p m2)
{
	vec_t d, md = 0;
	u32_t r, c;
	for(r = 0;r < m1->rows;r++)
		for(c = 0;c < m1->cols;c++)
		{
			d = v_abs(m1->cell[r][c] - m2->cell[r][c]);
			if(d > md)
				md = d;
		}
	return md;
}

matrix_p prev, next, Mu, C;

vec2_t center = {0.5,0.5};
vec_t R = 0.2;

void mu_c()
{
	u32_t r, c;
	vec2_t p, pos;
	Mu = matAlloc(ROWS, COLS);
	C = matAlloc(ROWS, COLS);
	for(r = 0;r < Mu->rows;r++)
	{
		pos[0] = (vec_t)r / Mu->rows;
		for(c = 0;c < Mu->cols;c++)
		{
			pos[1] = (vec_t)c / Mu->cols;
			vec2Sub(p, pos, center);
			if(vec2Dot(p, p) < R * R)
			{
				Mu->cell[r][c] = 1.5; /* TF */
				C->cell[r][c] = 1;
			}
			else
			{
				Mu->cell[r][c] = 1; /* epoxy */
				C->cell[r][c] = 1;
			}
		}
	}
}

void initial(matrix_p m)
{
	u32_t r, c;
	for(r = 0;r < m->rows;r++)
		for(c = 0;c < m->cols;c++)
			m->cell[r][c] = 20;
	/*
	for(c = 0;c < m->cols;c++)
		m->cell[0][c] = 100;
	*/
	mu_c();
}

#define SKIP	10

int main(int argc, char *argv[])
{   
	char fn[256];
	u32_t i;
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);

	prev = matAlloc(ROWS, COLS);
	next = matAlloc(ROWS, COLS);
	
	initial(prev);
	
	for(i = 0;i < 1000;i++)
	{
		if(!(i % SKIP))
		{
			sprintf(fn,"out/f%04u.grd",i);
			save_grd(prev,fn);
			sprintf(fn,"out/f%04u.bmp",i);
			save(prev,fn);
		}
		step(prev, next, Mu, C);
		matCopy(prev, next);
	}
//	save_grd(next,"out.grd");
//	save(Mu,"Mu.bmp");
	return 0;
}
