/* SQLWindow.m:
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 *
 * Written by Jack Greenfield
 *
 */

#import	"ScrollViewExtras.h"
#import "SQLWindow.h"
#import	"MultiBinder.h"

#define FAILURE NXLocalizedString("Failure:", NULL, \
	"Message given to user when an operation has failed.")

#define CANNOT_CONNECT NXLocalizedString("Cannot connect to database", NULL, \
	"Message given to user to explain what fails. ")

#define OK NXLocalizedString("OK", NULL, "Okay to continue ")

@implementation SQLWindow

- free
{
    [database free];
    return [super free];
}

- initWithFile:(const char *)filename
{
    char path[MAXPATHLEN];

    if ((self = [super init]) != nil)
    {
	database = [[DBDatabase alloc] initFromFile:filename];
	if (![database connect])
	{
	    NXRunAlertPanel(FAILURE, CANNOT_CONNECT, OK, NULL, NULL);
	    return [self free];
	}

	[[NXBundle mainBundle] getPath:path forResource:"SQLWindow" ofType:"nib"];
	[NXApp loadNibFile:path owner:self withNames:NO];
	[[resultsView window] setDelegate:self];
	[[resultsView window] setTitleAsFilename:filename];
	[[queryView docView] selectAll:self];
	[[resultsView window] makeKeyAndOrderFront:self];
    }

    return self;
}

- database { return database; }

- evaluate:sender
{
    int	charCount;
    char *buffer;
    MultiBinder *binder;
    id <DBContainers> rowsList;
    
    if ((charCount = [[queryView docView] textLength]) > 0)
    {
	binder = [[MultiBinder alloc] initFromPropertyLists:nil];
	[binder setDatabase:database];
	[binder setDelegate:self];
	rowsList = [[List alloc] init];
	[binder setContainer: rowsList];

	buffer = (char *) alloca(1 + charCount);
	[[queryView docView] getSubstring:buffer start:0 length:charCount];
	buffer[charCount] = 0;
	while (charCount > 0 && buffer[--charCount] == '\n')
	    buffer[charCount] = 0;

	[resultsView sprintf:"%s\n", buffer];
	if (![binder evaluateString:buffer])
	    [resultsView sprintf:"EVALUATION FAILED\n\n"];
	else
	if (![binder fetch])
	    [resultsView sprintf:"FETCH FAILED\n\n"];
	else
			/* For the last result set, binderWillChangeResultSet
			 * needs to be called explicitly.
			 */
	    [self binderWillChangeResultSet:binder];
    
	[resultsView sprintf:"\n"];
	[binder free];
	[(List *)rowsList free];
    }

    [[queryView docView] selectText:self];
    return self;
}

- clear:sender
{
    [resultsView clear:sender];
    [[queryView docView] selectText:self];
    return self;
}

- print:sender
{
    [resultsView print:sender];
    [[queryView docView] selectText:self];
    return self;
}

/* This delegate method will be called every time a new result set is being
 * fetched. All objects returned by the binder fetch can be retrieved from the 
 * binder container (named rowsList here). Note that the container will get
 * flushed everytime a new result set is retrieved. This delegate method is the
 * place where you can save the result set elsewhere if needed.
 */
- binderWillChangeResultSet:(MultiBinder *)binder
{
    int rowsIndex;
    int rowsCount;
    int propIndex;
    int propCount;
    List *rowsList;
    List *propList;
    
    propList = [[List alloc] init];
    [binder getCurrentProperties:propList];
    rowsList = binder->container; // public instance variable!!!
    rowsCount = [rowsList count];
    [resultsView sprintf:"\nresult set %d\n", [binder currentResultSet]];
    [resultsView sprintf:"%u records selected\n", rowsCount];
    [binder setFirst];
    for (rowsIndex = 0; rowsIndex < rowsCount; ++rowsIndex)
    {
	propCount = [propList count];
	for (propIndex = 0; propIndex < propCount; ++propIndex)
	{
	    id p = [propList objectAt:propIndex];
	    [resultsView sprintf:"%s=(%s) ", 
		[p name], [[binder valueForProperty:p] stringValue]];
	}

	[resultsView sprintf:"\n"];
	[binder setNext];
    }

    [propList free];
    return self;
}

@end
