/***************************************************************************
                          QtSupport.m  -  description
                             -------------------
    begin                : Wed Mar 29 2000
    copyright            : (C) 2000 by Richard Dale
    email                : Richard_Dale@tipitina.demon.co.uk
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSInvocation.h>
#include <Foundation/NSMethodSignature.h>
#include <Foundation/NSCalendarDate.h>
#include <Foundation/NSException.h>

#include <qtc/qt_Slot.h>
#include <qtc/qt_QObject.h>
#include <qtc/qt_QByteArray.h>
#include <qtc/qt_QEvent.h>
#include <qtc/qt_QFileInfoList.h>
#include <qtc/qt_QObjectList.h>
#include <qtc/qt_QString.h>
#include <qtc/qt_QCString.h>
#include <qtc/qt_QStrList.h>
#include <qtc/qt_QStringList.h>
#include <qtc/qt_QDate.h>
#include <qtc/qt_QTime.h>
#include <qtc/qt_QDateTime.h>
#include <qtc/qt_QPoint.h>
#include <qtc/qt_QPointArray.h>
#include <qtc/qt_QDomNode.h>
#include <qtc/qt_QDomNodeList.h>
#include <qtc/qt_QWidget.h>
#include <qtc/qt_QWidgetList.h>

#include <qtobjc/QWindowsStyle.h>
#include <qtobjc/QFileInfo.h>
#include <qtobjc/QDomNode.h>

static NSAutoreleasePool *			pool = nil;
static NSMutableDictionary *		qtKeyToObjcMap = nil;
static NSMutableDictionary *		qtSignalDictionary = nil;
static BOOL							bigEndianUnicode = NO;

static int
qtEventDelegate(void * qtObject, char * methodName,void * qtEvent, char * eventClass)
{
	QObject *			qobject;
	QEvent *			event;
	SEL					eventMethod;                                                                          	
	NSAutoreleasePool * eventPool;
	Class				objcClass;
		
//	eventMethod = NSSelectorFromString([NSString stringWithFormat: @"%s:", methodName]);
	eventMethod = sel_get_uid([[NSString stringWithFormat: @"%s:", methodName] cString]);
	
	qobject = [QtSupport objectForQtKey: qtObject withClass: [QObject class]];
	
	if ([[qobject class] instancesRespondToSelector: eventMethod]) {
		eventPool = [[NSAutoreleasePool alloc] init];
		objcClass = NSClassFromString([NSString stringWithCString: eventClass]);
		event = [[[[objcClass alloc] setQt: qtEvent] init] autorelease];
		[qobject performSelector: eventMethod withObject: event]; 	
		[eventPool release];
		return 1;
	}
		
	return 0;
}

void
ObjcSlotCallback(void * userData, void * value)
{
	NSInvocation *	invocation;
	invocation = (NSInvocation *) userData;

	if ([[invocation methodSignature] numberOfArguments] > 2) {
		[invocation setArgument: value atIndex: 2];
	}

	[invocation invoke];
}

@implementation QtSupport

+ (void) initialize
{
	qt_QString *	temp;
	NSString *		testString;
	
	pool = [[NSAutoreleasePool alloc] init];
	
	// Force all the event classes to be loaded
	[QChildEvent class];
	[QCloseEvent class];
	[QCustomEvent class];
	[QDragEnterEvent class];
	[QDragLeaveEvent class];
	[QDragMoveEvent class];
	[QDragResponseEvent class];
	[QDropEvent class];
	[QEvent class];
	[QFocusEvent class];
	[QHideEvent class];
	[QKeyEvent class];
	[QMouseEvent class];
	[QMoveEvent class];
	[QPaintEvent class];
	[QResizeEvent class];
	[QShowEvent class];
	[QTimerEvent class];
	[QWheelEvent class];
	qt_QObject_registerEventDelegate(qtEventDelegate);
	
	qtKeyToObjcMap = [[NSMutableDictionary alloc] initWithCapacity: 100];
	qtSignalDictionary = [[NSMutableDictionary alloc] initWithCapacity: 100];
	
	temp = qt_new_QString5("A");
	NS_DURING
		testString = [[NSString fromQString: temp] autorelease];
		bigEndianUnicode = strcmp([testString cString], "A") == 0;
	NS_HANDLER
		bigEndianUnicode = NO;
	NS_ENDHANDLER
	qt_del_QString(temp);
	return;
}

+ setObject: anId forQtKey: (void *) qt
{
//	NSLog(@"setObject: %@ key: %8.8x", anId, qt);
	[qtKeyToObjcMap setObject: anId forKey: [NSString stringWithFormat: @"%8.8x", qt]];
	[anId release];
	return self;
}

+ removeQtKey: (void *) qt
{
//	NSLog(@"removeQtKey: %8.8x", qt);
	[qtKeyToObjcMap removeObjectForKey: [NSString stringWithFormat: @"%8.8x", qt]];
	return self;
}

+ objectForQtKey: (void *) qt withClass: (Class) objcClass
{
	return [self objectForQtKey: qt withClass: objcClass allocatedInObjcWorld: NO];
}

+ objectForQtKey: (void *) qt withClass: (Class) objcClass allocatedInObjcWorld: (BOOL) yn
{
	id		objcInstance;

	if (qt == (void *) NULL) {
		return nil;
	}
	
	objcInstance = [qtKeyToObjcMap objectForKey: [NSString stringWithFormat: @"%8.8x", qt]];

	/*
	 * If there is no existing mapping for the current qt instance,
	 * then allocate an Objective-C instance with the given class.
	 */
	if (objcInstance == nil) {
		if ([objcClass instancesRespondToSelector: @selector(className)]) {
			objcClass = NSClassFromString([NSString stringWithCString: qt_QObject_className(qt)]);
			objcInstance = [[[[objcClass alloc] setQt: qt] init] retain];
			[objcInstance setAllocatedInObjcWorld: yn];
		} else if ([objcClass conformsToProtocol: @protocol(QtSupport)]) {
			objcInstance = [[[[objcClass alloc] setQt: qt] init] autorelease];
			[objcInstance setAllocatedInObjcWorld: yn];
		} else {
			objcInstance = [[[objcClass alloc] init] autorelease];
		}
		
		[self setObject: objcInstance forQtKey: qt];
	}
	
	return objcInstance;
}

