/* 
   EOAdaptor.m

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
   Date: October 1996

   This file is part of the GNUstep Database Library.

   This library 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.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <config.h>

#if HAVE_LIBC_H
# include <libc.h>
#else
#ifndef __WIN32__
# include <unistd.h>
#endif /* !__WIN32__ */
#endif

#include <Foundation/NSArray.h>
#include <Foundation/NSString.h>
#include <Foundation/NSPathUtilities.h>
#include <Foundation/NSBundle.h>
#include <Foundation/NSObjCRuntime.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSProcessInfo.h>

#include <extensions/NSException.h>
#include <extensions/exceptions/GeneralExceptions.h>

#include <eoaccess/common.h>
#include <eoaccess/EOModel.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EOSQLExpression.h>
#include <eoaccess/EOAdaptor.h>
#include <eoaccess/EOAdaptorContext.h>
#include <eoaccess/EOAdaptorChannel.h>
#include <eoaccess/exceptions/EOFExceptions.h>

#ifdef SQLSERVER_DATABASE
#include <eoadaptors/SQLServer/SQLServerAdaptor.h>
#endif

#if 0
#ifdef POSTGRES95_DATABASE
# include <eoadaptors/Postgres95/Postgres95Adaptor.h>
#endif
#endif

@implementation EOAdaptor

+ adaptorWithModel:(EOModel*)_model
{
    /* Check first to see if the adaptor class exists in the running program
       by testing the existence of [_model adaptorClassName]. */
    NSString* adaptorName = [_model adaptorName];
    Class adaptorClass = NSClassFromString([_model adaptorClassName]);
    id adaptor;

    if(adaptorClass)
	adaptor = [[[adaptorClass alloc] initWithName:adaptorName]
			autorelease];
    else
	adaptor = [self adaptorWithName:adaptorName];
    [adaptor setModel:_model];
    [adaptor setConnectionDictionary:[_model connectionDictionary]];
    return adaptor;
}

+ adaptorWithName:(NSString*)adaptorName
{
    int i, count;
    NSBundle* bundle = [NSBundle mainBundle];
    NSString* adaptorBundlePath;
    NSString* adaptorNameExt = [adaptorName
	stringByAppendingPathExtension:@"gdladaptor"];
    NSArray* adaptorsPath = nil;
    NSMutableArray* paths;
    Class adaptorClass;
    NSProcessInfo *pInfo;
    NSDictionary *env;
    NSString *user_path, *local_path, *system_path;
    NSMutableString *gdl, *user, *local, *system;

    /* Check error */
    if (!adaptorName || [adaptorName isEqual:@""])
	return nil;

    /* Look in application bundle */
    adaptorBundlePath = [bundle
	pathForResource:adaptorName ofType:@"gdladaptor"];

    /* Look in standard paths */
    if (!adaptorBundlePath)
      {
	/*
	  The path of where to search for the adaptor files
	  is based upon environment variables.
	  GDL_ADAPTORS_PATH
	  GNUSTEP_USER_ROOT
	  GNUSTEP_LOCAL_ROOT
	  GNUSTEP_SYSTEM_ROOT
	  */
	pInfo = [NSProcessInfo processInfo];
	env = [pInfo environment];
	paths = [NSMutableArray array];

	/* GDL_ADAPTORS_PATH is a separated list of paths */
	gdl = [env objectForKey: @"GDL_ADAPTORS_PATH"];
	if (gdl)
	  adaptorsPath = [gdl componentsSeparatedByString:@":"];

	if (adaptorsPath)
	  [paths addObjectsFromArray: adaptorsPath];
	
	user = [[[env objectForKey: @"GNUSTEP_USER_ROOT"]
		  mutableCopy] autorelease];
	[user appendString: @"/Libraries"];
	if (user)
	  [paths addObject: user];

	local = [[[env objectForKey: @"GNUSTEP_LOCAL_ROOT"]
		   mutableCopy] autorelease];
	[local appendString: @"/Libraries"];
	if (local)
	  [paths addObject: local];

	system = [[[env objectForKey: @"GNUSTEP_SYSTEM_ROOT"]
		    mutableCopy] autorelease];
	[system appendString: @"/Libraries"];
	if (system)
	  [paths addObject: system];

	/* Loop through the paths and check each one */
	for(i = 0, count = [paths count]; i < count; i++)
	  {
	    bundle = [NSBundle bundleWithPath: [paths objectAtIndex:i]];
	    adaptorBundlePath = [bundle pathForResource: adaptorName
					ofType: @"gdladaptor"
					inDirectory: @"Adaptors"];

	    if(adaptorBundlePath && [adaptorBundlePath length])
		break;
	  }
      }

    /* Make adaptor bundle */
    if(adaptorBundlePath)
	bundle = [NSBundle bundleWithPath:adaptorBundlePath];
    else
	bundle = nil;

    /* Check bundle */
    if (!bundle)
	THROW([[CannotFindAdaptorBundleException alloc]
		initWithFormat:@"Cannot find adaptor bundle '%@'",
				adaptorName]);

    /* Get the adaptor bundle "infoDictionary", and principal class, ie. the
       adaptor class. Other info about the adaptor should be put in the
       bundle's "Info.plist" file (property list format - see NSBundle class
       documentation for details about reserved keys in this dictionary
       property list containing one entry whose key is adaptorClassName. It
       identifies the actual adaptor class from the bundle. */

    adaptorClass = [bundle principalClass];
    if(!adaptorClass)
	THROW([[InvalidAdaptorBundleException alloc]
		    initWithFormat:@"The adaptor bundle '%@' doesn't contain "
				   @"a principal class", adaptorName]);
    return [[[adaptorClass alloc] initWithName:adaptorName] autorelease];
}

