#import "../ImageOpr.h"
#import <appkit/Application.h>
#import <appkit/publicWraps.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/Control.h>
#import <appkit/Panel.h>
#import <appkit/TextField.h>
#import <objc/NXBundle.h>	/* LocalizedString */
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <libc.h>
#import <streams/streams.h>
#import "../TVController.h"
#import "../ToyWin.h"
#import "../ToyWinEPS.h"
#import "../ToyView.h"
#import "../common.h"
#import "../strfunc.h"
#import "../getpixel.h"
#import "../ColorMap.h"
#import "imageOperation.h"
#import "MDAmethod.h"
#import "Dither.h"

@implementation ImageOpr (ColorHalf)

/* Local Method */
- (commonInfo *)colorHalfMap:(unsigned char **)newmap method:(int)tag
	from:(ToyWin *)tw with:(commonInfo *)cinf
	colors:(int)colnum alpha:(BOOL)hasalpha
{
	id	msgtext;
	commonInfo *newinf = NULL;
	int	i, x, y, mdaidx, bits;
	int	pl, err = 0;
	const char *fnam;
	unsigned char *working[MAXPLANE];
	unsigned char apool[MAXWidth];
	unsigned char *q[4];
	id <Dithering> dither[3];
	static int MDAbits[3][3] = { {8,8,4}, {4,4,4}, {2,2,2} };

	fnam = [tw filename];
	newmap[0] = working[0] = NULL;
	switch (colnum) {
	default:
	case 256: mdaidx = 0;  bits = 4;  break;
	case  64: mdaidx = 1;  bits = 2;  break;
	case   8: mdaidx = 2;  bits = 1;  break;
	}
	pl = hasalpha ? 4 : 3;
	err = allocImage(working, cinf->width, cinf->height, 8, pl);
	if (err) goto ErrEXIT;
	err = allocImage(newmap, cinf->width, cinf->height, bits, pl);
	if (err) goto ErrEXIT;
	if ((newinf = (commonInfo *)malloc(sizeof(commonInfo))) == NULL) {
		err = Err_MEMORY;
		goto ErrEXIT;
	}
	*newinf = *cinf;
	newinf->xbytes = byte_length(bits, newinf->width);
	newinf->bits = bits;
	newinf->numcolors = 3;
	newinf->isplanar = YES;
	newinf->alpha = hasalpha;
	newinf->palsteps = 0;
	newinf->palette = NULL;

	for (i = 0; i < 3; i++) {
		dither[i] = tag ? [MDAmethod alloc] : [Dither alloc];
		if (dither[i] == nil
		|| [dither[i] init:MDAbits[mdaidx][i] width:newinf->width] == nil)
			goto ErrEXIT;
	}

	msgtext = [theController messageDisplay:"Reducing..."];
	for (y = 0; y < newinf->height; y++) {
		unsigned char *qn, *ptr;
		int elm[MAXPLANE];
		for (i = 0; i < 3; i++)
			q[i] = [dither[i] buffer];
		for (x = 0; x < newinf->width; x++) {
			getPixel(&elm[0], &elm[1], &elm[2], &elm[3]);
			for (i = 0; i < 3; i++)
				q[i][x] = elm[i];
			apool[x] = elm[3];
		}
		for (i = 0; i < 3; i++) {
			qn = [dither[i] getNewLine];
			ptr = &working[i][y * newinf->width];
			for (x = 0; x < newinf->width; x++)
				*ptr++ = *qn++;
		}
		if (pl == 4) {
			for (i = 0; i < pl; i++)
				q[i] = &working[i][y * newinf->width];
			ptr = q[ALPHA];
			for (x = 0; x < newinf->width; x++)
				if (apool[x] == AlphaTransp) {
					for (i = 0; i < 3; i++)
						q[i][x] = 255;
					ptr[x] = AlphaTransp;
				}else
					ptr[x] = AlphaOpaque;
		}
	}
	if (hasalpha && colnum == 256) { /* reduce to 255 */
		int cnum = 0;
		BOOL al;
		ColorMap *colormap = nil;
		commonInfo tmpinf;

		tmpinf = *newinf;
		tmpinf.xbytes = newinf->width;
		tmpinf.bits = 8;	/* Temporally */
		tmpinf.alpha = NO;
		if (initGetPixel(&tmpinf) > 0
		|| (colormap = [[ColorMap alloc] init]) == nil
		|| [colormap allocFullColor] == nil)
		    cnum = 0;
		else
		    cnum = [colormap getAllColor:working limit:0 alpha:&al];
		if (colormap) [colormap free];
		if (cnum == 256) {
		    const unsigned char *lv;
		    [msgtext setStringValue: "Reducing into 255 colors"];
		    NXPing();
		    lv = [dither[RED] threshold];
		    for (y = 0; y < newinf->height; y++) {
			for (i = 0; i < 3; i++)
			    q[i] = &working[i][y * newinf->width];
			for (x = 0; x < newinf->width; x++) {
			    if (q[GREEN][x] == 0 && q[BLUE][x] == 0
				&& q[RED][x] == lv[1])
				q[RED][x] = ((x ^ y) & 1) ? lv[2] : 0;
			}
		    }
		}
		colnum = 255;
	}
	for (i = 0; i < 3; i++)
		[dither[i] free];

	[msgtext setStringValue: "Packing Bits..."];
	NXPing();
	packWorkingImage(newinf, pl, working, newmap);
	free((void *)working[0]);
	[tw freeTempBitmap];
	[theController messageDisplay:NULL];

	sprintf(newinf->memo, "%d x %d  %dcolors(%dbit%s)%s",
			newinf->width, newinf->height, colnum,
			newinf->bits, ((newinf->bits > 1) ? "s" : ""),
			(newinf->alpha ? "  Alpha" : ""));
	comment_copy(newinf->memo, cinf->memo);
	return newinf;

ErrEXIT:
	if (err) errAlert(fnam, err);
	[tw freeTempBitmap];
	if (newmap[0]) {
		free((void *)newmap[0]);
		newmap[0] = NULL;
	}
	if (working[0]) free((void *)working[0]);
	if (newinf) free((void *)newinf);
	return NULL;
}


