/* 
   EODetailDatabaseDataSource.m

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
   Date: 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 <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSString.h>
#include <Foundation/NSException.h>

#include <eoaccess/EOModel.h>
#include <eoaccess/EOEntity.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EORelationship.h>
#include <eoaccess/EOQualifier.h>
#include <eoaccess/EOGenericRecord.h>
#include <eoaccess/EONull.h>

#include <eoaccess/EOAdaptor.h>
#include <eoaccess/EOAdaptorContext.h>
#include <eoaccess/EOAdaptorChannel.h>

#include <eoaccess/EODatabase.h>
#include <eoaccess/EODatabaseContext.h>
#include <eoaccess/EODatabaseChannel.h>

#include "EODatabaseDataSource.h"
#include "EODetailDatabaseDataSource.h"

@implementation EODetailDatabaseDataSource

// Initializing instances
- initWithMasterDataSource:(EODatabaseDataSource*)aMasterDataSource
  entity:(EOEntity*)anEntity
{
    masterDataSource = [aMasterDataSource retain];
    currentEntity = [anEntity retain];
    [self qualifyWithRelationshipKey:nil ofObject:nil];
    return self;
}

- (void)dealloc
{
    [array release];
    [objects release];
    [masterKey release];
    [masterObject release];
    [masterDataSource release];
    [currentEntity release];
    return [super dealloc];
}

// Getting the master data source
- (EODatabaseDataSource*)masterDataSource
{
    return masterDataSource;
}

// Getting the entity
- (EOEntity*)entity
{
    return currentEntity;
}

// Getting the detail key
- (NSString*)detailKey
{
    return masterKey;
}

// Getting the objects
- masterObject
{
    return masterObject;
}

- (NSArray*)array
{
    return [self fetchObjects];
}

// EODataSources methods

- (NSArray *)keys
{
    return [currentEntity classPropertyNames];
}

- createObject
{
    if (!masterKey || !masterObject || [currentEntity isReadOnly])
	return nil;
    return [[masterDataSource databaseChannel] initializedObjectForRow:nil 
	entity:currentEntity zone:[self zone]];
}

- (BOOL)insertObject:object
{
    if (!masterKey || !masterObject || [currentEntity isReadOnly])
	return NO;
    if (!isToMany && [objects count])
	return NO;
    if (![masterDataSource insertObject:object])
	return NO;
    isUnsincronized = YES;
    [objects addObject:object];
    return YES;
}

- (BOOL)deleteObject:object
{
    if (!masterKey || !masterObject || [currentEntity isReadOnly])
	return NO;
    if ([objects indexOfObjectIdenticalTo:object] == NSNotFound)
	return NO;
    if (![masterDataSource deleteObject:object])
	return NO;
    isUnsincronized = YES;
    [object removeObjectIdenticalTo:object];
    return YES;
}

- (BOOL)updateObject:object
{
    if (!masterKey || !masterObject || [currentEntity isReadOnly])
	return NO;
    if ([objects indexOfObjectIdenticalTo:object] == NSNotFound)
	return NO;
    if (![masterDataSource updateObject:object])
	return NO;
    return YES;
}

- (NSArray *)fetchObjects
{
    if (isUnsincronized) {
	if (!masterKey || !masterObject)
	    array = [[NSArray alloc] init];
	else {
	    [array release];
	    array = [[NSArray alloc] initWithArray:objects];
	}
	isUnsincronized = NO;
    }
    return array;
}

- (BOOL)saveObjects
{
    NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
    
    if (!masterKey || !masterObject || [currentEntity isReadOnly])
	return NO;
    if (![masterDataSource saveObjects])
	return NO;
    [dict setObject:(!isToMany ? 
	    ([objects count] ? [objects objectAtIndex:0] : [EONull null]) 
	    : [self array])
	forKey:masterKey];
    
    // TODO - set fields for relationship link in objects
    
    [masterObject takeValuesFromDictionary:dict];
    [dict release];
    
    return YES;
}

- (BOOL)canDelete
{
    return [currentEntity isReadOnly] ? NO : YES;
}

- coerceValue:value forKey: (NSString *)key
{
    EOAttribute* attr = [currentEntity attributeNamed:key];
    
    // TODO - call public conversion function
    return attr ? [attr convertValueToModel:value] : value;
}

// EOQualifiableDataSources methods

- (void)qualifyWithRelationshipKey:(NSString *)key ofObject:sourceObject
{
    EOEntity* objEntity;
    EOEntity* destEntity;
    EORelationship* relation;
    id obj;
    
    if (!masterObject || !masterKey)
	masterObject = masterKey = nil;
    
    [masterObject autorelease];
    masterObject = [sourceObject retain];
    [masterKey autorelease];
    masterKey = [key retain];
    
    [array autorelease];
    [objects autorelease];
    array = objects = nil;
    isUnsincronized = YES;
    
    if ([sourceObject respondsToSelector:@selector(entity)])
    	objEntity = [sourceObject entity];
    else
	objEntity = [[[[[[masterDataSource databaseChannel] 
	    adaptorChannel] adaptorContext] adaptor] model] 
	    entityForObject:sourceObject];

    relation = [objEntity relationshipNamed:key];
    if (!relation) {
	// WARN - no relationship named for entity
    	return;
    }
    destEntity = [relation destinationEntity];
    if (!destEntity) {
    	// WARN - no destinatin entity
	return;
    }

    [currentEntity autorelease];
    currentEntity = [destEntity retain];
    isToMany = [relation isToMany];
    
    obj = [[masterObject valuesForKeys:[NSArray arrayWithObject:masterKey]]
    	objectForKey:masterKey];
    objects = isToMany ? [obj retain]
	: ((!obj || obj == [EONull null]) ? 
		[[NSArray array] retain] 
		: [[NSArray arrayWithObject:obj] retain]);
}

// EOMasterDataSources methods

- (id <EOQualifiableDataSources>)dataSourceQualifiedByKey:(NSString *)key
{
    EORelationship* relation = [currentEntity relationshipNamed:key];
    EOEntity* destEntity = [relation destinationEntity];
    
    if (!relation) {
    	// WARN - no relationshhip named
	return nil;
    }
    
    if (!destEntity) {
    	// WARN - no destinatin entity
	return nil;
    }
    
    return [[[[self class] allocWithZone:[self zone]]
    	initWithMasterDataSource:masterDataSource entity:destEntity]
	autorelease];
}

// EORollbackDataSources methods

- (void)rollback
{
    [masterDataSource rollback];
}

@end /* EODetailDatabaseDataSource */
