#import "../ImageOpr.h"
#import <appkit/Application.h>
#import <appkit/publicWraps.h>
#import <appkit/NXEPSImageRep.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/Control.h>
#import <appkit/Panel.h>
#import <appkit/TextField.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <streams/streams.h>
#import <sys/types.h>
#import <libc.h>
#import "../TVController.h"
#import "../ToyWin.h"
#import "../ToyWinEPS.h"
#import "../ToyView.h"
#import "../common.h"
#import "../getpixel.h"
#import "imageOperation.h"

@implementation ImageOpr

- keyParentWindow:(int)op
{
	ToyWin *tw, *win;

	if ((tw = [theController keyWindow]) == nil)
		return nil;
	if (op != NoOperation && [tw madeby] == op) {
		win = [tw parent];
		if (win && [theController isOpenedID: win])
			return win;
	}
	return tw;
}

- (BOOL)checkCommonInfo:(const commonInfo *)cinf filename:(const char *)fn
{
	if (cinf->type == Type_eps) {
		warnAlert(fn, Err_EPS_IMPL);
		return NO;
	}
	if (cinf->cspace == NX_CMYKColorSpace) {
		warnAlert(fn, Err_IMPLEMENT);
		return NO;
	}
	if (cinf->width >= MAXWidth || cinf->height >= MAXWidth) {
		errAlert(fn, Err_MEMORY);
		return NO;
	}
	return YES;
}


static void sub_clip(NXRect *select, commonInfo *cinf, commonInfo *newinf,
			int idx[], unsigned char **working)
{
	int	x, y;
	int	i, pidx, ptr;
	int	pix[MAXPLANE];

	int skipx = select->origin.x;
	int skipt = cinf->width - (skipx + select->size.width);
	int skipy = cinf->height - (select->origin.y + select->size.height);
	for (y = 0; y < skipy; y++) {
		for (x = 0; x < cinf->width; x++)
			getPixelA(pix);
	}
	for (y = 0; y < newinf->height; y++) {
		ptr = y * newinf->width;
		for (x = 0; x < skipx; x++)
			getPixelA(pix);
		for (x = 0; x < newinf->width; x++) {
			getPixelA(pix);
			for (i = 0; i <= ALPHA; i++) {
				if ((pidx = idx[i]) < 0) continue;
				working[pidx][ptr + x] = pix[i];
			}
		}
		for (x = 0; x < skipt; x++)
			getPixelA(pix);
	}
}


/******* Random Functions ********/
#define  ValBAND	32
#define  BANDMask	0x1f
#define  ValBIAS	16
static int randomFreq = 0, randomMag = 0;

- setRandom:(float)freq :(float)mag
{
	if (!randomMag)
		srandom(getpid());
	randomFreq = freq * freq * 256.0;
	randomMag = (int)(mag * mag * (ValBAND - ValBIAS + 256.0)) + ValBIAS;
	return self;
}

static int random_value(int pix)
{
	long r, v;
	r = random();
	if ((r & 0xff) >= randomFreq)
		return pix;
	r = random();
	v = randomMag - ((r >> 1) & BANDMask);
	if (r & 1) v += pix;
	else v = pix - v;
	return (v > 255) ? 255 : ((v < 0) ? 0 : v);
}

static void sub_negative(int op, NXRect *select, 
		commonInfo *newinf, int idx[], unsigned char **working)
{
	int	x, y;
	int	i, pidx, ptr, alp;
	int	pix[MAXPLANE];
	int	selectflag = NO, yout, xout;
	int	skipx = 0, skipy = 0, yorig = 0;

	if (select && select->size.width > 0) {
		skipx = select->origin.x + select->size.width - 1;
		skipy = newinf->height - select->origin.y - 1;
		yorig = skipy - select->size.height + 1;
		selectflag = YES;
	}

	if ((alp = idx[ALPHA]) < 0)
		alp = 0;	/* index of Alpha > 0 */
	for (y = 0; y < newinf->height; y++) {
		yout = (selectflag && (y < yorig || y > skipy));
		ptr = y * newinf->width;
		for (x = 0; x < newinf->width; x++) {
			xout = (yout ||
			(selectflag && (x < select->origin.x || x > skipx)));
			getPixelA(pix);
			if (xout) {
				for (i = 0; i < ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = pix[i];
				}
			}else if (op == Negative) {
				for (i = 0; i < ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = 0xff - pix[i];
				}
			}else {
				for (i = 0; i < ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = random_value(pix[i]);
				}
			}
			if (alp) /* Alpha */
				working[alp][ptr + x] = pix[i];
		}
	}
	if (newinf->palette) {
		unsigned char *p;
		paltype *pal = newinf->palette;
		for (x = 0; x < newinf->palsteps; x++) {
			p = pal[x];
			for (i = 0; i < 3; i++)
				p[i] = 0xff - p[i];
		}
	}
}


