/*
 * ( c ) SPDsoft & GTIC 20 julio 1993
 * 2D: Mon Jun 17 23:37:08 MDT 1996
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>


#include "pict.h"

#ifdef THINK_C
#include <console.h>
#endif

#ifdef MYGETOPT
char getopt( int	argc, char	*argv[], char *format );
#endif

#ifndef MAX_FILTER_W
#	define MAX_FILTER_W	16
#endif

#define IDENT   	"@(#)filterp 0.1 ( c ) SPDsoft, Jun 1996"
char	__ident[] = IDENT;
#define VERS_STR	(( char*)&__ident[4])
#ifndef MAX
#   define MAX(a, b)       (((a) > (b)) ? (a) : (b))
#endif
#define	iw	( pictIn->image_W )
#define	ih	( pictIn->image_H )


float	filter[MAX_FILTER_W][MAX_FILTER_W];

void	usage( char *name );
void	fatalError( char *name,char *str );
int		LoadFilter( char *name, int *fw, int *fh );

void	usage( char *name )
{
	fprintf( stderr,
	"usage: %s [-r] [-o output] [-d div] <filter> files...\n",
	name );
	fprintf( stderr, "filter: (-f|-s 'filter string') | -c name \n" );
	exit(-1 );
}

void	fatalError( char *name,char *str )
{
	fprintf( stderr,
	"%s: Fatal Error: %s. bye...\n",name,str );
	exit(-2 );
}

int	GetFilterV( char *str, float filter[] )
{
	char *tmpstr;
	int	fh = 0;

	for(
		tmpstr = strtok( str," ");
		( tmpstr != NULL ) && ( fh < MAX_FILTER_W );
		tmpstr = strtok( NULL," ")
		)
		filter[fh++] = atof( tmpstr );

	return( fh );
}

main( int argc,char *argv[])
{
	char	*fname="fltout.pict";

	unsigned int
			first		= 0,
			last		= 0,
			NumOfSFrames;

	int		OutputOpt	= 0;
	char	Verbose		= 0;
	char	Zero		= 0;
	char	s_f			= 0;
	char	c_f			= 0;
	char	f_f			= 0;

	Pict	pictIn,
			pictOut;
			
	int		np,nl,nm,nf,ic,i,j,k,opcion;
	int		data_w;
	int		fh			= 0,
			fw			= 0;

	int		nc, icc, iccw;
	int		fw2;

	unsigned char	 *row[MAX_FILTER_W];
	unsigned char	 *rowp;
	unsigned char	 *zero=NULL;

	float			filt_r[MAX_FILTER_W];
	float			filt_c[MAX_FILTER_W];
	float			div = 1.0;
	float			res;

	extern char     *optarg;
	extern int      optind,opterr;

#ifdef THINK_C
	argc = ccommand(&argv );
#endif

	while ( ( opcion=getopt( argc,argv,"zo:f:s:d:c:vh")) != EOF )
	{
		switch( opcion )
		{
			case 'z':
					Zero = !Zero;
					break;
			case 'o':
					OutputOpt=optind-1;
					break;
			case 's':
					if ( s_f || c_f )
						fatalError( argv[0],"Bad filter specification");
						
					s_f = 1;

					if ( !f_f ) fh = 1 ;
					if (
					0 == ( fw = GetFilterV( argv[optind-1], filt_r ))
					)
						fatalError( argv[0],"Bad filter string");
					
					for ( i = 0 ; i < fw ; i++)
						filter[0][i] = filt_r[i];

					break;
			case 'f':
					if ( f_f || c_f )
						fatalError( argv[0],"Bad filter specification");
						
					f_f = 1;
					
					if ( !s_f ) fw = 1 ;
					if (
					0 == ( fh = GetFilterV( argv[optind-1], filt_c ))
					)
						fatalError( argv[0],"Bad filter string");
					
					for ( i = 0 ; i < fh ; i++)
						filter[i][0] = filt_c[i];

					break;

			case 'c':
					if ( f_f || s_f )
						fatalError( argv[0],"Bad filter specification");
						
					if ( 0 != LoadFilter(argv[optind-1], &fw, &fh) )
						fatalError( argv[0],"can't load filter");

					break;
					
			case 'd':
					div = atof( optarg );
					break;
			case 'v':
					fprintf( stdout,"%s\n", VERS_STR );
					Verbose=1;
					break;
			defatult:	usage( argv[0]);
		}
	}



	if  ( ( fw * fh == 0 ) || ( div <= 0.0 ) )
		usage( argv[0] );


	if ( 0==( fh%2 ))
		fatalError( argv[0],"Filter width must be odd");

	if ( 0==( fw%2 ))
		fatalError( argv[0],"Filter height must be odd");


	first			= optind;
	last			= argc - 1;
	NumOfSFrames	= last - first + 1;

	if ( NumOfSFrames==0 ) usage( argv[0]);
	if (( NumOfSFrames > 1 ) && ( OutputOpt != 0 ))
		fatalError( argv[0],"-o requires just one pict");

	if ( f_f && s_f )
		for( i=0 ; i<fh ; i++ )
		for( j=0 ; j<fw ; j++ )
		{
			filter[i][j] = filt_c[i] * filt_r[j];
		}

	if ( div != 1.0 )
		for( i=0 ; i<fh ; i++ )
		for( j=0 ; j<fw ; j++ )
		{
			filter[i][j] /= div;
		}

	fw2 = fw/2;

	if( Verbose )
	{
		printf( "%s:%d\t%d\n", argv[0], fh, fw );
		for( i=0 ; i<fh ; i++ )
			for( j=0 ; j<fw ; j++ )
			printf("%f%c",filter[i][j], (j==fw-1?'\n':' '));
	}

	for( nf = first; nf <= last; nf++ )
	{
		sprintf( fname,"f_%s",argv[nf]);
		if( Verbose )
		{
			printf("%s:output )\t%s\n",
				argv[0],
				OutputOpt != 0 ? argv[OutputOpt] : fname );

			printf("%s:input  )\t%s\n",argv[0],argv[nf]);
		}

		pictIn = pict_open( argv[nf],P_READ,0,0,0,NULL );

		if( pictIn->clr_mode == M_RGB8 )
			fatalError( argv[0],"Indexed color not supported!");

		if (( pictIn->clr_mode == M_MONO ) && Verbose )
			fprintf( stderr,
			"Warning: Grayscale image. Operating on indexes\n");

		data_w = iw * pictIn->n_comp;

		if ( ( pictIn->pixels = ( U_char*)malloc( data_w * fh )) == NULL )
			fatalError( argv[0],"Buy more memory!");

		pictOut = pict_open(
			OutputOpt != 0 ? argv[OutputOpt] : fname,
			P_WRITE,
			ih, iw,
			pictIn->clr_mode,
			pictIn->map );

		if ( ( pictOut->pixels = ( U_char*)malloc( data_w ) ) == NULL )
			fatalError( argv[0],"Buy more memory!");

		if ( Zero )
		if ( ( zero = ( U_char*)calloc( data_w , sizeof(char))) == NULL )
			fatalError( argv[0],"Buy more memory!");

		/******************************************************************/
		/* This is the real work */
		for( np=fh/2; np < fh-1; np++ )
			pict_get_line( pictIn, pictIn->pixels+data_w*np );
	
		for( j=0, nm=0; j < ih; j++, nm = ( nm+1 )%fh )
		{
			for( np=0; np < fh; np++ )
				row[np]=pictIn->pixels+data_w*(( np+nm )%fh );
	
			for( np=0; np < fh; np++ )
			{
				nl=( j+np-fh/2 );	
				if( nl<0 ) row[np] = Zero ? zero : row[fw/2];
				else if ( nl >= ih-1 )
					row[np] = Zero ? zero : row[fw-1];
			}
			if( j < ih - fh/2 )
				pict_get_line( pictIn,row[fh-1]);
	

			for( i=0, ic=0, nc=0; i<data_w; i++ )
			{
				res = 0;

				icc = ic - fw2; 
				iccw = iw - icc - 1; 

				for( np=0; np < fh ;np++ )
				{
					rowp = row[np] + i - fw2;

					for( k = 0 ; k < fw ; k++ )
					{
						if ( icc + k < 0 )
						{
							res +=
								Zero ?
								0.0 :
								(float)*(row[np] + nc) * filter[np][k];
							rowp++;
						}
						else if ( iccw < k )
						{
							res +=
								Zero ?
								0.0 :
								(float)*(row[np] + nc -1 ) * filter[np][k];
						}
						else
							res += (float) *rowp++ * filter[np][k];
					}
				}
				if ( res < 0.0 ) res = 0.0;
				else if ( res > 255.0 ) res = 255.0;
	
				*( pictOut->pixels+i )=( unsigned char )res;

				if ( ++ic == iw )
				{
					nc += iw;
					ic = 0;
				}

			} /* columns */

			pict_put_line( pictOut,pictOut->pixels );

		} /* lines */
	
		/* End of real work */
		/******************************************************************/

		pict_close( pictIn );
		pict_close( pictOut );
		pict_free( pictIn );
		pict_free( pictOut );
		if ( NULL != zero ) free (zero);
	}
}

int	LoadFilter( char *name, int *fw, int *fh )
{
	char    *rpath, *path, *argt, p[512];
	char    *defpath=".:/usr/local/lib/filters";
    int     found=0;
    struct  stat sbp;
	FILE	*fp;

	if ((rpath=getenv("MATRIX_PATH"))==NULL) rpath = defpath;
	if ((path = strdup(rpath))==NULL)
	{
		perror("Load Filter");
	}
    argt = strtok( path, ":"  );

	while((argt!=NULL) && (!found))
	{
		sprintf(p,"%s/%s",argt,name);

		if ( stat(p,&sbp)!=-1)
		{
			found = ((sbp.st_mode & S_IFMT)==S_IFREG);
		}
		argt=strtok( NULL, ":" );
	}

	if ( NULL == ( fp = fopen( p, "r" )))
	{
		perror("fopen");
		return(-1);
	}
	else
	{
		for( *fh = 0;
			*fh < MAX_FILTER_W &&
			NULL != ( fgets ( p, sizeof(p), fp ))
			;  )

		if ( ( *p != '\n' ) && ( *p != '#' ))
		{
			*fw = GetFilterV( p, filter[*fh] );
			(*fh)++;
		}
			
		fclose( fp );
		return( 0 );
	}
}