#import "../ImageSave.h"
#import <appkit/NXImage.h>
#import <appkit/SavePanel.h>
#import <appkit/NXEPSImageRep.h>
#import <appkit/NXBitmapImageRep.h>
#import <stdio.h>
#import <libc.h>
#import <string.h>
#import <sys/file.h>
#import "../ToyView.h"
#import "../ToyWin.h"
#import "../ToyWinEPS.h"
#import "../ColorMap.h"
#import "../strfunc.h"
#import "../common.h"
#import "save.h"
#import "JpegSavePanel.h"
#import "TiffSavePanel.h"

/* This data is used by ImageSave family */
char saveDir[MAXFILENAMELEN];
char *appDir = NULL;


@implementation ImageSave

+ (char *)mk_tmpname:(const char *)path ext:(const char *)ex nopath:(BOOL)flag
{
	static char	srcfn[MAXFILENAMELEN];
	int	i, j;

	if (flag) {
		for (i = j = 0; path[i]; i++)
			if (path[i] == '/') j = i + 1;
	}else
		j = 0;
	strcpy(srcfn, path + j);
	for (i = j = 0; srcfn[i]; i++)
		if (srcfn[i] == '.') j = i + 1;
	if (j == 0)
		srcfn[i] = '.',  j = i + 1;
	while (*ex)
		srcfn[j++] = *ex++;
	srcfn[j] = 0;
	return srcfn;
}


- initWithWin: aToyWin
{
	[super init];
	toyWin = aToyWin;
	toyView = [toyWin toyView];
	if (!saveDir[0] || access(saveDir, X_OK) != 0)
		strcpy(saveDir, getenv("HOME"));
	if (appDir == NULL) {
		appDir = str_dup(NXArgv[0]); /* alloc only */
		(void) dircopy(appDir, NXArgv[0], NO);
	}
	return self;
}


- (char *)getSavename: (const char *)path with:(int)itype
{
	SavePanel *savePanel;
	char	*stmp, *sav = NULL;
	const char *ex = NULL;

	switch (itype) {
	case Type_eps: ex = "eps";  break;
	case Type_bmp: ex = "bmp";  break;
	case Type_gif: ex = "gif";  break;
	case Type_ppm: ex = "pnm";  break;
	case Type_jpg: ex = "jpg";  break;
	case Type_xbm: ex = "xbm";  break;
	case Type_jbg: ex = "bie";  break;
	case Type_png: ex = "png";  break;
	}
	stmp = [ImageSave mk_tmpname:path ext:ex nopath:YES];
	savePanel = [SavePanel new];
	if ([savePanel runModalForDirectory: saveDir file: stmp]) {
		sav = [ImageSave mk_tmpname:
				[savePanel filename] ext:ex nopath:NO];
		if (sav == NULL || *sav == 0)
			return NULL;
		strcpy(saveDir, [savePanel directory]);
	}
	return sav;
}

/* Local Method */
- (char *)getSaveTiffName: (const char *)path jpeg: (BOOL)flag
	compress: (int *)type by: (float *)factor
{
	TiffSavePanel *savePanel;
	char	*stmp, *sav = NULL;
	static	float jpegFactor = 25.0; /* Default */

	stmp = [ImageSave mk_tmpname:path ext:"tiff" nopath:YES];
	savePanel = [[TiffSavePanel new] init: jpegFactor jpeg: flag];
	if ([savePanel runModalForDirectory: saveDir file: stmp]) {
		sav = [ImageSave mk_tmpname:
				[savePanel filename] ext:"tiff" nopath:NO];
		if (sav == NULL || *sav == 0)
			return NULL;
		strcpy(saveDir, [savePanel directory]);
		[savePanel compressType: type with: factor];
		if (*type == NX_TIFF_COMPRESSION_JPEG)
			jpegFactor = *factor;
	}
	return sav;
}

/* Local Method */
- (char *)getSaveJpegName: (const char *)path by: (float *)factor : (BOOL *)progress
{
	JpegSavePanel *savePanel;
	char	*stmp, *sav = NULL;
	static	float jpegFactor = 25.0; /* Default */

	stmp = [ImageSave mk_tmpname:path ext:"jpg" nopath:YES];
	savePanel = [[JpegSavePanel new] initFactor: jpegFactor];
	if ([savePanel runModalForDirectory: saveDir file: stmp]) {
		sav = [ImageSave mk_tmpname:
				[savePanel filename] ext:"jpg" nopath:NO];
		if (sav == NULL || *sav == 0)
			return NULL;
		strcpy(saveDir, [savePanel directory]);
		*factor = jpegFactor = [savePanel compressFactor];
		*progress = [savePanel progressive];
	}
	return sav;
}

/* Local Method */
- saveEPSAsTiff: sender
{
	NXStream *stream;
	FILE	*fd;
	char	*sav;
	int	type, ch;
	float	factor;

	sav = [self getSaveTiffName: [toyWin filename] jpeg: NO
		compress: &type by: &factor];
	if (sav == NULL) /* canceled */
		return self;
	if ((fd = fopen(sav, "w")) == NULL) {
		errAlert(sav, Err_SAVE);
		return self;
	}
	/* type = none/lzw */
	stream = [(ToyWinEPS *)toyWin openTiffStream:
			(type != NX_TIFF_COMPRESSION_NONE)];
	if (stream == NULL) {
		(void) fclose(fd);
		errAlert(sav, Err_SAVE);
		return self;
	}
	while ((ch = NXGetc(stream)) != EOF)
		putc(ch, fd);
	NXCloseMemory(stream, NX_FREEBUFFER);
	(void)fclose(fd);
	return self;
}