- colorHalftoneWith:(int)colnum method:(int)tag
	/* by Dither or MDA (Mean Density Approximation Method) */ 
{
	ToyWin		*tw, *newtw;
	commonInfo	*cinf, *newinf = NULL;
	unsigned char	*map[MAXPLANE], *newmap[MAXPLANE];
	const char *fnam;
	char	fn[256];
	BOOL	needflag = NO, hasalpha;
	int	cnum, err = 0;

	if ((tw = [self keyParentWindow: Reduction]) == nil) {
		NXBeep();
		return self;
	}
	fnam = [tw filename];
	cinf = [[tw toyView] commonInfo];
	if (![self checkCommonInfo:cinf filename:fnam])
		return self;
	if (cinf->numcolors == 1) {
		err = Err_OPR_IMPL;
		goto ErrEXIT;
	}
	if (cinf->palette && cinf->palsteps <= colnum) {
		if ([self needReduce:fnam colors:cinf->palsteps ask:YES])
			needflag = YES;
		else
			return self;
	}

	if ((err = [tw getBitmap:map info: &cinf]) == 0)
		err = initGetPixel(cinf);
	if (err) goto ErrEXIT;
	hasalpha = cinf->alpha;
	if (!needflag) {
		ColorMap *colormap = [[ColorMap alloc] init];
		if ([colormap allocFullColor] != nil) {
			cnum = [colormap getAllColor:map
					limit:0 alpha:&hasalpha];
			[colormap free];
		}else {
			err = Err_MEMORY;
			goto ErrEXIT;
		}
		if (cnum <= colnum
			&& ![self needReduce:fnam colors:cnum ask:YES])
			goto ErrEXIT;
	}

	resetPixel(map, 0);
	newinf = [self colorHalfMap:newmap method:tag
		from:tw with:cinf colors:colnum alpha:hasalpha];
	if (newinf == NULL)
		goto ErrEXIT;
	newtw = [[ToyWin alloc] init:tw by:Reduction];
	sprintf(fn, "%s(%s%d)", fnam, (tag ? "Halftone" : "Dither"), colnum);
	[newtw initLocateWindow:fn width:newinf->width height:newinf->height];
	// [newtw makeComment: newinf];
	if ([newtw drawView:newmap info: newinf] == nil) {
		err = Err_MEMORY;
		[newtw free];
		free((void *)newmap[0]);
		goto ErrEXIT;
	}else
		[theController newWindow: newtw];
	return self;

ErrEXIT:
	if (err) errAlert(fnam, err);
	[tw freeTempBitmap];
	if (newinf) free((void *)newinf);
	return NULL;
}

@end
