#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 <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <streams/streams.h>
#import "../TVController.h"
#import "../ToyWin.h"
#import "../ToyWinEPS.h"
#import "../ToyView.h"
#import "../PrefControl.h"
#import "../common.h"
#import "../strfunc.h"
#import "../getpixel.h"
#import "MDAmethod.h"
#import "Dither.h"
#import "imageOperation.h"


@implementation ImageOpr (Mono)

/* Local Method */
- (int)doMonochrome:(int)op parent:parent
	filename:(const char *)fn info:(commonInfo *)cinf
	scale:(const unsigned char *)scale method:(int)tag
{
	ToyWin	*tw;
	commonInfo *newinf = NULL;
	unsigned char *working[MAXPLANE];
	int	x, y, w;
	int	r, g, b, a;
	int	pn, binf = 1;
	unsigned char *ptr, *pta;

	working[0] = NULL;
	tw = NULL;
	if ((newinf = (commonInfo *)malloc(sizeof(commonInfo))) == NULL)
		goto ErrEXIT;
	*newinf = *cinf;
	newinf->cspace = NX_OneIsWhiteColorSpace;
		/* getPixel() fixes 0 as Black */
	newinf->bits = 8;
	newinf->numcolors = 1;
	newinf->isplanar = YES;
	newinf->xbytes = byte_length(newinf->bits, newinf->width);
	newinf->palsteps = 0;
	newinf->palette = NULL;
	pn = newinf->alpha ? 2: 1;

	w = newinf->width * newinf->height;
	if ((working[0] = (unsigned char *)calloc(w * pn, 1)) == NULL)
		goto ErrEXIT;
	if (pn == 2) working[1] = working[0] + w;

	tw = [[ToyWin alloc] init:parent by:op];
	[tw initLocateWindow:fn width:newinf->width height:newinf->height];

	if (op == Monochrome) {
		for (y = 0; y < newinf->height; y++) {
			ptr = &working[0][y * newinf->width];
			pta = ptr + w;
			for (x = 0; x < newinf->width; x++) {
				getPixel(&r, &g, &b, &a);
				r = Bright255(r, g, b);
				ptr[x] = r = scale[r];
				if (r && r != 255)
					binf = 8;
				if (pn == 2)
					pta[x] = (a == AlphaTransp)
						? AlphaTransp : AlphaOpaque;
			}
		}
	}else {
		unsigned char apool[MAXWidth];
		id <Dithering> dither;

		binf = (op == Gray2Bits) ? 2 : 1;
		dither = tag ? [MDAmethod alloc] : [Dither alloc];
		if (dither == nil 
		|| [dither init:(1 << binf) width:newinf->width] == nil)
			goto ErrEXIT;
		for (y = 0; y < newinf->height; y++) {
			unsigned char *q;
			q = [dither buffer];
			ptr = &working[0][y * newinf->width];
			for (x = 0; x < newinf->width; x++) {
				getPixel(&r, &g, &b, &a);
				r = Bright255(r, g, b);
				*q++ = scale[r];
				apool[x] = a;
			}
			q = [dither getNewLine];
			for (x = 0; x < newinf->width; x++)
				*ptr++ = *q++;
			if (pn == 2) {
				pta = &working[1][y * newinf->width];
				for (x = 0; x < newinf->width; x++)
					pta[x] = (apool[x] == AlphaTransp)
						? AlphaTransp : AlphaOpaque;
			}
		}
		[dither free];
	}

	if (binf < 8) {
		unsigned char *planes[MAXPLANE];
		if (pn == 2 && !hadAlpha()) {
			pn = 1;
			newinf->alpha = NO;
		}
		newinf->bits = binf;
		newinf->xbytes = byte_length(newinf->bits, newinf->width);
		if (allocImage(planes, newinf->width, newinf->height, binf, pn))
			goto ErrEXIT;
		packWorkingImage(newinf, pn, working, planes);
		[tw makeComment: newinf from: cinf];
		if ([tw drawView:planes info: newinf] == nil) {
			free((void *)planes[0]);
			goto ErrEXIT;
		}
		free((void *)working[0]);
	}else {
		if (pn == 2 && !hadAlpha()) {
			pn = 1;
			newinf->alpha = NO;
			working[0] = (unsigned char *)realloc(working[0], w);
			working[1] = NULL;
		}
		[tw makeComment:newinf from:cinf];
		if ([tw drawView:working info: newinf] == nil)
			goto ErrEXIT;
	}
	[theController newWindow: tw];
	return 0;

ErrEXIT:
	if (working[0]) free((void *)working[0]);
	if (newinf) free((void *)newinf);
	if (tw) [tw free];
	return Err_MEMORY;
}


