#include <../source/TAL/objbase.h>

/*
 * Bilinear-interpolated matrix resampling
 */

typedef struct fpm_resample_context_s
{	
	libv_object_t	base;
	/* dimensions of source and destination */
	u32_t	old_rows, new_rows;
	u32_t	old_cols, new_cols;
	/* pointers to source and destrination */
	matrix_p	src, dst;
	/* fpm_upsample */
	u32_t		upsample_width, upsample_height;
	matrix_p	fpm_upsample_pass1;
	matrix_p	fpm_upsample_pass2;
	/* fpm_downsample */
	u32_t		pixel_width, pixel_height;
	matrix_p	fpm_downsample_dst;
	void		(*transform)(struct fpm_resample_context_s *self,
		matrix_p dst,matrix_p src);
}	fpm_resample_context_t, *fpm_resample_context_p;

#ifdef	CHECKED_BUILD
#define	RSCT_MAGIC	0x52534354		/* RSCT */
static	char 		*rsct_name = "bilinear resampling context";	/* object name */
#endif	/* CHECKED_BUILD */

V_LOCAL void
fpv_upsample_row(vec_t dst[],u32_t dst_len,vec_t src[],u32_t src_len)
{
	u32_t dp, sp;
	vec_t s1, s2, t = 0;
	u32_t scale_n = src_len - 1;
	u32_t scale_d = dst_len - 1;
	vec_t scale = (vec_t)scale_n / (vec_t)scale_d;
	for(dp = 0,sp = 1;sp < src_len;sp++)
	{
		s1 = src[sp-1];
		s2 = src[sp];
		while(dp * scale_n <= sp * scale_d)
		{
			dst[dp++] = s1 * (1 - t) + s2 * t;
			t += scale;
		}
		t = t - 1;
	}
}

V_LOCAL void
fpv_upsample_col(vec_p dst[],u32_t dst_len,u32_t dst_col,
	vec_p src[],u32_t src_len,u32_t src_col)
{
	u32_t dp, sp;
	vec_t s1, s2, t = 0;
	u32_t scale_n = src_len - 1;
	u32_t scale_d = dst_len - 1;
	vec_t scale = (vec_t)scale_n / (vec_t)scale_d;
	for(dp = 0,sp = 1;sp < src_len;sp++)
	{
		s1 = src[sp-1][src_col];
		s2 = src[sp][src_col];
		while(dp * scale_n <= sp * scale_d)
		{
			dst[dp++][dst_col] = s1 * (1 - t) + s2 * t;
			t += scale;
		}
		t = t - 1;
	}
}

V_LOCAL	void
fpm_upsample(fpm_resample_context_p self)
{
	u32_t r, c;
	matrix_p dst, tmp, src;
	src = self->src;
	tmp = self->fpm_upsample_pass1;
	dst = self->dst;
	V_ASSERT(tmp->rows == src->rows && tmp->cols == self->upsample_width);
	V_ASSERT(dst->rows == self->upsample_height && dst->cols == self->upsample_width);
	/* increase width up to 'upsample_width' */
	for(r = 0;r < tmp->rows;r++)
		fpv_upsample_row(tmp->cell[r],tmp->cols,src->cell[r],src->cols);
	/* increase height up to 'upsample_height' */
	for(c = 0;c < dst->cols;c++)
		fpv_upsample_col(dst->cell,dst->rows,c,tmp->cell,tmp->rows,c);
}

V_LOCAL	void
fpm_downsample(fpm_resample_context_p self)
{
	matrix_p dst = self->dst;
	matrix_p src = self->src;
	u32_t sc, r, sr, srb, srl, pixel_width = self->pixel_width;
	vec_t sum, scale = 1 / (vec_t)(self->pixel_width * self->pixel_height);
	vec_p sptr, end_sptr, dptr, end_dptr;
	V_ASSERT(dst->rows * self->pixel_height == src->rows);
	V_ASSERT(dst->cols * self->pixel_width == src->cols);
	for(srb = r = 0;r < dst->rows;r++)
	{
		dptr = dst->cell[r];
		end_dptr = dptr + dst->cols;
		srl = srb + self->pixel_height;
		for(sc = 0;dptr < end_dptr;dptr++,sc += pixel_width)
		{
			sum = 0;
			for(sr = srb;sr < srl;sr++)
			{
				sptr = src->cell[sr] + sc;
				end_sptr = sptr + pixel_width;
				for(;sptr < end_sptr;sptr++)
					sum += *sptr;
			}
			*dptr = sum * scale;
		}
		srb = srl;
	}
}

V_LOCAL	void
fpm_resample_upsample_pass(fpm_resample_context_p self,
	matrix_p dst,matrix_p src)
{
	self->src = src;
	self->dst = dst;
	fpm_upsample(self);
	self->src = self->dst = NULL;
}

V_LOCAL	void
fpm_resample_downsample_pass(fpm_resample_context_p self,
	matrix_p dst,matrix_p src)
{
	self->src = src;
	self->dst = dst;
	fpm_downsample(self);
	self->src = self->dst = NULL;
}

V_LOCAL	void
fpm_resample_two_pass(fpm_resample_context_p self,
	matrix_p dst,matrix_p src)
{
	self->src = src;
	self->dst = self->fpm_upsample_pass2;
	fpm_upsample(self);
	self->src = self->dst;
	self->dst = dst;
	fpm_downsample(self);
	self->src = self->dst = NULL;
}

#include <../source/PAL/argsused.h>
V_LOCAL	void
fpm_resample_no_op(fpm_resample_context_p self,
	matrix_p dst,matrix_p src)
{
	matCopy(dst,src);
}