+ (BOOL) connect: sender signal: (NSString *) signal receiver: receiver slot: (NSString *) member
{
	qt_Slot *			slotId;
	
	if ([member hasSuffix: @")"]) {
	 	if (	!qt_QObject_connect1(	([signal hasSuffix: @")"]) ? [sender qt] : [QtSupport signalIdForSender: sender signal: signal],
 								(char *)[[NSString stringWithFormat: @"2%@", signal] cString],
 								[receiver qt],
 								(char *)[[NSString stringWithFormat: @"1%@", member] cString] ) )
 		{
 			NSLog(@"connect failed...signal: %@ slot: %@", signal, member);
 			return NO;
 		}
	} else {
		slotId = (qt_Slot *) [QtSupport slotIdForReceiver: receiver slot: member];
		
		if ([signal hasSuffix: @")"]) {
			if (	!qt_QObject_connect1(	[sender qt],
											(char *)[[NSString stringWithFormat: @"2%@", signal] cString],
											(qt_QObject *) slotId,
											([member hasSuffix: @":"]) ?
											([signal hasSuffix: @"(int)"] ? "1UserDataSlot(int)" : "1UserDataSlot(void *)")
											: "1UserDataSlot()" ) )
			{
 				NSLog(@"connect failed...signal: %@ slot: %@", signal, member);
 				return NO;
			}
		} else {
			if (	!qt_QObject_connect1(	[QtSupport signalIdForSender: sender signal: signal],
											([signal hasSuffix: @":"]) ? "2pointerSignal(void *)" : "2voidSignal()",
											(qt_QObject *) slotId,
											([member hasSuffix: @":"]) ? "1UserDataSlot(void *)" : "1UserDataSlot()" ) )
			{
 				NSLog(@"connect failed...signal: %@ slot: %@", signal, member);
 				return NO;
			}
		}
	}
	
	return YES;
}

