/*
		graphic effect lib.
		  カラーデータ再配置

		h. Toda 1994 5 16
*/

#include <stdio.h>
#include <stdlib.h>
#include <egb.h>
#include "g_eff.h"

#define NOERR 0		/* no error */
/* #define TOTAL 1024 */
#define TOTAL 16384

static int mx ;
static int aSen ;
static int mSen ;
static int cMax ;
static int aMax ;
static int x1 ;
static int y1 ;
static int x2 ;
static int y2 ;
static int (*read1)() ;
static int (*write)() ;
static int (*mask)() ;

static char *work1 ;
static char *work2 ;
static int workMax ;
static int dx ;
static int dy ;

static char memory[4*TOTAL] ;
static int flg = 0 ;
static int counter ;

g_replaceColors( int wkMax, char *wk, BASICPARA *para, int mode )
{
	mx = para->mix ;
	aSen = para->alphaSen ;
	mSen = para->maskSen ;
	cMax = para->colorMax ;
	aMax = para->alphaMax ;
	x1 = para->lupx ;
	y1 = para->lupy ;
	x2 = para->rdwx ;
	y2 = para->rdwy ;
	read1 = para->read1 ;
	write = para->write ;
	mask = para->mask ;

	dx = x2 - x1 + 1 ;
	dy = y2 - y1 + 1 ;
	if( dx < 0 || dy < 0 )return -1 ;
	work1 = wk ;
	work2 = wk + dx*dy ;
	workMax = wkMax ;	/* ワークエリア使用可能最大バイト数 */
	if( workMax < dx*dy*2 )		/* ワークエリアが小さすぎる時はリターン */
		return -1 ;

	switch( mode )
	{
	case 0:
		collect() ;
		break ;
	case 1:
		replace() ;
		break ;
	case 2:
		replace2() ;
		break ;
	}

	return NOERR ;
}

static int collect()
{
	char a[4] ;
	int x, y, i, j ;

	i = 0 ;
	for( x=x1 ; x<=x2 ; x++ )
	{
		for( y=y1 ; y<=y2 ; y++ )
		{
			read1( x, y, a ) ;
			if( (aSen && a[3]) || (aSen == 0) )
			{
				DWORD( work1 + i*4 ) = DWORD( a ) ;
				i++ ;
				if( (i*4) >= (workMax-4) )goto col01 ;
			}
		}
	}
col01:
	counter = i ;

	for( i=0 ; i<counter-1 ; i++ )
	{
		int y0, y1, temp ;

		y0 = 19595*work1[i*4] + 38470*work1[i*4+1] + 7471*work1[i*4+2] ;

		for( j=i+1 ; j<counter ; j++ )
		{
			y1 = 19595*work1[j*4] + 38470*work1[j*4+1] + 7471*work1[j*4+2] ;
			if( y1 < y0 )
			{
				temp = DWORD( work1 + i*4 ) ;
				DWORD( work1 + i*4 ) = DWORD( work1 + j*4 ) ;
				DWORD( work1 + j*4 ) = temp ;
				y0 = y1 ;
			}
		}
	}

	j = 0 ;
	for( i=0 ; i<counter-1 ; i++ )
	{
		while( ( DWORD( work1 + i*4 ) & 0x00ffffff )
		    == ( DWORD( work1 + j*4 ) & 0x00ffffff ) )
		{
			j++ ;
			if( j >= counter )break ;
		}
		if( j >= counter )break ;
		DWORD( work1 + (i+1)*4 ) = DWORD( work1 + j*4 ) ;
	}
	counter = i + 1 ;

	if( counter > TOTAL )
	{
		int delt ;

		j = 0 ;
		delt = counter / TOTAL ;
		for( i=0 ; i<TOTAL ; i++ )
		{
			DWORD( memory + i*4 ) = DWORD( work1 + j*4 ) ;
			j += delt ;
		}
		counter = TOTAL ;
	}
	else
	{
		for( i=0 ; i<counter ; i++ )
		{
			DWORD( memory + i*4 ) = DWORD( work1 + i*4 ) ;
		}
	}

	flg = 1 ;
	return NOERR ;
}

