/* 
   MySQLAdaptor.m

   Copyright (C) 2000 Free Software Foundation, Inc.

   Author: Stephen Peters <portnoy@portnoy.org>
	      based on the Postgres95 Adaptor written by 
	   Mircea Oancea <mircea@jupiter.elcom.pub.ro>
   Date: August 2000

   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 <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSSet.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSString.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSAutoreleasePool.h>

#include <extensions/NSException.h>

#include <eoaccess/common.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EOExpressionArray.h>

#include <eoadaptors/MySQL/MySQLAdaptor.h>
#include <eoadaptors/MySQL/MySQLContext.h>
#include <eoadaptors/MySQL/MySQLChannel.h>
#include <eoadaptors/MySQL/MySQLExceptions.h>
#include <eoadaptors/MySQL/MySQLSQLExpression.h>
#include <eoadaptors/MySQL/MySQLValues.h>

@implementation MySQLAdaptor

- init
{
    return [self initWithName:@"MySQL"];
}

- initWithName:(NSString*)_name
{
    [super initWithName:_name];
    mysqlHandlePool = [NSMutableArray new];
    [self initTypeNames];
    
    return self;
}

- (void)dealloc
{
    NSEnumerator* enumerator;
    MYSQL* mysqlHandle;

    enumerator = [mysqlHandlePool objectEnumerator];
    while((mysqlHandle = [[enumerator nextObject] pointerValue]))
	[self releaseMysqlHandle:mysqlHandle force:YES];
    [mysqlHandlePool release];

    [super dealloc];
}

- (void)privateReportError:(MYSQL*)mysqlHandle
{
    char* message = "NULL mysqlHandle in privateReportError:";

    if (mysqlHandle)
	message = mysql_error(mysqlHandle);
    
    [self reportError:[NSString stringWithCString:message]];
}

- (void)initTypeNames
{
    NSArray* values = 
	[NSArray arrayWithObjects:
		     [NSNumber numberWithInt:FIELD_TYPE_TINY],
		 [NSNumber numberWithInt:FIELD_TYPE_SHORT],
		 [NSNumber numberWithInt:FIELD_TYPE_LONG],
		 [NSNumber numberWithInt:FIELD_TYPE_INT24],
		 [NSNumber numberWithInt:FIELD_TYPE_LONGLONG],
		 [NSNumber numberWithInt:FIELD_TYPE_DECIMAL],
		 [NSNumber numberWithInt:FIELD_TYPE_DECIMAL],
		 [NSNumber numberWithInt:FIELD_TYPE_FLOAT],
		 [NSNumber numberWithInt:FIELD_TYPE_DOUBLE],
		 [NSNumber numberWithInt:FIELD_TYPE_DOUBLE],
		 [NSNumber numberWithInt:FIELD_TYPE_TIMESTAMP],
		 [NSNumber numberWithInt:FIELD_TYPE_DATE],
		 [NSNumber numberWithInt:FIELD_TYPE_TIME],
		 [NSNumber numberWithInt:FIELD_TYPE_DATETIME],
		 [NSNumber numberWithInt:FIELD_TYPE_YEAR],
		 [NSNumber numberWithInt:FIELD_TYPE_STRING],
		 [NSNumber numberWithInt:FIELD_TYPE_STRING],
		 [NSNumber numberWithInt:FIELD_TYPE_BLOB],
		 [NSNumber numberWithInt:FIELD_TYPE_BLOB],
		 [NSNumber numberWithInt:FIELD_TYPE_SET],
		 [NSNumber numberWithInt:FIELD_TYPE_ENUM],
		 [NSNumber numberWithInt:FIELD_TYPE_CHAR],
		 nil];
    NSArray* keys = [NSArray arrayWithObjects:
				 @"tinyint",
			     @"smallint",
			     @"int",
			     @"mediumint",
			     @"bigint",
			     @"decimal",
			     @"numeric",
			     @"float",
			     @"double",
			     @"real",
			     @"timestamp",
			     @"date",
			     @"time",
			     @"datetime",
			     @"year",
			     @"char",
			     @"varchar",
			     @"blob",
			     @"text",
			     @"set",
			     @"enum",
			     @"char - deprecated",
			     nil];

    typesByName = [[NSMutableDictionary dictionaryWithObjects:values
					forKeys:keys]
		    retain];
    typesByCode = [[NSMutableDictionary dictionaryWithObjects:keys
					forKeys:values]
		    retain];
}

- (void)setCacheMysqlHandle:(BOOL)flag	{ flags.cacheMysqlHandle = flag; }
- (BOOL)cacheMysqlHandle			{ return flags.cacheMysqlHandle; }
- (void)setMysqlHandlePoolLimit:(int)value	{ mysqlHandlePoolLimit = value; }
- (int)mysqlHandlePoolLimit			{ return mysqlHandlePoolLimit; }


/* Inherited methods */