+ (BOOL) disconnect: sender signal: (NSString *) signal receiver: receiver slot: (NSString *) member
{
//	return (BOOL) qt_QObject_disconnect([self qt]);
	return NO;
}

+ (void *) signalIdForSender: sender signal: (NSString *) signal
{
	NSValue *	qtSignal;
	
	qtSignal = [qtSignalDictionary objectForKey: [NSString stringWithFormat: @"%@%@", sender, signal]];
	
	if (qtSignal == nil) {
		qtSignal = [NSValue valueWithPointer: qt_new_Signal()];
		[qtSignalDictionary setObject: qtSignal forKey: [NSString stringWithFormat: @"%@%@", sender, signal]];
	}
	
	return [qtSignal pointerValue];
}

+ (int) slotIdForReceiver: receiver slot: (NSString *) member
{
	SEL					selector;
	NSInvocation *		invocation;
	NSMethodSignature *	signature;
	qt_Slot *			slotId;
	
	selector = sel_get_uid([member cString]);
	signature = [[(<NSObject>) receiver class] instanceMethodSignatureForSelector: selector];
	invocation = [NSInvocation invocationWithMethodSignature: signature];
	[invocation setSelector: selector];
	[invocation setTarget: receiver];
	slotId = qt_new_UserDataSlot(ObjcSlotCallback, (void *) invocation);
	
	return (int) slotId;
}

+ sender: sender emit: (NSString *) signal
{
	NSValue *	qtSignal;
	
	qtSignal = [qtSignalDictionary objectForKey: [NSString stringWithFormat: @"%@%@", sender, signal]];
	qt_emitVoid([qtSignal pointerValue]);
	
	return self;
}

+ sender: sender emit: (NSString *) signal intValue: (int) value
{
	NSValue *	qtSignal;
	
	qtSignal = [qtSignalDictionary objectForKey: [NSString stringWithFormat: @"%@%@", sender, signal]];
	
	qt_emitPointer([qtSignal pointerValue], (void *) value);
	return self;
}

+ sender: sender emit: (NSString *) signal value: value
{
	NSValue *	qtSignal;
	
	qtSignal = [qtSignalDictionary objectForKey: [NSString stringWithFormat: @"%@%@", sender, signal]];
	
	qt_emitPointer([qtSignal pointerValue], (void *) value);
	return self;
}

@end

@implementation NSDate (QtSupport)

- initWithQTime: (qt_QTime *) qtime
{
static NSCalendarDate *	nsdate = nil;

	if (nsdate == nil) {
		nsdate = [[NSCalendarDate alloc] dateWithCalendarFormat: nil timeZone: nil];
	}
	
	[nsdate initWithTimeIntervalSinceReferenceDate: [self timeIntervalSinceReferenceDate]];	
	[nsdate
		initWithYear: [nsdate yearOfCommonEra] month: [nsdate monthOfYear] day: [nsdate dayOfMonth]
		hour: qt_QTime_hour((qt_QTime*)qtime) minute: qt_QTime_minute((qt_QTime*)qtime) second: qt_QTime_second((qt_QTime*)qtime)
		timeZone: nil ];
	[self initWithTimeIntervalSinceReferenceDate: [nsdate timeIntervalSinceReferenceDate]];	
	return self;
}