- initWithName:(NSString*)_name
{
    ASSIGN(name, _name);
    contexts = [NSMutableArray new];
    return self;
}

- (void)dealloc
{
    [model release];
    [name release];
    [connectionDictionary release];
    [contexts release];
    [super dealloc];
}

- (void)setConnectionDictionary:(NSDictionary*)dictionary
{
    if([self hasOpenChannels])
	THROW([[InvalidArgumentException alloc]
		    initWithFormat:@"Cannot set the connection dictionary "
				   @"while the adaptor is connected!"]);
    ASSIGN(connectionDictionary, dictionary);
    [model setConnectionDictionary:dictionary];
}

- (EOAdaptorContext*)createAdaptorContext
{
    return [[[[self adaptorContextClass] alloc] 
	initWithAdaptor:self] autorelease];
}

- (void)contextDidInit:aContext
{
    [contexts addObject:[NSValue valueWithNonretainedObject:aContext]];
}

- (void)contextWillDealloc:aContext
{
    int i;
    
    for (i = [contexts count]-1; i >= 0; i--)
	if ([[contexts objectAtIndex:i] nonretainedObjectValue] == aContext) {
	    [contexts removeObjectAtIndex:i];
	    break;
	}
}

- (BOOL)hasOpenChannels
{
    int i;

    for (i = [contexts count] - 1; i >= 0; i--) {
	EOAdaptorContext* ctx = [[contexts objectAtIndex:i] 
	    nonretainedObjectValue];
	if ([ctx hasOpenChannels])
	    return YES;
    }
    return NO;
}

- formatAttribute:(EOAttribute*)attribute
{
    return [attribute expressionValueForContext:nil];
}

- formatValue:value forAttribute:(EOAttribute*)attribute
{
    return [value expressionValueForContext:nil];
}

- (void)reportError:(NSString*)error
{
    if(delegateWillReportError) {
	if([delegate adaptor:self willReportError:error] == NO)
	    return;
    }
    NSLog(@"%@ adaptor error: %@", name, error);
}

- (void)setDelegate:_delegate
{
    delegate = _delegate;
    delegateWillReportError
	= [delegate respondsToSelector:@selector(adaptor:willReportError:)];
}

- (void)setModel:(EOModel*)_model	{ ASSIGN(model, _model); }
- (NSString*)name			{ return name; }
- (NSDictionary*)connectionDictionary	{ return connectionDictionary; }
- (BOOL)hasValidConnectionDictionary	{ return NO; }
- (EOModel*)model			{ return model; }
- (Class)expressionClass		{ return [EOSQLExpression class]; }
- (Class)adaptorContextClass		{ return [EOAdaptorContext class]; }
- (Class)adaptorChannelClass		{ return [EOAdaptorChannel class]; }
- (BOOL)isValidQualifierType:(NSString*)typeName { return NO; }
- delegate				{ return delegate; }

#ifdef SQLSERVER_DATABASE
- (void)forceLink
{
  [SQLServerAdaptor alloc];
}
#endif

#if 0
#ifdef POSTGRES95_DATABASE
- (void)forceLinkWithPostGres
{
    [Postgres95Adaptor new];
}
#endif /* POSTGRES_DATABASE */
#endif

@end /* EOAdaptor */