static int replace()
{
	int hist1[256] ;
	int hist2[2048] ;
	int i, j, x, y ;
	char a[4] ;
	int avf, avft, av, avt ;
	int delt, sum, low, high, delt2, low2 ;
	int y0, u0, v0, r0, g0, b0, y00 ;
	int add ;

	if( flg == 0 )return -1 ;

	for( i=0 ; i< 256 ; i++ )hist1[ i ] = 0 ;
	i = 0 ; j = 0 ;
	for( y=y1 ; y<=y2 ; y++ )
	{
		for( x=x1 ; x<=x2 ; x++ )
		{
			read1( x, y, a ) ;
			y0 = rgbToY( a[0], a[1], a[2], cMax ) ;
			BYTE( work1 + i ) = y0 ;
			if( (aSen && a[3]) || (aSen == 0) )
			{
				hist1[ y0 ] += 1 ;
				mixWrite( x, y, memory, a ) ;
				j++ ;
			}
			i++ ;
		}
	}

	avf = j * 256 / counter ;
	avft = 0 ;
	avt = 0 ;
	av = 0 ;
	high = 255 ;
	low  = 255 ;
	delt = 0 ;

	for( i=0 ; i< dx*dy ; i++ )BYTE( work2 + i ) = 0 ;

	for( i=counter-1 ; i>0 ; i-- )
	{
		avft += avf ;
		av = ( avft - avt*256 )/256 ;
		avt += av ;
		if( av == 0 )goto next ;

		for( sum=0 ; sum<av ; low-- )
		{
			if( low < 0 )break ;
			sum = sum + hist1[ low ] ;
		}
		low++ ;

		delt = hist1[ low ] - sum + av ;
		hist1[ low ] -= delt ;

		for( j=0 ; j<2048 ; j++ )hist2[ j ] = 0 ;

		for( y=y1 ; y<=y2 ; y++ )
		{
			for( x=x1 ; x<=x2 ; x++ )
			{
				add = (y-y1)*dx + (x-x1) ;
				read1( x, y, a ) ;
				if( ( BYTE( work2 + add ) == 0 ) && 
					((aSen && a[3]) || (aSen == 0)) )
				{
					y0 = BYTE( work1 + add ) ;
					if( (y0 > low) && (y0 <= high) )
					{
						read1( x, y, a ) ;
						mixWrite( x, y, memory+i*4, a ) ;
						BYTE( work2 + add ) = 1 ;
					}
					else if( y0 == low )
					{
						y00 = readYaddData( x, y ) ;
						hist2[ y00 ] += 1 ;
					}
				}

			}
		}

		low2 = 2040 ;
		for( sum=0 ; sum<delt ; low2-- )
		{
			if( low2 < 0 )break ;
			sum = sum + hist2[ low2 ] ;
		}
		low2++ ;

		delt2 = hist2[ low2 ] - sum + delt ;

		for( y=y1 ; y<=y2 ; y++ )
		{
			for( x=x1 ; x<=x2 ; x++ )
			{
				add = (y-y1)*dx + (x-x1) ;
				read1( x, y, a ) ;
				if( (BYTE( work2 + add ) == 0) && 
					((aSen && a[3]) || (aSen == 0)) )
				{
					y0 = BYTE( work1 + add ) ;
					if( y0 == low )
					{
						y00 = readYaddData( x, y ) ;
						if( y00 > low2 )
						{
							read1( x, y, a ) ;
							mixWrite( x, y, memory+i*4, a ) ;
							BYTE( work2 + add ) = 1 ;
						}
						else if( y00 == low2 )
						{
							if( delt2 > 0 )
							{
								read1( x, y, a ) ;
								mixWrite( x, y, memory+i*4, a ) ;
								BYTE( work2 + add ) = 1 ;
								delt2-- ;
							}
						}
					}
				}
			}
		}

next:	high = low ;
	}
	return NOERR ;
}

static int replace2()
{
	int i, x, y, y0 ;
	int min, max ;
	char a[4] ;

	if( flg == 0 )return -1 ;

	min = 65536*cMax ; max = 0 ;
	for( y=y1 ; y<=y2 ; y++ )
	{
		for( x=x1 ; x<=x2 ; x++ )
		{
			read1( x, y, a ) ;
			y0 = ( 19595*a[0] + 38470*a[1] + 7471*a[2] ) / cMax ;
			if( (aSen && a[3]) || (aSen == 0) )
			{
				if( min > y0 )min = y0 ;
				if( max < y0 )max = y0 ;
			}
		}
	}

	for( y=y1 ; y<=y2 ; y++ )
	{
		for( x=x1 ; x<=x2 ; x++ )
		{
			read1( x, y, a ) ;
			y0 = ( 19595*a[0] + 38470*a[1] + 7471*a[2] ) / cMax ;
			if( max > min )
				i = ( counter - 1 )*( y0 - min )/( max - min ) ;
			else
				i = ( counter - 1 ) / 2 ;
			mixWrite( x, y, memory+i*4, a ) ;
		}
	}

	return NOERR ;
}