- initWithQDate: (qt_QDate *) qdate
{
static NSCalendarDate *	nsdate = nil;

	if (nsdate == nil) {
		nsdate = [[NSCalendarDate alloc] dateWithCalendarFormat: nil timeZone: nil];
	}
	
 	[nsdate initWithTimeIntervalSinceReferenceDate: [self timeIntervalSinceReferenceDate]];	
	[nsdate
		initWithYear: qt_QDate_year((qt_QDate*)qdate) month: qt_QDate_month((qt_QDate*)qdate)  day: qt_QDate_day((qt_QDate*)qdate)
		hour: [nsdate hourOfDay] minute: [nsdate minuteOfHour] second: [nsdate secondOfMinute]
		timeZone: nil ];
	[self initWithTimeIntervalSinceReferenceDate: [nsdate timeIntervalSinceReferenceDate]];	
	return self;
}

- initWithQDateTime: (qt_QDateTime *) qdatetime
{
	qt_QDate * qdate;
	qt_QTime * qtime;
	
	qdate = qt_QDateTime_date((qt_QDateTime*)qdatetime);
	qtime = qt_QDateTime_time((qt_QDateTime*)qdatetime);
	[self initWithQDate: qdate];
	[self initWithQTime: qtime];
	return self;
}

- (qt_QDate *) toQDate: (qt_QDate **) qdate
{
static NSCalendarDate *	nsdate = nil;

	if (nsdate == nil) {
		nsdate = [[NSCalendarDate alloc] dateWithCalendarFormat: nil timeZone: nil];
	}
	
	if (*qdate == NULL) {
		*qdate = qt_new_QDate();
	}
	
	[nsdate initWithTimeIntervalSinceReferenceDate: [self timeIntervalSinceReferenceDate]];	
	qt_QDate_setYMD((qt_QDate *) *qdate, [nsdate yearOfCommonEra], [nsdate monthOfYear], [nsdate dayOfMonth]);
	return *qdate;
}

- (qt_QTime *) toQTime: (qt_QTime **) qtime
{
static NSCalendarDate *	nsdate = nil;

	if (nsdate == nil) {
		nsdate = [[NSCalendarDate alloc] dateWithCalendarFormat: nil timeZone: nil];
	}
	
	if (*qtime == NULL) {
		*qtime = qt_new_QTime();
	}
	
	[nsdate initWithTimeIntervalSinceReferenceDate: [self timeIntervalSinceReferenceDate]];	
	qt_QTime_setHMS((qt_QTime *) *qtime, [nsdate hourOfDay], [nsdate minuteOfHour], [nsdate secondOfMinute], 0);
	return *qtime;
}

- (qt_QDateTime *) toQDateTime: (qt_QDateTime **) qdatetime
{
static qt_QDate *	qdate = NULL;
static qt_QTime *	qtime = NULL;
	
	
	if (*qdatetime == NULL) {
		*qdatetime = qt_new_QDateTime();
	}
	
	if (qdate == NULL) {
		qdate = qt_new_QDate();
	}
	
	qt_QDateTime_setDate((qt_QDateTime *) *qdatetime, (qt_QDate*)[self toQDate: &qdate]);
	
	if (qtime == NULL) {
		qtime = qt_new_QTime();
	}
	qtime = qt_new_QTime();
	qt_QDateTime_setTime((qt_QDateTime *) *qdatetime, (qt_QTime*)[self toQTime: &qtime]);
	
	return *qdatetime;
}

@end

@implementation NSString (QtSupport)

- (qt_QString *) toQString: (qt_QString **) string
{
static qt_QByteArray *	qbytearray = 0L;

	if (*string == 0L) {
		*string = qt_new_QString();
	}
	
	if (qbytearray == 0L) {
		qbytearray = qt_new_QByteArray();
	}
	
	qt_QByteArray_resize(qbytearray, [self length] * sizeof(unichar));
	[self getCharacters: (unichar *) qt_QByteArray_data(qbytearray)];
	
	if (bigEndianUnicode) {
		qt_QString_setUnicode(*string, (qt_QChar *) qt_QByteArray_data(qbytearray), [self length]);
	} else {
		qt_QString_setUnicodeCodes(*string, (const ushort *) qt_QByteArray_data(qbytearray), [self length]);
	}
	
	return *string;
}