- saveAsTiff: sender
{
	NXStream *stream;
	int	fd = -1;
	char	*sav;
	commonInfo *cinf;
	NXBitmapImageRep *rep;
	BOOL	jflag;
	int	type;
	float	factor;

	cinf = [toyView commonInfo];
	if (cinf->type == Type_eps)
		return [self saveEPSAsTiff: sender];

	rep = (NXBitmapImageRep *)[[toyView image] bestRepresentation];
	jflag = (cinf->bits > 2  /* && cinf->type != Type_eps */
		&& [rep canBeCompressedUsing: NX_TIFF_COMPRESSION_JPEG]);
	sav = [self getSaveTiffName: [toyWin filename] jpeg: jflag
		compress: &type by: &factor];
	if (sav == NULL) /* canceled */
		return self;
	/* BUG ?  Over Writing LZW on no-compression */
	if (access(sav, F_OK|W_OK) == 0)
		unlink(sav);
	if ((fd = open(sav, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0
	|| (stream = NXOpenFile(fd, NX_WRITEONLY)) == NULL) {
		if (fd >= 0) (void) close(fd);
		errAlert(sav, Err_SAVE);
		return self;
	}
	if (factor < 1.0) factor = 1.0;
	else if (factor > 255.0) factor = 255.0;
	[rep writeTIFF:stream usingCompression:type andFactor:factor];
	NXClose(stream);
	(void)close(fd);
	if (type != NX_TIFF_COMPRESSION_JPEG)
		[toyWin resetFilename:sav];
	return self;
}

- saveAsEPS: sender
{
	NXStream *stream;
	char	*sav;
	commonInfo *cinf;

	sav = [self getSavename: [toyWin filename] with:Type_eps];
	if (sav == NULL) /* canceled */
		return self;
	if ((stream = [toyWin openEPSStream]) == NULL) {
		errAlert(sav, Err_MEMORY);
		return self;
	}
	if (NXSaveToFile(stream, sav))
		errAlert(sav, Err_SAVE);
	NXCloseMemory(stream, NX_FREEBUFFER);
	cinf = [toyView commonInfo];
	if (cinf->type == Type_eps)
		[toyWin resetFilename:sav];
	return self;
}


- saveAsType: (int)itype	/* BMP, PPM, JPG, or JBIG */
{
	FILE	*fp;
	char	*sav;
	commonInfo *cinf;
	ColorMap *colormap = nil;
	unsigned char *map[MAXPLANE];
	float	jpegFactor;
	BOOL	progress = NO;
	int	cnum = 0, err = 0;

	cinf = [toyView commonInfo];
	if (cinf->cspace == NX_CMYKColorSpace) {
		warnAlert([toyWin filename], Err_SAV_IMPL);
		return self;
	}
	if (itype == Type_jbg
	&& (cinf->bits > 2 || cinf->numcolors != 1)) {
		/* ignore alpha, ignore 2 bits gray. Because of EPS... */
		warnAlert([toyWin filename], Err_SAV_IMPL);
		return self;
	}

	if (itype == Type_jpg)
		sav = [self getSaveJpegName:[toyWin filename]
				by:&jpegFactor : &progress];
	else
		sav = [self getSavename:[toyWin filename] with: itype];
	if (sav == NULL) /* canceled */
		return self;
	if ((fp = fopen(sav, "w")) == NULL) {
		errAlert(sav, Err_SAVE);
		return self;
	}

	err = [toyWin getBitmap:map info: &cinf];
	if (!err) err = initGetPixel(cinf);
	if (err) goto EXIT;

	if (itype == Type_ppm) {
		resetPixel(map, 0);
		err = ppmwrite(fp, cinf, map);
		goto EXIT;
	}else if (itype == Type_jpg) {
		int q;
		resetPixel(map, 0);
		q = (255.0 - jpegFactor) / 2.55 + 0.5;
		q = (q < 0) ? 0 : ((q > 100) ? 100 : q);
		err = jpgwrite(fp, cinf, appDir, q, progress);
		fp = NULL;
		goto EXIT;
	}else if (itype == Type_jbg) {
		resetPixel(map, 0);
		err = jbigwrite(fp, cinf, map[0], appDir);
		fp = NULL;
		goto EXIT;
	}
	/* bmp only */
	colormap = [[ColorMap alloc] init];
	cnum = [self getPalette:colormap
			info:cinf map:map needAlpha:NO err:&err];
	err = 0;	/* Error is ignored */
	if (cnum == 0)
		saveBmpbmap(fp, cinf, FIXcount + 1, NULL, map);
	else
		saveBmpbmap(fp, cinf, cnum, cinf->palette, map);

EXIT:
	if (fp) (void)fclose(fp);
	if (colormap) [colormap free];
	[toyWin freeTempBitmap];
	if (err) {
		errAlert(sav, err);
		(void)unlink(sav);
	}else if (itype != Type_jpg)
		[toyWin resetFilename:sav];
	return self;
}

@end