- monochrome:(int)op tone:(const unsigned char *)tone method:(int)tag
{
	ToyWin	*tw;
	ToyView	*tv = NULL;
	NXImageRep	*rep;
	commonInfo	*cinf;
	unsigned char	*map[MAXPLANE];
	char	fn[256];
	const char *fnam, *ex = NULL;
	int	isby, err;

	if ((tw = [theController keyWindow]) == nil) {
		NXBeep();
		return self;
	}
	isby = [tw madeby];
	if (isby == Monochrome || isby == Gray2Bits || isby == BiLevel
		|| isby == Brightness) {
		ToyWin	*win = [tw parent];
		if (win && [theController isOpenedID: win])
			tw = win;
	}

	tv = [tw toyView];
	fnam = [tw filename];
	cinf = [tv commonInfo];
	if (cinf->numcolors == 1) {
		if (cinf->bits == 1 || (op == Monochrome && !cinf->alpha)) {
			warnAlert(fnam, Err_OPR_IMPL);
			return self;
		}
	}
	if (![self checkCommonInfo:cinf filename:fnam])
		return self;
	if (op == Brightness) {
		ex = "Bright";
		op = Monochrome;
	}else if (op == Monochrome) ex = "Gray8";
	else if (op == BiLevel) ex = "BiLevel";
	else ex = "Gray2";
	sprintf(fn, "%s(%s)", fnam, ex);

	rep = [[tv image] bestRepresentation];
	[(NXBitmapImageRep *)rep getDataPlanes: map];
	if ((err = initGetPixel(cinf)) != 0) {
		errAlert(fnam, err);
		return self;
	}
	resetPixel(map, 0);
	if ((err = [self doMonochrome:op parent:tw
		filename:fn info:cinf scale:tone method:tag]) != 0)
		errAlert(fnam, err);
	return self;
}


/* Local Method */
- (int)doBrightness:(const char *)fn parent:(ToyWin *)parent
	info:(commonInfo *)cinf scale:(const unsigned char *)scale
{
	ToyWin	*tw;
	commonInfo *newinf = NULL;
	unsigned char *working[MAXPLANE];
	int	x, y, pn;

	working[0] = NULL;
	tw = NULL;
	if ((newinf = (commonInfo *)malloc(sizeof(commonInfo))) == NULL)
		goto ErrEXIT;
	*newinf = *cinf;
	newinf->bits = 8;
	newinf->isplanar = YES;
	newinf->xbytes = newinf->width;
	newinf->palsteps = 0;
	newinf->palette = NULL;
	pn = newinf->alpha ? 4: 3;

	if (allocImage(working, newinf->width, newinf->height, 8, pn))
		goto ErrEXIT;

	tw = [[ToyWin alloc] init:parent by:Brightness];
	[tw initLocateWindow:fn width:newinf->width height:newinf->height];

	for (y = 0; y < newinf->height; y++) {
	    int  elm[MAXPLANE];
	    int  i, v, ny, dd;
	    int  ptr = y * newinf->width;
	    for (x = 0; x < newinf->width; x++) {
		getPixel(&elm[0], &elm[1], &elm[2], &elm[3]);
		if (elm[3] != AlphaTransp) {
			ny = Bright255(elm[RED], elm[GREEN], elm[BLUE]);
			if ((dd = scale[ny] - ny) != 0)
			    for (i = 0; i < 3; i++) {
				v = elm[i] + dd;
				elm[i] = (v > 255) ? 255 : ((v < 0) ? 0 : v);
			    }
		}
		for (i = 0; i < pn; i++)
		    working[i][ptr] = elm[i];
		ptr++;
	    }
	}

	if (newinf->alpha && !hadAlpha()) {
		pn = 3;
		newinf->alpha = NO;
		working[0] = (unsigned char *)
		    realloc(working[0], newinf->width * newinf->height * pn);
		working[pn] = NULL;
	}
	[tw makeComment: newinf from: cinf];
	if ([tw drawView:working info: newinf] == nil)
		goto ErrEXIT;

	[theController newWindow: tw];
	return 0;

ErrEXIT:
	if (working[0]) free((void *)working[0]);
	if (newinf) free((void *)newinf);
	if (tw) [tw free];
	return Err_MEMORY;
}

- brightness:(const unsigned char *)tone
{
	ToyWin	*tw;
	ToyView	*tv = NULL;
	NXImageRep	*rep;
	commonInfo	*cinf;
	unsigned char	*map[MAXPLANE];
	char	fn[256];
	const char *fnam;
	int	isby, err;

	if ((tw = [theController keyWindow]) == nil) {
		NXBeep();
		return self;
	}
	isby = [tw madeby];
	if (isby == Monochrome || isby == Gray2Bits || isby == BiLevel
		|| isby == Brightness) {
		ToyWin	*win = [tw parent];
		if (win && [theController isOpenedID: win])
			tw = win;
	}
	tv = [tw toyView];
	fnam = [tw filename];
	cinf = [tv commonInfo];
	if (cinf->numcolors == 1)	/* Monochrome */
		return [self monochrome:Brightness tone:tone method:1];
			/* Don't Mind method: */

	if (![self checkCommonInfo:cinf filename:fnam])
		return self;
	sprintf(fn, "%s(Bright)", fnam);

	rep = [[tv image] bestRepresentation];
	[(NXBitmapImageRep *)rep getDataPlanes: map];
	if ((err = initGetPixel(cinf)) != 0) {
		errAlert(fnam, err);
		return self;
	}
	resetPixel(map, 0);
	if ((err = [self doBrightness:fn parent:tw info:cinf scale:tone]) != 0)
		errAlert(fnam, err);
	return self;
}

@end