- (qt_QCString *) toQCString: (qt_QCString **) string
{
	if (*string == 0L) {
		*string = qt_new_QCString();
	}
	
	qt_QCString_setStr(*string, (char *) [self cString]);
	return *string;
}

+ (NSString *) fromQString: (qt_QString *) qstring
{
	if (qstring == 0L) {
		return [[NSString alloc] init];
	}
	
	if (bigEndianUnicode) {
		return [	NSString
						stringWithCharacters: (unichar *) qt_QString_unicode((qt_QString *) qstring)
						length: (unsigned int) qt_QString_length((qt_QString *) qstring) ];
	} else {
static qt_QString *	temp = 0L;

		if (temp == 0L) {
			temp = qt_new_QString();
		}

		// Hack to change the big endian unicode in 'qstring' to little endian in 'temp'.
		qt_QString_setUnicodeCodes(temp, (const ushort *) qt_QString_unicode(qstring), (long) qt_QString_length(qstring));
		return [	NSString
						stringWithCharacters: (unichar *) qt_QString_unicode((qt_QString *) temp)
						length: (unsigned int) qt_QString_length((qt_QString *) temp) ];
	}

}

+ (NSString *) fromQCString: (qt_QCString *) qcstring
{
	if (qcstring == 0L) {
		return [[NSString alloc] init];
	}
	
	return [[NSString alloc] stringWithCString: qt_QCString_data((qt_QCString *) qcstring)];
}

@end

@implementation NSArray (QtSupport)

- (qt_QStrList *) toQStrList: (qt_QStrList **) strlist
{
	int count;
	int index;
	NSString *	nsstring;
	
	if (*strlist == NULL) {
		*strlist = qt_new_QStrList(0);
	}
	
	qt_QStrList_clear((qt_QStrList *)*strlist);
	
	for (count = [self count], index = 0; index < count; index++) {
		nsstring = [self objectAtIndex: index];
		qt_QStrList_append((qt_QStrList *)*strlist, (char *)[nsstring cString]);
	}
	
	return *strlist;
}

- (qt_QStringList *) toQStringList: (qt_QStringList **) strlist
{
	int count;
	int index;
	NSString *	nsstring;
static qt_QString *	qstring = 0L;
	
	if (*strlist == NULL) {
		*strlist = qt_new_QStringList();
	}
	
	qt_QStringList_clear((qt_QStringList *)*strlist);
	
	for (count = [self count], index = 0; index < count; index++) {
		nsstring = [self objectAtIndex: index];
		qt_QStringList_append((qt_QStringList *)*strlist, [nsstring toQString: &qstring]);
	}
	
	return *strlist;
}

@end

@implementation NSMutableArray (QtSupport)

+ (NSArray *) arrayWithQStrList: (qt_QStrList *) strlist
{
	qt_QString *		currentString;
	NSMutableArray *	stringArray;
	
	stringArray = [QtSupport objectForQtKey: strlist withClass: [NSMutableArray class]];
	[stringArray removeAllObjects];
	
	currentString = qt_QStrList_first((qt_QStrList *) strlist);
	while (currentString != NULL) {
		[stringArray addObject: [NSString fromQString: currentString]];
		currentString = qt_QStrList_next((qt_QStrList *) strlist);
	}
	
	return stringArray;
}

+ (NSArray *) arrayWithQStringList: (qt_QStringList *) strlist
{
	qt_QString *		currentString;
	NSMutableArray *	stringArray;
	
	stringArray = [QtSupport objectForQtKey: strlist withClass: [NSMutableArray class]];
	[stringArray removeAllObjects];
	
	qt_QStringList_begin((qt_QStringList *) strlist);
	currentString = qt_QStringList_current();
	while (currentString != NULL) {
		[stringArray addObject: [NSString fromQString: currentString]];
		currentString = qt_QStringList_next();
	}
	
	return stringArray;
}

