#import <stdio.h>
#import <stdlib.h>
#import <string.h>
//#import <defaults.h>
#import <sys/types.h>
#import <sys/dir.h>
#import <sys/param.h>
#import <objc/objc-load.h>
#import <appkit/appkit.h>
#import "NXBitmapImageRepControl.h"
#import "Converter.h"
#import "ImageControl.h"

@implementation NXBitmapImageRepControl

	id		imageControl;

- splitFilename: (const char *)filename to: (char *)name andPath: (char *)path
{
	int		x;
	
	for (x = strlen(filename); (x != -1) && filename[x] != '/'; x--) ;
	strcpy(name, filename + x + 1);
	if (x == -1) path = NULL;
	else strncpy(path, filename, x);
	path[x] = '\0';
	
	return self;
}

- (Type *)getConverterType: (const char *)typeIn
{
	int		x;
	
	for (x  = 0; x < typesCount; x++) {
		if (!strcmp(typeIn, types[x]->type)) {
			return types[x];
		}
	}
	
	return NULL;
}

- getTypesFromPath: (char *)path
{
	DIR			*myDir;
	struct direct	*myEntry;
	int			x, y;
	char			buffer[MAXPATHLEN];

#ifdef DEBUG
	fprintf(stderr, "Opening %s\n", path);
#endif
	myDir = opendir(path);
	if (myDir) {
		while (myEntry = readdir(myDir)) {
			for (x = strlen(myEntry->d_name); x && myEntry->d_name[x] != '.'; x--);
			if (!strcmp(myEntry->d_name + x, ".bcvt")) {
				for (y = 0; y < typesCount; y++) {
					strncpy(buffer, myEntry->d_name, x); buffer[x] = '\0';
					if (!strcmp((const char *)types[y]->type, (const char *)buffer)) {
						fprintf(stderr, "Type collision for %s\n", myEntry->d_name);
						break;
					}
				}
				if (y == typesCount) {
					if (typesCount >= curMax) {
#ifdef DEBUG
						fprintf(stderr, "Reallocing\n");
#endif
						curMax += 10;
						types = (Type **)realloc(types, sizeof(Type *) * curMax);
					}
					types[typesCount] = (Type *)malloc(sizeof(Type));
					sprintf(buffer, "%s%s", path, myEntry->d_name);
					strcpy(types[typesCount]->fullpath, buffer);
					strncpy(types[typesCount]->type, myEntry->d_name, x);
					types[typesCount]->type[x] = '\0';
#ifdef DEBUG
					fprintf(stderr, "   converter = %s in %s\n", 
								types[typesCount]->type,
								types[typesCount]->fullpath);
#endif
					typesCount++;
				}
			}
		}
		closedir(myDir);
	}
	else {
#ifdef DEBUG
		fprintf(stderr, "Unable to open dir\n");
#endif
	}

	return self;
}

int typeCompare(const void *t1, const void *t2)
{
	return strcmp((**((Type **)t1)).type, (**((Type **)t2)).type);
}

- getTypesFromDisk
{
	char		buffer[1000];

	sprintf(buffer, "%s/Library/Converters/", NXHomeDirectory());
	[self getTypesFromPath: buffer];
	[self getTypesFromPath: "/LocalLibrary/Converters/"];
	[self getTypesFromPath: "/NextLibrary/Converters/"];

	qsort(&(types[0]), typesCount, sizeof(Type *), typeCompare);

	return self;
}

- (char *)getImageControlFromPath: (char *)path
{
	DIR			*myDir;
	struct direct	*myEntry;
	static char	buffer[MAXPATHLEN];

#ifdef DEBUG
	fprintf(stderr, "Opening %s\n", path);
#endif
	myDir = opendir(path);
	if (myDir) {
		while (myEntry = readdir(myDir)) {
			if (!strcmp(myEntry->d_name, "Bitmap.tools")) {
				strcpy(buffer, path);
				strcat(buffer, myEntry->d_name);
#ifdef DEBUG
				fprintf(stderr, "Found image tools: %s\n", buffer);
#endif
				break;
			}
		}
		closedir(myDir);
	}
	else {
#ifdef DEBUG
		fprintf(stderr, "Unable to open dir\n");
#endif
	}

	return buffer;
}