- (Class)expressionClass
{
    return [MySQLSQLExpression class];
}

- (Class)adaptorChannelClass
{
    return [MySQLChannel class];
}

- (Class)adaptorContextClass
{
    return [MySQLContext class];
}

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

- (BOOL)isValidQualifierType:(NSString*)typeName
{
    int mysqlType = [[typesByName objectForKey: typeName] intValue];
    if (mysqlType)
	return YES;
    else 
	return NO;
}

/* Private methods for MySQL Adaptor */

- (MYSQL*)createMysqlHandle
{
    char* my_host;
    char* my_database;
    int my_port = 0;
    char* my_user;
    char* my_pwd;
    char* my_options;
    MYSQL *mysqlHandle = NULL;
    NSString* str;
    
    str = [connectionDictionary objectForKey:@"databaseServer"]; 
    if (!str)
	str = [connectionDictionary objectForKey:@"hostName"];
    my_host = (char*)[str cString];

    my_database = (char*)[[connectionDictionary objectForKey:@"databaseName"]
			   cString]; 
    my_port = [[connectionDictionary objectForKey:@"port"] intValue]; 
    
    my_user = (char*)[[connectionDictionary objectForKey:@"user"]
		       cString]; 
    my_pwd = (char*)[[connectionDictionary objectForKey:@"password"]
		      cString]; 
    my_options = (char*)[[connectionDictionary objectForKey:@"options"]
			  cString]; 

    // Try to connect to the MySQL server
    mysqlHandle = mysql_init(NULL);
    if (my_options) 
      mysql_options(mysqlHandle, MYSQL_READ_DEFAULT_GROUP, my_options);

    if (!mysql_real_connect(mysqlHandle, my_host, my_user, my_pwd,
			    my_database, my_port, NULL, 0)) { 
      // error occurred
      [self privateReportError: mysqlHandle];
      mysql_close(mysqlHandle);
      mysqlHandle = NULL;
    }
     
    return mysqlHandle;
}

- (MYSQL*)newMysqlHandle
{
    MYSQL* mysqlHandle;

    if(flags.cacheMysqlHandle && [mysqlHandlePool count]) {
	mysqlHandle = [[mysqlHandlePool lastObject] pointerValue];
	[mysqlHandlePool removeLastObject];
	return mysqlHandle;
    }
    else
	mysqlHandle = [self createMysqlHandle];

    return mysqlHandle;
}

- (void)releaseMysqlHandle:(MYSQL*)mysqlHandle force:(BOOL)flag
{
    if (   !flag 
	&& flags.cacheMysqlHandle 
	&& [mysqlHandlePool count] < mysqlHandlePoolLimit)
		[mysqlHandlePool addObject:
		    [NSValue value:mysqlHandle withObjCType:@encode(MYSQL*)]];
    else
	mysql_close(mysqlHandle);
}

- (BOOL)hasValidConnectionDictionary
{
  MYSQL* mysqlHandle = NULL;

  // Test by trying to create a connection
  NSLog(@"Check if valid connection dictionary\n");
  mysqlHandle = [self newMysqlHandle];
  if (mysqlHandle)
    {
      [self releaseMysqlHandle: mysqlHandle force: YES];
      return YES;
    }
  else
    return NO;
}

- (NSString*)externalTypeNameForCode:(int) typeCode 
{
    return 
	(NSString*) [typesByCode objectForKey: 
				     [NSNumber numberWithInt: typeCode]];
}

- (int)mysqlCodeForType:(NSString*)typeName
{
    // strip off a trailing (...) specification
    NSRange rng = [typeName rangeOfString:@"("];
    if (rng.length > 0) 
	typeName = [typeName substringToIndex: rng.location  ];

    return [[typesByName objectForKey: typeName] intValue];
}

@end /* MySQLAdaptor */