+ (NSArray *) arrayWithQWidgetList: (qt_QWidgetList *) widgetlist
{
	qt_QWidget *		currentWidget;
	NSMutableArray *	widgetArray;
	
	widgetArray = [QtSupport objectForQtKey: widgetlist withClass: [NSMutableArray class]];
	[widgetArray removeAllObjects];
	
	currentWidget = qt_QWidgetList_first((qt_QWidgetList *) widgetlist);
	while (currentWidget != NULL) {
		[widgetArray addObject: [QtSupport objectForQtKey: currentWidget withClass: [QWidget class]]];
		currentWidget = qt_QWidgetList_next((qt_QWidgetList *) widgetlist);
	}
	
	return widgetArray;
}

+ (NSArray *) arrayWithQPointArray: (qt_QPointArray *) pointArray
{
	qt_QPoint *			currentPoint;
	NSMutableArray *	nsarray;
	unsigned int		count;
	unsigned int		index;
	
	nsarray = [QtSupport objectForQtKey: pointArray withClass: [NSMutableArray class]];
	[nsarray removeAllObjects];
	
	for (	count = qt_QPointArray_count((qt_QPointArray *) pointArray), index = 0;
			index < count;
			index++ )
	{
		currentPoint = qt_QPointArray_at((qt_QPointArray *) pointArray, index);
		[nsarray addObject: [QtSupport objectForQtKey: currentPoint withClass: [QPoint class]]];
	}
	
	return nsarray;
}

+ (NSArray *) arrayWithQDomNodeList: (qt_QDomNodeList *) nodelist
{
	qt_QDomNode *		currentNode;
	NSMutableArray *	nsarray;
	unsigned int		count;
	unsigned int		index;
	
	nsarray = [QtSupport objectForQtKey: nodelist withClass: [NSMutableArray class]];
	[nsarray removeAllObjects];
	
	for (	count = qt_QDomNodeList_length((qt_QDomNodeList *) nodelist), index = 0;
			index < count;
			index++ )
	{
		currentNode = qt_QDomNodeList_item((qt_QDomNodeList *) nodelist, index);
		[nsarray addObject: [QtSupport objectForQtKey: currentNode withClass: [QDomNode class]]];
	}
	
	return nsarray;
}

+ (NSArray *) arrayWithQObjectList: (qt_QObjectList *) objectlist
{
	qt_QObject *		currentObject;
	NSMutableArray *	nsarray;
	
	nsarray = [QtSupport objectForQtKey: objectlist withClass: [NSMutableArray class]];
	[nsarray removeAllObjects];
	
	currentObject = qt_QObjectList_first((qt_QObjectList *) objectlist);
	while (currentObject != NULL) {
		[nsarray addObject: [QtSupport objectForQtKey: currentObject withClass: [QObject class]]];
		currentObject = qt_QObjectList_next((qt_QObjectList *) objectlist);
	}
	
	return nsarray;
}

+ (NSArray *) arrayWithQFileInfoList: (qt_QFileInfoList *) infolist
{
	qt_QFileInfo *		currentFileInfo;
	NSMutableArray *	nsarray;
	
	nsarray = [QtSupport objectForQtKey: infolist withClass: [NSMutableArray class]];
	[nsarray removeAllObjects];
	
	currentFileInfo = qt_QFileInfoList_first((qt_QFileInfoList *) infolist);
	while (currentFileInfo != NULL) {
		[nsarray addObject: [QtSupport objectForQtKey: currentFileInfo withClass: [QFileInfo class]]];
		currentFileInfo = qt_QFileInfoList_next((qt_QFileInfoList *) infolist);
	}
	
	return nsarray;
}

@end