- (char *)getImageControls
{
	char		buffer[1000];
	char		*imagePath;

	imagePath = [self getImageControlFromPath: "/NextLibrary/Converters/"];
	imagePath = [self getImageControlFromPath: "/LocalLibrary/Converters/"];
	sprintf(buffer, "%s/Library/Converters/", NXHomeDirectory());
	imagePath = [self getImageControlFromPath: buffer];

	return imagePath;
}

void LinkImageControlCallBack(Class cl, Category ca)
{
#ifdef DEBUG
	fprintf(stderr, "Call back for image controls reached\n");
#endif
	imageControl = (id)cl;
#ifdef DEBUG
	fprintf(stderr, "Linked object at %p\n", cl);
#endif
}

- init
{
	NXStream	*myStream;
	int			x;
	char			*cvtFiles[] = {	NULL,
								NULL };

	cvtFiles[0] = [self getImageControls];
	myStream = NXOpenFile(fileno(stderr), NX_WRITEONLY);
#ifdef DEBUG
	fprintf(stderr, "stderr linked to stream myStream\n");
#endif
	if (objc_loadModules(cvtFiles, myStream, LinkImageControlCallBack, NULL, NULL)) {
		NXFlush(myStream);
		return nil;
	}
	NXFlush(myStream);
	NXClose(myStream);

	curMax = 10;
	types = (Type **)malloc(sizeof(Type *) * curMax);
	curType = NULL;
	[self getTypesFromDisk];
	mySubView = nil;
	useNeXTStep = YES;
	
	// This first loop is run to link in the Converter base class.
	for (x = 0; x < typesCount; x++) {
		fprintf(stderr, "Linking type %s...", types[x]->type);
		if (!strcmp(types[x]->type, "Converter")) {
			if (![self linkConverter: types[x]]) {
				[self free];
				return nil;
			}
			fprintf(stderr, "done.\n");
			break;
		} else fprintf(stderr, "Skipping.\n");
	}

	// Now this loop is run to link in the remaining, subclasses of the Converter base class
	for (x = 0; x < typesCount; x++) {
		fprintf(stderr, "Linking type %s...", types[x]->type);
		if (!strcmp(types[x]->type, "tiff")) {
			curType = types[x];
		}
		if (strcmp(types[x]->type, "Converter")) {
			if (![self linkConverter: types[x]]) {
				if (!strcmp(types[x]->type, "tiff")) { // If tiff doesn't link, we're screwd,
					return [self free];		    // so give up. Otherwise, make the converter
				}						    // unavailble for use.
				fprintf(stderr, "Making %s converter unavailble...\n", types[x]->type);
				types[x]->converter = nil;
			}
			fprintf(stderr, "done.\n");
		} else fprintf(stderr, "Skipping.\n");
	}

	return self;
}

	id 		convert;			// This badly placed global is here to support the
							// objc_loadModules call. It's gotta be global since fucntions
							// cannot access class variables and c doesn't allow nested
							// functions.

void LinkCallBack(Class cl, Category ca)
{
	convert = [(id)cl new];
}

- linkConverter: (Type *)cvtType
{
	NXStream	*myStream;
	char			*cvtFiles[] = {	NULL,
								NULL };

	cvtFiles[0] = cvtType->fullpath;
	myStream = NXOpenFile(fileno(stderr), NX_WRITEONLY);
	if (objc_loadModules(cvtFiles, myStream, LinkCallBack, NULL, NULL)) {
		NXFlush(myStream);
		return nil;
	}
	NXFlush(myStream);
	NXClose(myStream);
	
	cvtType->converter = convert;

	[cvtType->converter init];

	return self;
}

- unlinkConverter
{
	// This no longer does anything. It's here for compatibility sakes. Basically, it was
	// nuked when the decission to link all converters at start up was made. This allows
	// the proper use loadNib... calls from your application.
	return self;
}

- handleLink: (char *)inType;
{
	int		x;
	
	for (x = 0; x < typesCount; x++) {
		if (!strcasecmp(types[x]->type, inType)) {
			if (types[x]->converter == nil) {
				return nil;
			}
			curType = types[x];
			return self;
		}
	}
	if (useNeXTStep) {
		NXRunAlertPanel("Error", "Converter for type %s does not exist\n", "Abort", NULL, NULL, inType);
	} else {
		fprintf(stderr, "Converter for type %s does not exist\n", inType);
	}

	return nil;
}

- openAndReturnImageOfType: (char *)inType fromStream: (NXStream *)stream
{
	if ([self handleLink: inType]) {
		return [curType->converter readFromStream: stream from: self];
	}
	return nil;
}