static mixWrite( int x, int y, unsigned char *a, unsigned char *b )
{
	unsigned char c[4] ;
	int mix ;

	if( mSen )
	{
		if( mask( x, y ) >= mSen )
			return NOERR ;
	}

	mix = mx ;
	if( aSen )
	{
		mix = mix * b[3] / aMax ;
	}

	c[0] = ( a[0] * mix + b[0] * ( 256 - mix ) + 0x80 ) >> 8 ;
	c[1] = ( a[1] * mix + b[1] * ( 256 - mix ) + 0x80 ) >> 8 ;
	c[2] = ( a[2] * mix + b[2] * ( 256 - mix ) + 0x80 ) >> 8 ;
	c[3] = b[3] ;

	if( mix )
	{
		write( x, y, c ) ;
	}

	return NOERR ;
}

static readYaddData( x, y )
{
	int lx, ly, rx, ry ;
	int ret ;

	x = x - x1 ;
	y = y - y1 ;

	lx = ( x > 0 ) ? (x-1) : 0 ;
	ly = ( y > 0 ) ? (y-1) : 0 ;
	rx = ( x < (dx-1) ) ? (x+1) : (dx-1) ;
	ry = ( y < (dy-1) ) ? (y+1) : (dy-1) ;

	ret = BYTE( work1 + ly*dx + lx )
		+ BYTE( work1 + ly*dx + x  )
		+ BYTE( work1 + ly*dx + rx )

		+ BYTE( work1 +  y*dx + lx )
		+ BYTE( work1 +  y*dx + rx )

		+ BYTE( work1 + ry*dx + lx )
		+ BYTE( work1 + ry*dx + x  )
		+ BYTE( work1 + ry*dx + rx ) ;

	return ret ;
}

static rgbToY( int r0, int g0, int b0, int max )
{
	int y ;

	y  =  19595*r0 + 38470*g0 + 7471*b0 ;

	y = ( ((y/max)*219) >> 16 ) + 16 ;

	if( y < 0 )y = 0 ;
	if( y > 255 )y = 255 ;

	return y ;
}

static rgbToYuv( int r0, int g0, int b0, int *y0, int *u0, int *v0, int max )
{
	int y, cb, cr ;

	y  =  19595*r0 + 38470*g0 + 7471*b0 ;
	cb = -11049*r0 - 21699*g0 + 32748*b0 ;
	cr =  32755*r0 -27427*g0 -5328*b0 ;

	*y0 = ( ((y/max)*219) >> 16 ) + 16 ;
	*u0 = ( ((cb/max)*224) >> 16 ) + 128 ;
	*v0 = ( ((cr/max)*224) >> 16 ) + 128 ;

	if( *y0 < 0 )*y0 = 0 ;
	if( *y0 > 255 )*y0 = 255 ;
	if( *u0 < 0 )*u0 = 0 ;
	if( *u0 > 255 )*u0 = 255 ;
	if( *v0 < 0 )*v0 = 0 ;
	if( *v0 > 255 )*v0 = 255 ;

	return NOERR ;
}

static yuvToRgb( int y0, int u0, int v0, int *r0, int *g0, int *b0, int max )
{
	y0 = (( y0 - 16 ) * max )/219 ;
	u0 = (( u0 -128 ) * max )/224 ;
	v0 = (( v0 -128 ) * max )/224 ;

	*r0 = ( 65536*y0 + 4*u0 + 91920*v0 ) >> 16 ;
	*g0 = ( 65536*y0 - 22569*u0 - 46819*v0 ) >> 16 ;
	*b0 = ( 65536*y0 + 116198*u0 - 9*v0 ) >> 16 ;

	if( *r0 < 0 )*r0 = 0 ;
	if( *r0 > max )*r0 = max ;
	if( *g0 < 0 )*g0 = 0 ;
	if( *g0 > max )*g0 = max ;
	if( *b0 < 0 )*b0 = 0 ;
	if( *b0 > max )*b0 = max ;

	return NOERR ;
}