/* Local Method */
- doBitmap:(int)op parent:parent
		filename:(const char *)fn info:(commonInfo *)cinf
		to:(int)angle rect:(NXRect *)select
{
	ToyWin	*tw;
	commonInfo *newinf = NULL;
	unsigned char *working[MAXPLANE], *planes[MAXPLANE];
	int	i, pl;
	int	idx[MAXPLANE];
	BOOL	rotalpha = NO, hadalpha = NO;

	working[0] = planes[0] = NULL;
	tw = NULL;
	if ((newinf = (commonInfo *)malloc(sizeof(commonInfo))) == NULL)
		goto ErrEXIT;
	*newinf = *cinf;
	if (cinf->cspace == NX_OneIsBlackColorSpace)
		newinf->cspace = NX_OneIsWhiteColorSpace;
		/* getPixel() fixes 0 as Black */
	newinf->isplanar = YES;
	if (op == Rotation) {
		rotate_size(angle, cinf, newinf);
		if (newinf->width >= MAXWidth || newinf->height >= MAXWidth)
			goto ErrEXIT;
	}else if (op == Clip) {
		newinf->width = select->size.width;
		newinf->height = select->size.height;
	}
	newinf->xbytes = byte_length(newinf->bits, newinf->width);

	/** if rotalpha==YES one color(transparent) is added **/
	if (cinf->palette 
	&& (cinf->alpha || !rotalpha || newinf->palsteps < FIXcount)) {
		newinf->palette = copyPalette(cinf->palette, newinf->palsteps);
		if (newinf->palette == NULL)
			goto ErrEXIT;
	}else {
		newinf->palette = NULL;
		newinf->palsteps = 0;
	}

	pl = newinf->numcolors;
	for (i = 0; i < pl; i++) idx[i] = i;
	for (i = pl; i < MAXPLANE; i++) idx[i] = -1;
	if (op == Rotation && (angle % 90 != 0))
		newinf->alpha = rotalpha = YES;
	if (newinf->alpha) idx[ALPHA] = pl++;
	if (allocImage(working, newinf->width, newinf->height, 8, pl))
		goto ErrEXIT;
	tw = [[ToyWin alloc] init:parent by:op];
	[tw initLocateWindow:fn width:newinf->width height:newinf->height];

	if (op == Clip) {
		sub_clip(select, cinf, newinf, idx, working);
		if (newinf->alpha) {
			int aw = newinf->width * newinf->height;
			unsigned char *ap = working[pl-1];
			for (i = 0; i < aw; i++, ap++)
				if (isAlphaTransp(*ap)) {
					hadalpha = YES;
					break;
				}
		}
	}else if (op == Negative || op == RandomPttn) {
		sub_negative(op, select, newinf, idx, working);
		if (newinf->alpha) hadalpha = hadAlpha();
	}else /* Rotation | Horizontal | Vertical */ {
		if (sub_rotate(op, angle, cinf, newinf, idx, working))
			goto ErrEXIT;
		if (rotalpha)
			hadalpha = YES;
		else if (newinf->alpha)
			hadalpha = hadAlpha();
	}

	if (newinf->alpha && !hadalpha) {
		newinf->alpha = NO;
		working[--pl] = NULL;
	}
	if (newinf->alpha && newinf->palette && newinf->palsteps >= 256) {
		free((void *)newinf->palette);	/* 256 colors are too much */
		newinf->palette = NULL;
		newinf->palsteps = 0;
	}
	[tw makeComment: newinf from: cinf];
	if (newinf->bits < 8) {
		if (allocImage(planes, newinf->width, newinf->height,
			newinf->bits, pl))
			goto ErrEXIT;
		packWorkingImage(newinf, pl, working, planes);
		if ([tw drawView:planes info: newinf] == nil)
			goto ErrEXIT;
		free((void *)working[0]);
	}else {
		if ([tw drawView:working info: newinf] == nil)
			goto ErrEXIT;
	}
	[theController newWindow: tw];
	return self;

ErrEXIT:
	if (working[0]) free((void *)working[0]);
	if (planes[0]) free((void *)planes[0]);
	if (newinf) {
		if (newinf->palette) free((void *)newinf->palette);
		free((void *)newinf);
	}
	if (tw) [[tw window] performClose: self];
		/* This call frees tw */
	return nil;
}