V_LOCAL u32_t	V_CALL
fpm_resample_destroy(vhandle_t handle)
{
	fpm_resample_context_p self = (fpm_resample_context_p)handle;
	UNREF_EXIT(self);
	if(self->fpm_upsample_pass1)
		memFree(self->fpm_upsample_pass1);
	if(self->fpm_upsample_pass2)
		memFree(self->fpm_upsample_pass2);
	memFreeObject(self);
	return 0;
}

vhandle_t	V_GLOBAL
matCreateResampleContext(u32_t old_rows,u32_t old_cols,
	u32_t new_rows,u32_t new_cols)
{
	fpm_resample_context_p self;
	vboolean need_upsample = false, need_downsample = false;
	u32_t pixel_width, pixel_height;
	ALLOC_OBJECT(self,fpm_resample_context_t,0,RSCT_MAGIC);
	self->base.destroy = fpm_resample_destroy;
	self->old_rows = old_rows;
	self->old_cols = old_cols;
	self->new_rows = new_rows;
	self->new_cols = new_cols;
	/* detemine pixel size for second pass */
	for(pixel_width = 1;new_cols * pixel_width < old_cols;)
		pixel_width++;
	for(pixel_height = 1;new_rows * pixel_height < old_rows;)
		pixel_height++;
	self->pixel_width = pixel_width;
	self->pixel_height = pixel_height;
	self->upsample_width = new_cols * pixel_width;
	self->upsample_height = new_rows * pixel_height;
	if(old_cols != self->upsample_width || 
		old_rows != self->upsample_height)
		need_upsample = true;
	if(pixel_width > 1 || pixel_height > 1)
		need_downsample = true;
	if(need_upsample)
	{
		if(need_downsample)
		{
			/* we need two intermediate matrices for upsampling */
			self->fpm_upsample_pass1 = matAlloc(old_rows, self->upsample_width);
			self->fpm_upsample_pass2 = matAlloc(self->upsample_height, self->upsample_width);
			self->transform = fpm_resample_two_pass;
		}
		else
		{
			/* we need one intermediate matrix for upsampling */
			self->fpm_upsample_pass1 = matAlloc(old_rows, self->upsample_width);
			self->transform = fpm_resample_upsample_pass;
		}
	}
	else
	{
		if(need_downsample)
			self->transform = fpm_resample_downsample_pass;
		else
			/* no operation */
			self->transform = fpm_resample_no_op;
	}
	return (vhandle_t)self;
}

vboolean	V_GLOBAL
matGetResampleParameters(vhandle_t handle,
	u32_p old_rows_ret,u32_p old_cols_ret,
	u32_p new_rows_ret,u32_p new_cols_ret)
{
	fpm_resample_context_p self;
	self = (fpm_resample_context_p)libvIsHandle(handle,
		"matGetResamplingParameters",rsct_name,RSCT_MAGIC);
	if(self == LIBV_INVALID_HANDLE)
		return false;
	if(old_rows_ret)
		*old_rows_ret = self->old_rows;
	if(old_cols_ret)
		*old_cols_ret = self->old_cols;
	if(new_rows_ret)
		*new_rows_ret = self->new_rows;
	if(new_cols_ret)
		*new_cols_ret = self->new_cols;
	return true;
}

#define	RSCT_CAN_RESAMPLE(self,dst,src) \
	(((self)->old_rows == (src)->rows) && \
	((self)->old_cols == (src)->cols) && \
	((self)->new_rows == (dst)->rows) && \
	((self)->new_cols == (dst)->cols))
	
vboolean	V_GLOBAL
matCanResample(vhandle_t handle,matrix_p dst,matrix_p src)
{
#ifdef	CHECKED_BUILD
	static char *caller_name = "matCanResample";
#endif	/* CHECKED_BUILD */
	fpm_resample_context_p self;
	self = (fpm_resample_context_p)libvIsHandle(handle,
		"matCanResample",rsct_name,RSCT_MAGIC);
	if(self == LIBV_INVALID_HANDLE)
		return false;
	if(!dst || !src)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"%s(%08X,%08X,): incorrect parameter(s).\n",
			caller_name,dst,src);
#endif	/* CHECKED_BUILD */
		return false;
	}
	if(RSCT_CAN_RESAMPLE(self,dst,src))
		return true;
	return false;
}

vboolean	V_GLOBAL
matResample(vhandle_t handle,matrix_p dst,matrix_p src)
{
#ifdef	CHECKED_BUILD
	static char *caller_name = "matResample";
#endif	/* CHECKED_BUILD */
	fpm_resample_context_p self;
	self = (fpm_resample_context_p)libvIsHandle(handle,
		caller_name,rsct_name,RSCT_MAGIC);
	if(self == LIBV_INVALID_HANDLE)
		return false;
	if(!dst || !src)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"%s(%08X,%08X,): incorrect parameter(s).\n",
			caller_name,dst,src);
#endif	/* CHECKED_BUILD */
		return false;
	}
	if(RSCT_CAN_RESAMPLE(self,dst,src))
	{
		self->transform(self,dst,src);
		return true;
	}
#ifdef	CHECKED_BUILD
	conlPrintf(LOG_ERROR,
		"%s([%u,%u]->[%u,%u]): created for [%u,%u]->[%u,%u].\n",
		caller_name,
		src->rows,src->cols,
		dst->rows,dst->cols,
		self->old_rows,self->old_cols,
		self->new_rows,self->new_cols);
#endif	/* CHECKED_BUILD */
	return false;
}

void	V_GLOBAL
matDestroyResampleContext(vhandle_t handle)
{
	libvCloseHandle(handle,"matDestroyResamplingContext",rsct_name,RSCT_MAGIC);
}