- openAndReturnImage: (const char *)filename
{
	char			*inType;
	int			x;
	NXStream	*myStream;
	id			retImage;
		
	for (x = strlen(filename); x && filename[x] != '.'; x--);
	inType = (char *)filename + x + 1;

	if ([self handleLink: inType]) {
#ifdef DEBUG
		fprintf(stderr, "here we go\n");
#endif
		myStream = NXMapFile(filename, NX_READONLY);
		retImage = [curType->converter readFromStream: myStream from: self];
		NXClose(myStream);
		
		return retImage;
	}
	return nil;
}

- (char *)getCurrentFormatName
{
	if (curType) {
		return [curType->converter getFormatName];
	}
	else {
		return "No Format Loaded";
	}
}

- popClick
{
	char		*inType;
	NXRect	r;
	id		tView;
	
	inType = (char *)[myPopUp selectedItem];
#ifdef DEBUG
	fprintf(stderr, "Yeah, I got clicked, and I selected: %s\n", inType);
#endif
	if (!strcmp(inType, "All Types")) {
		[nameText setStringValue: "Any Bitmap Format May be Selected"];
		[panelSave allowMultipleFiles: YES];
		return self;
	}
	[panelSave setRequiredFileType: inType];
	if ([self handleLink: inType]) {
		if (!amSaving) [panelSave allowMultipleFiles: NO];
		[nameText setStringValue: [self getCurrentFormatName]];
		if (mySubView) {
			[mySubView removeFromSuperview];
			[mySubView free];
#ifdef DEBUG
			fprintf(stderr, "Old subview unlinked\n");
#endif
		}
		[myBox getBounds: &r];
		if (amSaving) {
			mySubView = [curType->converter customSaveView: r.size.width - 15];
		} else {
			mySubView = [curType->converter customOpenView: r.size.width - 15];
		}
		if (mySubView) {
			int		tmp = 0;
		
			if (currentView) {
				NXRect		tmpRect;

#ifdef DEBUG
				fprintf(stderr, "Adding custom view\n");
#endif
				[currentView getBounds: &tmpRect];
				tmp = tmpRect.size.height;
			}
		
			[mySubView moveBy: 0: 53 + tmp];
			[myBox addSubview: mySubView];
#ifdef DEBUG
			fprintf(stderr, "New subview added: %p\n", mySubView);
#endif
		}
		
		[myBox getFrame: &r];
		tView = [Box alloc];
		[tView initFrame: &r];
		[tView setTitle: "Format"];
		[panelSave setAccessoryView: tView];
		[myBox sizeToFit];
		[panelSave setAccessoryView: myBox];
		[tView free];
	}
	return self;
}

- myButtonIIPush
{
	const char	*tmp = [myPopUp selectedItem];

	if (!tmp) tmp = "All Types";
	if (!strcmp(tmp, "All Types")) {
		NXRunAlertPanel("Information", 
						"NXBitmapImageRepControl\nby Alex Raftis\n\nCopyright 1991, California Polytechnic State University, San Luis Obispo and Alex Raftis",
						"Continue", NULL, NULL, NULL);
	} else {
		NXRunAlertPanel("Converter Information", 
						[curType->converter copyrightNotice],
						"Continue", NULL, NULL, NULL);
	}
	return self;
}

