/**
 *  \file segmentation.c
 *  \brief This module makes an image segmentation based on threshold.
 *
 * segmentation.c
 *
 *
 *  \ingroup corefiles
 *  \date Jun 3, 2009
 *  \author Alexander Pimenov
 */

#include "g12buffer.h"
#include "datatypes.h"
#include "global.h"
#include "segmentation.h"

#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#ifdef TRACE
#include <stdio.h>
#endif

VECTOR_GENERIC(Segment);

static void addPointToSegment (segmentList segments, int index, int x, int y, double weight)
{
	if (index >= segments->size)
		printf("TROUBLE");

	Segment *seg = vectorElement_Segment(segments, index);
	seg->sumy += y;
	seg->sumx += x;
	seg->count++;
}

static int addSegment (segmentList segments, int x, int y, double weight)
{
	Segment *seg = vectorNewElement_Segment(segments);
	seg->parent = NO_PARENT;
	seg->sumy = y;
	seg->sumx = x;
	seg->count = 1;
	uint32_t newIndex = vectorGetSize_Segment(segments);
	return newIndex - 1;
}

static int getSegmentParent (segmentList segments, int index)
{
	if (index >= segments->size)
		printf("TROUBLE");
	int id = vectorElement_Segment(segments, index)->parent;
	return id;
}

static void markMergeSegments (segmentList segments, int index1, int index2 )
{
	while (getSegmentParent(segments, index1) != NO_PARENT)
		index1 = getSegmentParent(segments, index1);

	while (getSegmentParent(segments, index2) != NO_PARENT)
		index2 = getSegmentParent(segments, index2);

	if (index1 != index2)
		vectorElement_Segment(segments, index1)->parent = index2;

}

static int dadifySegments (segmentList segments)
{
	int resultnumber = 0;
	int i;
	for (i = 0; i < vectorGetSize_Segment(segments); i++)
	{
		int topParent = i;
		int result = -1;
		while (topParent != NO_PARENT)
		{
			result = topParent;
			topParent = getSegmentParent(segments,topParent);
		}
		if (result == i)
			resultnumber++;
		if (result != i && result != NO_PARENT)
		{
			Segment *seg1 = vectorElement_Segment(segments, result);
			Segment *seg2 = vectorElement_Segment(segments, i);
			seg1->count += seg2->count;
			seg1->sumx += seg2->sumx;
			seg1->sumy += seg2->sumy;
			seg2->parent = result;
		}

	}
	return resultnumber;
}

int childRemoveFunction (Segment *data)
{
	return (data->parent == NO_PARENT);
}

/**
 *  This function does the simple segmentation based no the threshold. For every area in the picture that is
 *  not 8 way connected to other several statistics are computed.
 *
 *  NB don't forget to free the resulting segment list.
 */

segmentList getPoints(G12Buffer *buf, int threshold)
{
	int h = buf->h;
	int w = buf->w;

	int i,j;
	uint32_t *markup = (uint32_t *) calloc(w * h, sizeof(uint32_t));
	int list[4];
	segmentList segments = vectorAlloc_Segment(10);

	for (i = 0; i < h; i ++)
	{
		for (j = 0; j < w; j++)
		{
			int count = 0;
			if (buf->data[i * w + j] > threshold)
			{
				if (i > 0 && j > 0 && markup[(i - 1) * w + (j - 1)] != 0 )
					list[count++] = markup[(i - 1) * w + (j - 1)];

				if (i > 0 && markup[(i - 1) * w + j] != 0 )
					list[count++] = markup[(i - 1) * w + j];

				if (i > 0 && j < w - 1 && markup[(i - 1) * w + (j + 1)] != 0 )
					list[count++] = markup[(i - 1) * w + (j + 1)];

				if (j > 0 && markup[i * w + (j - 1)])
					list[count++] = markup[i * w + (j - 1)];

				if (count == 0)
				{
					markup[i * w + j] = addSegment(segments, j, i, 1.0);
				}
				if (count >=1 )
				{
					markup[i * w + j] = list[0];
					addPointToSegment(segments, list[0], j,i, 1.0);
				}
				if (count >= 2)
				{
					markMergeSegments(segments, list[0], list[1]);
				}
				if (count >= 3)
				{
					markMergeSegments(segments, list[0], list[2]);
				}
			}
		}
	}

	/*Now collect all info about the children to the parents*/
	int resultnumber = dadifySegments(segments);

	vectorFilter(segments, childRemoveFunction);
	free(markup);

	return segments;
}

segmentList filterCloseSegments(segmentList segments, int distance)
{
	int i,j;
	for (i = 0; i < vectorGetSize_Segment(segments); i++ )
	{
		Segment *acceptor = vectorElement_Segment(segments,i);
		for (j = i + 1; j < vectorGetSize_Segment(segments); j++)
		{
			Segment *donor = vectorElement_Segment(segments,j);
			int centerdx = (acceptor->sumx / acceptor->count) - (donor->sumx / donor->count);
			int centerdy = (acceptor->sumy / acceptor->count) - (donor->sumy / donor->count);
			if (centerdx * centerdx + centerdy * centerdy < distance * distance)
			{
				markMergeSegments(segments, i,j);
			}
		}
	}

	dadifySegments(segments);
	vectorFilter(segments, childRemoveFunction);

	return segments;
}
