#include <v_lib.h>

u32_t width, height;
u32_t total, ndivs;

vec_t min_rgb[3];
vec_t min_hsv[3];

vec_t max_rgb[3];
vec_t max_hsv[3];

vec_t mean_rgb[3];
vec_t mean_hsv[3];

vec3_t *rgb_bounds;
vec3_t *hsv_bounds;

vec3_t *rgb_freq;
vec3_t *hsv_freq;

static void vec3ToHistogram(vec3_t *freqptr,vec3_t *bndptr,vec3_t vec,u32_t nd,vec3_t hdelta)
{
	vec_t val, min, max;
	u32_t ino, c, in_between;
	for(c=0;c<3;c++)
	{
		val = vec[c];
		in_between = 0;
		for(ino=1;ino<=nd;ino++)
		{
			min = bndptr[ino-1][c];
			max = bndptr[ino][c];
			if(min >= val && val < max)
			{
				freqptr[ino-1][c] += hdelta[c];
				in_between = 1;
				break;
			}
		}
		if(!in_between) /* we shouldn't loose the last sample */
			freqptr[nd-1][c] += hdelta[c];
	}
}

static char_p v_ftoa(vec_t v)
{
	static char buf[128], *ptr;
	sprintf(buf,"%g",v);
	/*
	for(ptr=buf;*ptr;ptr++)
		if(*ptr == '.')
			*ptr = ',';
	*/
	return buf;
}

static void outvec3(FILE *out,vec3_t v)
{
	fprintf(out,"%s\t",v_ftoa(v[0]));
	fprintf(out,"%s\t",v_ftoa(v[1]));
	fprintf(out,"%s\n",v_ftoa(v[2]));
}
	
int main(int argc,char *argv[])
{
	u32_t x,y;
	u8_t *src;
	vec3_t rgb, hsv, rgb_delta, hsv_delta, rgb_hdelta, hsv_hdelta;
	vec_t rtotal, rndivs;
	u8_t *pixels;
	vboolean verbose;
	/*
	 * Stage I: loading
	 */
	if(argc < 2)
		return -1;
	verbose = (argc == 3 && !strcasecmp(argv[2],"-q")) ? false : true;
	if(verbose)
	{
		logStartConsoleLog();
		logUpperLevel(LOG_TRACE);
	}
	pixels = rgbaLoad(argv[1],&width,&height);
	if(!pixels)
		return -1;
	total = width * height;
	if(total < 100)
	{
		memFree(pixels);
		return -1;
	}
	ndivs = (u32_t)floor(1 + 3.3 * log10(total));
	rtotal = (vec_t)1.0/total;
	rndivs = (vec_t)1.0/ndivs;
	rgb_bounds = (vec3_t*)memAlloc((ndivs+1)*sizeof(vec3_t));
	hsv_bounds = (vec3_t*)memAlloc((ndivs+1)*sizeof(vec3_t));
	rgb_freq = (vec3_t*)memAllocZ(ndivs*sizeof(vec3_t));
	hsv_freq = (vec3_t*)memAllocZ(ndivs*sizeof(vec3_t));
	/*
	 * Stage II: processing
	 */
	/* pass 1: find minimal and maximal values */
	bboxClear(min_rgb,max_rgb);
	bboxClear(min_hsv,max_hsv);
	vec3Zero(mean_rgb);
	vec3Zero(mean_hsv);
	for(y=0;y<height;y++)
	{
		src = pixels + ((y * width) << 2);
		for(x=0;x<width;x++)
		{
			rgbFromByte(rgb,src);
			hsvFromRgb(hsv,rgb);
			bboxAddPoint(min_rgb,max_rgb,rgb);
			bboxAddPoint(min_hsv,max_hsv,hsv);
			vec3Add(mean_rgb,mean_rgb,rgb);
			vec3Add(mean_hsv,mean_hsv,hsv);
			src+=4;
		}
	}
	vec3Scale(mean_rgb,mean_rgb,rtotal);
	vec3Scale(mean_hsv,mean_hsv,rtotal);
	vec3Sub(rgb_delta,max_rgb,min_rgb);
	vec3Sub(hsv_delta,max_hsv,min_hsv);
	vec3Scale(rgb_delta,rgb_delta,rndivs);
	vec3Scale(hsv_delta,hsv_delta,rndivs);
	vec3Copy(rgb_bounds[0],min_rgb);
	vec3Copy(hsv_bounds[0],min_hsv);
	for(x=1;x<=ndivs;x++)
	{
		vec3Add(rgb_bounds[x],rgb_bounds[x-1],rgb_delta);
		vec3Add(hsv_bounds[x],hsv_bounds[x-1],hsv_delta);
	}
	/* pass 2: histogram */
	for(x=0;x<3;x++)
	{
		rgb_hdelta[x] = rtotal; // rgb_delta[x];
		hsv_hdelta[x] = rtotal; // hsv_delta[x];
	}
	for(y=0;y<height;y++)
	{
		src = pixels + ((y * width) << 2);
		for(x=0;x<width;x++)
		{
			rgbFromByte(rgb,src);
			hsvFromRgb(hsv,rgb);
			vec3ToHistogram(rgb_freq,rgb_bounds,rgb,ndivs,rgb_hdelta);
			vec3ToHistogram(hsv_freq,hsv_bounds,hsv,ndivs,hsv_hdelta);
			src+=4;
		}
	}
	memFree(pixels);
	pixels=NULL;
	/*
	 * Stage III: output
	 */
	if(verbose)
		printf("Mean RGB values: ");
	outvec3(stdout,mean_rgb);
	if(verbose)
		printf("Mean HSV values: ");
	outvec3(stdout,mean_hsv);
	if(verbose)
		printf("Number of divisions: ");
	printf("%u\n",ndivs);
	if(verbose)
		printf("RGB bounds:\n");
	for(x=0;x<=ndivs;x++)
		outvec3(stdout,rgb_bounds[x]);
	if(verbose)
		printf("RGB frequencies:\n");
	for(x=0;x<ndivs;x++)
		outvec3(stdout,rgb_freq[x]);
	if(verbose)
		printf("HSV bounds:\n");
	for(x=0;x<=ndivs;x++)
		outvec3(stdout,hsv_bounds[x]);
	if(verbose)
		printf("HSV frequencies:\n");
	for(x=0;x<ndivs;x++)
		outvec3(stdout,hsv_freq[x]);
	/*
	 * Done
	 */
	memFree(rgb_bounds);
	memFree(hsv_bounds);
	memFree(rgb_freq);
	memFree(hsv_freq);
	return 0;
}