- createViewForType: 		(char *)inType 
			inRect: 		(NXRect *)r
			withString:	(const char *)secondPrompt
{
	id		myButtonII 	= [Button alloc];
	id		myText		= [TextField alloc];
	NXRect	myRect 		= {0, 0, r->size.width - 10, 54 };
	NXRect	myButRect 	= {r->size.width - 99, 35,   80, 21 };
	NXRect	myButRectII 	= {9, 35,   70, 21 };
	NXRect	myTextRect 	= {5, 36, r->size.width - 109, 18 };
	NXRect	myTextRectII	= {9, 8, r->size.width - 28, 22 };
	int		x;

	mySubView = nil;

	myBox = [Box alloc];
	[myBox initFrame: &myRect];	
		
	myView = [View alloc];
	[myView initFrame: &myRect];
	viewDefault = myRect;

	myPopUp  = [PopUpList alloc];
	[myPopUp init];
	for (x = 0; x < typesCount; x++) {
		if (types[x]->converter && (strcmp(types[x]->type, "Converter"))) {
			[myPopUp addItem: types[x]->type];
#ifdef DEBUG
			fprintf(stderr, "%s\n", types[x]->type);
#endif
		}
	}
	[myPopUp setAction: @selector(popClick)];
	[myPopUp setTarget: self];
	myButton = [Button alloc];
	[myButton initFrame: &myButRect];
	NXAttachPopUpList(myButton, myPopUp);
	[myView addSubview: myButton];
	[myButton setTitle: inType];

	[myText initFrame: &myTextRect];
	[myText setEditable: NO];
	[myText setStringValue: secondPrompt];
	[myText setBezeled: NO];
	[myText setAlignment: NX_RIGHTALIGNED];
	[myText setBackgroundGray: NX_LTGRAY];
	[myView addSubview: myText];

	nameText = [TextField alloc];
	[nameText initFrame: &myTextRectII];
	[nameText setEditable: NO];
	[nameText setStringValue: [curType->converter getFormatName]];
	[nameText setBezeled: YES];
	[nameText setAlignment: NX_CENTERED];
	[nameText setBackgroundGray: NX_WHITE];
	[myView addSubview: nameText];

	[myView addSubview: myButtonII];
	[myButtonII 	initFrame: 	&myButRectII
				title: 		"Info"
				tag:			1
				target:		self
				action:		@selector(myButtonIIPush)
				key:			'\1'
				enabled:		YES];
	[myButtonII setType: NX_MOMENTARYPUSH];

	[myBox setContentView: myView];
	[myBox setTitle: "Format"];
	[myBox sizeToFit];

	[myBox getBounds: &myRect];

	if (currentView) {
		NXRect 		tmpRect;
		
		[[myBox contentView] getBounds: &tmpRect];
		[currentView moveBy: -11: tmpRect.size.height + 30];
		[myBox addSubview: currentView];
	}

	if (amSaving) {
		mySubView = [curType->converter customSaveView: myRect.size.width - 15];
	} else {
		mySubView = [curType->converter customOpenView: myRect.size.width - 15];
		if (mySubView) [currentPanel allowMultipleFiles: NO];
		else [currentPanel allowMultipleFiles: YES];
	}

	if (mySubView) {
		int		tmp = 0;
		
		if (currentView) {
			NXRect		tmpRect;
			
			[currentView getBounds: &tmpRect];
			tmp = tmpRect.size.height;
		}
		
		[mySubView moveBy: 0: 53 + tmp];
		[myBox addSubview: mySubView];
		
#ifdef DEBUG
		fprintf(stderr, "New subview added: %p\n", mySubView);
#endif
	}
		
	[myBox sizeToFit];

	return myBox;
}

- (int)runOpenPanel: (id)openPanel
{
	char		**fTypes;
	int		x;
	NXRect	tRect;
	id		openView = [openPanel accessoryView];
	int		retVal;
	
	amSaving = NO; 						// Make sure to use custom open view.
	currentPanel = openPanel;				// Set up to use the open panel for messages.
	currentView = [openPanel accessoryView];
	[openPanel setAccessoryView: nil];
	
	fTypes = [self getTypeList];
	
	if (!curType) {
		[self handleLink: "tiff"];
	}
	
	panelSave = openPanel;
	[[openPanel contentView] getBounds: &tRect];
	myView = [self createViewForType:	curType->type 
					inRect: 			&tRect
					withString:		"Set Parameters For:"];
	[myPopUp addItem: "All Types"];
	[myButton setTitle: "All Types"];
	[nameText setStringValue: "Any Bitmap Format May be Selected"];
#ifdef DEBUG
	fprintf(stderr, "Custom view created\n");
#endif
	[openPanel setAccessoryView: myView];
	[openPanel setDelegate: self];
	retVal = [openPanel runModalForTypes: fTypes];
	[openPanel setAccessoryView: openView];
	[myView free];
	free(fTypes);

	currentView = nil;

	free(fTypes);

	return retVal;
}