- doRotateFlipClip: (int)op to:(int)angle
{
	ToyWin	*tw;
	ToyView	*tv = NULL;
	NXImageRep	*rep;
	commonInfo	*cinf;
	NXRect		*select = NULL;
	unsigned char	*map[MAXPLANE];
	const char	*ex = NULL, *fnam;
	char	fn[256];
	int	err;

	if ((tw = [theController keyWindow]) != nil) {
	    tv = [tw toyView];
	    if (op == Clip) {
		select = [tv selectedScaledRect];
		if (select->size.width < 1.0 || select->size.height < 1.0)
		    tw = nil;
	    }else if (op == Negative || op == RandomPttn)
		select = [tv selectedScaledRect];
	}
	if (tw == nil) {
		NXBeep();
		return self;
	}

	cinf = [tv commonInfo];
	if (cinf->width >= MAXWidth || cinf->height >= MAXWidth) {
		errAlert([tw filename], Err_MEMORY);
		return self;
	}
	switch (op) {
	case Rotation:	ex = "Rotate"; break;
	case Clip:	ex = "Clip"; break;
	case Negative:	ex = "Negative"; break;
	case RandomPttn: ex = "Random"; break;
	case Horizontal:
	case Vertical:
	default:	ex = "Flip"; break;
	}
	fnam = [tw filename];
	sprintf(fn, "%s(%s)", fnam, ex);

	if (cinf->type == Type_eps) {
		NXStream	*stream;
		ToyWinEPS	*newtw;
		commonInfo	info;

		if (op == Negative || op == RandomPttn) {
			warnAlert(fnam, Err_EPS_IMPL);
			return self;
		}
		if (op == Clip)
			stream = [(ToyWinEPS *)tw clipEPS:select error:&err];
		else { /* Rotate | Horizontal | Vertical */
			rotate_size(angle, cinf, &info);
			stream = [(ToyWinEPS *)tw rotateEPS:op to:angle
				width:info.width height:info.height
				name:fn error:&err];
		}
		if (stream == NULL) {
			errAlert(fnam, err);
			return self;
		}
		newtw = [[ToyWinEPS alloc] init:tw by:op];
		err = [newtw drawFromFile:(const char *)fn or:stream];
		NXCloseMemory(stream, NX_FREEBUFFER);
		if (err) {
			errAlert(fnam, err);
			[newtw free];
		}else
			[theController newWindow: newtw];
		return self;
	}

	if (cinf->cspace == NX_CMYKColorSpace) {
		warnAlert(fnam, Err_IMPLEMENT);
		return self;
	}

	rep = [[tv image] bestRepresentation];
	[(NXBitmapImageRep *)rep getDataPlanes: map];
	if ((err = initGetPixel(cinf)) != 0) {
		errAlert(fnam, err);
		return self;
	}
	resetPixel(map, 0);
	if ([self doBitmap:op parent:tw
		filename:fn info:cinf to:angle rect:select] == nil)
		errAlert(fn, Err_MEMORY);
	return self;
}

@end
