#include "c4d_general.h"
#include "c4d_memory.h"
#include "c4d_gui.h"

#include "x4d_filter.h"
#include "c4d_bitmapfilter.h"
#include	"filterpreview.h"
#include	"matrixutil.h"
#include	"matrixwrapper.h"
#include	"linefetchutil.h"

LONG	do_matrix_effect_1dt( BM_REF bm, BM_TILE *dt, MATRIX_REF _emr, LONG tile_flags )
{
	RECT32	bounds;
	UCHAR	 *dst;
	LONG		no_components;
	LONG		x1, y1, x2, y2;
	LONG		width, height;
	LONG	 	y;
	UCHAR		*src_rows[MAXTRIX_MAX_COLUMNS];
	LONG		err = FILTER_OK;
	ROW_REF	src_rr;
	LONG	x_offset;
	LONG	y_offset;
	LONG	no_columns;
	LONG	no_rows;
		
	bounds = *(RECT32 *) &dt->xmin;														
	x1 = bounds.x1;
	y1 = bounds.y1;
	x2 = bounds.x2;
	y2 = bounds.y2;
	
	dst = (UCHAR *) dt->addr;

	width = bounds.x2 - bounds.x1;														// width of the selected area
	height = bounds.y2 - bounds.y1;														// height of the selected area
	no_components = get_PX_CMPNTS( bm->image_px_format );			// number of pixel components

	get_effect_matrix_info( _emr, &no_columns, &no_rows, &x_offset, &y_offset );

	if (( src_rr = new_row_fetcher( bm, x1 - x_offset, x2 + ( no_columns - x_offset - 1 ), tile_flags, TILE_BM_READ_ONLY, 0 )) != 0 )
	{
		LONG	j;
		
		for ( j = 0; j < no_rows - 1; j++ )
		{
			src_rows[j] = rf_get_bitmap_row( src_rr, y1 - y_offset + j );
			if (src_rows[j] == 0L	)																// unable to access this line?
			{
				delete_row_fetcher( src_rr, TRUE );									// free all buffered lines
				return( FILTER_MEM_ERR );
			}
		}

		for ( y = y1; y < y2; y++ )
		{
			j = no_rows - 1;
			src_rows[j] = rf_get_bitmap_row( src_rr, y + ( j - y_offset ));	// ask for the next line

			if ( src_rows[j] == 0L	)															// unable to access this line?
			{
				err = FILTER_MEM_ERR;
				break;
			}

			apply_matrix_effect( _emr, (void**) src_rows, dst, width );	// apply the matrix function to the current destination line
			rf_free_bitmap_row( src_rr, y - y_offset );						// we don't need this line for the next loop

			for ( j = 1; j < no_rows; j++ )												// swap the source line pointers
				src_rows[j - 1] = src_rows[j];

			dst += dt->width;																			// next destination line
		}
		delete_row_fetcher( src_rr, TRUE );											// free all buffered lines
	}
	return( err );
}

LONG	do_matrix_effect( BM_REF bm, MATRIX_REF _emr, FilterThread *thread, LONG tile_flags, Bool update_view, String *apply_filter_str )
{
	BM_TILE *dt;
	RECT32	bounds;
	UCHAR	 *dst;
	LONG		no_components;
	LONG		x1, y1, x2, y2;
	LONG		width, height;
	LONG	 	y;
	UCHAR		*src_rows[MAXTRIX_MAX_COLUMNS];
	LONG		err = FILTER_OK;
	ROW_REF	src_rr;
	LONG	x_offset;
	LONG	y_offset;
	LONG	no_columns;
	LONG	no_rows;
	PROGRESS_ID	pid;

	LONG	progress_cnt;
	LONG	progress_max;
	
	x1 = bm->mask_rect.x1;
	y1 = bm->mask_rect.y1;
	x2 = bm->mask_rect.x2;
	y2 = bm->mask_rect.y2;
	
	width = bm->mask_rect.x2 - bm->mask_rect.x1;							// width of the selected area
	height = bm->mask_rect.y2 - bm->mask_rect.y1;							// height of the selected area
	no_components = get_PX_CMPNTS( bm->image_px_format );			// number of pixel components

	progress_cnt = 0;
	progress_max = (height + bm->preferred_tile_height - 1) / bm->preferred_tile_height;
	pid = BfProgressNew();

	get_effect_matrix_info( _emr, &no_columns, &no_rows, &x_offset, &y_offset );

	if (( src_rr = new_row_fetcher( bm, x1 - x_offset, x2 + ( no_columns - x_offset - 1 ), tile_flags, TILE_BM_READ_ONLY, 0 )) != 0 )
	{
		LONG	j;
			
		for ( j = 0; j < no_rows - 1; j++ )
		{
			src_rows[j] = rf_get_bitmap_row( src_rr, y1 - y_offset + j );
			if ( src_rows[j] == 0L	)															// unable to access this line?
			{
				delete_row_fetcher( src_rr, TRUE );									// free all buffered lines
				return( FILTER_MEM_ERR );
			}
		}

		bounds = bm->mask_rect;
		dt = 0;
		
		for ( y = y1; y < y2; y++ )
		{
			if ((( y - y1 ) % bm->preferred_tile_height ) == 0 )
			{
				BfProgressUpdate( pid, progress_cnt++, progress_max, *apply_filter_str );

				if ( dt )
				{
					BfBitmapTileDetach( bm, dt, TRUE );								// apply the destination tile
					BfUpdateView( bm );
				}

				bounds.y1 = y;
				bounds.y2 = y + bm->preferred_tile_height;
				dt = BfBitmapTileGet( bm, &bounds, 0, 0, TILE_BM_WRITE, 0 );

				if ( dt == 0L )
				{
					err = FILTER_MEM_ERR;
					break;
				}
				
				dst = (UCHAR *) dt->addr;
	
				if ( thread && thread->TestBreak())									// force exit?
				{
					err = FILTER_ABORT;
					break;
				}
			}

			j = no_rows - 1;
			src_rows[j] = rf_get_bitmap_row( src_rr, y + ( j - y_offset ));	// ask for the next line

			if ( src_rows[j] == 0L	)															// unable to access this line?
			{
				err = FILTER_MEM_ERR;
				break;
			}

			apply_matrix_effect( _emr, (void**) src_rows, dst, width );	// apply the matrix function to the current destination line
			rf_free_bitmap_row( src_rr, y - y_offset );						// we don't need this line for the next loop
					
			for ( j = 1; j < no_rows; j++ )												// swap the source line pointers
				src_rows[j - 1] = src_rows[j];

			dst += dt->width;																			// next destination line
		}

		if ( dt )
		{
			BfBitmapTileDetach( bm, dt, err == FILTER_OK );				// apply the destination tile
			BfUpdateView( bm );
		}

		delete_row_fetcher( src_rr, TRUE );											// free all buffered lines
		BfProgressDelete( pid );
	}

	return( err );
}