- (int)runSavePanel: (id)savePanel withFilename: (const char *)filename
{
	int		retVal;
	id		saveView = [savePanel accessoryView];
	char		path[MAXPATHLEN];
	int		x;
	char		*inType;
	NXRect	tRect;

	amSaving = YES; 						// Make sure to use custom save view.
	currentPanel = savePanel;				// Set up to use the open panel for messages.
	currentView = [savePanel accessoryView];
	[savePanel setAccessoryView: nil];
	
	if (!curType)
		if (![self handleLink: "tiff"]) return NO;

#ifdef DEBUG
	fprintf(stderr, "And splitting filename\n");
#endif
	panelSave = savePanel;
	[self splitFilename: filename to: picName andPath: path];
#ifdef DEBUG
	fprintf(stderr, "and getting type\n");
#endif
	for (x = strlen(picName); x && picName[x] != '.'; x--);
	inType = (char *)picName + x + 1;
	picName[x] = '\0';
	[[savePanel contentView] getBounds: &tRect];
#ifdef DEBUG
	fprintf(stderr, "Types: %s %s\n", inType, curType->type);
#endif
	if (strcmp(inType, curType->type)) {
		[self handleLink: inType];
	}
	myView = [self createViewForType: 	inType 
					inRect: 			&tRect
					withString:		"Format to Save in:"];
	[savePanel setAccessoryView: myView];
#ifdef DEBUG
	fprintf(stderr, "name %s in %s\n", picName, path);
#endif
	[savePanel setRequiredFileType: inType];
	[savePanel setDelegate: self];
	retVal = [savePanel runModalForDirectory: path file: picName];
	[savePanel setAccessoryView: saveView];
	[myView free];

	currentView = nil;

	return retVal;
}

- (BOOL)saveImage: image ofType: (char *)inType toStream: (NXStream *)stream
{
	if ([self handleLink: inType]) {
		return [curType->converter write: image toStream: stream from: self];
	}
	return NO;
}

- (BOOL)saveImage: image toFile: (const char *)filename
{
	FILE		*myFile;
	NXStream	*myStream;
	BOOL		boolSave;

	myFile = fopen(filename, "w");
	if (myFile) {
		myStream = NXOpenFile(fileno(myFile), NX_WRITEONLY);
		if (myStream) {
			boolSave = [curType->converter write: image toStream: myStream from: self];
			NXClose(myStream);
			fclose(myFile);
			return boolSave;
		}
		fclose(myFile);
		return NO;
	}
	return NO;
}

- (char **)getTypeList
{
	char		**fTypes = (char **)malloc(sizeof(char *) * ((typesCount * 2) + 1));
	int		x, y, z;
	
	for (x = 0, y = 0; x < typesCount; x++) {
		if (strcmp(types[x]->type, "Converter")) {
			fTypes[y] = (char *)malloc(sizeof(char) * (strlen(types[x]->type) + 1));
			strcpy(fTypes[y++], types[x]->type);
			fTypes[y] = (char *)malloc(sizeof(char) * (strlen(types[x]->type) + 1));
			strcpy(fTypes[y++], types[x]->type);
			for (z = 0; z < strlen(fTypes[y-1]); z++) {
				fTypes[y-1][z] = toupper(fTypes[y-1][z]);
			}
#ifdef DEBUG
			fprintf(stderr, "Type: %s\n", types[x]->type);
#endif
		}
	}
#ifdef DEBUG
	fprintf(stderr, "\n");
#endif
	fTypes[y] = NULL;

	return fTypes;	
}

- windowDidResize: sender toSize:(NXSize *)frameSize
{
#ifdef DEBUG
	fprintf(stderr, "%p\n", [panelSave accessoryView]);
#endif
	[myView setAutodisplay: YES];
	[myView display];
	
	return self;
}

- windowWillResize: sender toSize:(NXSize *)frameSize
{
#ifdef DEBUG
	fprintf(stderr, "%p\n", [panelSave accessoryView]);
#endif
	
	return self;
}

- getImageControl: (id)image
{
#ifdef DEBUG
	fprintf(stderr, "Converter requested image controls.\n");
#endif

	return [imageControl new: image];
}

- getCurrentConverter
{
	return curType->converter;
}

- (const char *)filename
{
	return picName;
}

- setUseNeXTStep: (BOOL)state
{
	useNeXTStep = state;

	return self;
}

- (BOOL)usesNeXTStep
{
	return useNeXTStep;
}

- (int)errorState
{
	if (convert) {
		return [curType->converter errorState];
	}
	return CONVERT_ERR_FATAL;
}

- (int)errorMessage
{
	if (convert) {
		return [curType->converter errorMessage];
	}
	return ERROR_UNABLETOLINK;
}

- (char *)errorStringMessage
{
	if (convert) {
		return [curType->converter errorStringMessage];
	}
	return "Unable to link converter";
}

@end