//
//  MSchemaDataSource.m
//  MySQLGUICommon
//
//  Created by Alfredo Kojima on Sun Jun 27 2004.
//  Copyright (c) 2004 MySQL AB. All rights reserved.
//

#import "MSchemaDataSource.h"


@implementation MSchemaItem

- (id)initWithCatalog: (MYX_CATALOG*)catalog
                 icon: (NSImage*)aicon
{
  if (!(self= [super init]))
    return nil;
  
  type= MCatalogItemType;
  _object.catalog= catalog;
  _repr= nil;
  icon= [aicon retain];

  return self;
}

- (id)initWithSchema: (MYX_SCHEMA*)schema
	   parentCatalog: (MSchemaItem*)parentItem
                icon: (NSImage*)aicon
{
  if (!(self= [super init]))
    return nil;
  
  type= MSchemaItemType;
  _object.schema= schema;
  parent= parentItem;
  _repr= nil;
  icon= [aicon retain];

  return self;
}

- (id)initWithTable: (MYX_SCHEMA_TABLE*)table
	   parentSchema: (MSchemaItem*)parentItem
               icon: (NSImage*)aicon
{
  if (!(self= [super init]))
    return nil;
  
  type= MTableItemType;
  _object.table= table;
  parent= parentItem;
  _repr= nil;
  icon= [aicon retain];

  return self;
}

- (id)initWithColumn: (MYX_SCHEMA_TABLE_COLUMN*)column
		 parentTable: (MSchemaItem*)parentItem
                icon: (NSImage*)aicon
{
  if (!(self= [super init]))
    return nil;
  
  type= MColumnItemType;
  _object.column= column;
  _repr= nil;
  parent= parentItem;
  icon= [aicon retain];

  return self;
}

- (void)dealloc
{
  [_identifier release];
  [_repr release];
  [icon release];
  [super dealloc];
}

- (NSImage*)icon
{
  return icon;
}


- (id)identifier
{
  if (!_identifier)
	_identifier= [[NSString stringWithFormat:@"%p",self] retain];
  return _identifier;
}

- (NSString*)repr
{
  if (!_repr)
  {
    switch (type)
    {
      case MCatalogItemType:
        _repr= [NSString stringWithUTF8String: _object.catalog->catalog_name?:"?"];
        break;
      case MSchemaItemType:
        _repr= [NSString stringWithUTF8String: _object.schema->schema_name?:""];
        break;
      case MTableItemType:
        _repr= [NSString stringWithUTF8String: _object.table->table_name?:""];
        break;
      case MColumnItemType:
        _repr= [NSString stringWithUTF8String: _object.column->column_name?:""];
        break;
    }
    [_repr retain];
  }
  return _repr;
}

- (MYX_CATALOG*)catalog
{
  MSchemaItem *item= nil;
  switch (type)
  {
    case MCatalogItemType:
      item= self;
      break;
    case MSchemaItemType:
      item= self->parent;
      break;
    case MTableItemType:
      item= self->parent->parent;
      break;
    case MColumnItemType:
      item= self->parent->parent->parent;
      break;
  }
  return item ? item->_object.catalog : NULL;
}

- (MYX_SCHEMA*)schema
{
  MSchemaItem *item= nil;
  switch (type)
  {
    case MSchemaItemType:
      item= self;
      break;
    case MTableItemType:
      item= self->parent;
      break;
    case MColumnItemType:
      item= self->parent->parent;
      break;
    default:
      NSAssert(0,@"invalid type (table)");
      break;      
  }
  return item ? item->_object.schema : NULL;
}

- (MYX_SCHEMA_TABLE*)table
{
  MSchemaItem *item= nil;
  switch (type)
  {
    case MTableItemType:
      item= self;
      break;
    case MColumnItemType:
      item= self->parent;
      break;
    default:
      NSAssert(0,@"invalid type (table)");
      break;
  }
  return item ? item->_object.table : NULL;  
}

- (MYX_SCHEMA_TABLE_COLUMN*)column
{
  MSchemaItem *item= nil;
  switch (type)
  {
    case MColumnItemType:
      item= self;
      break;
    default:
      NSAssert(0,@"invalid type (table)");
      break;
  }
  return item ? item->_object.column : NULL;  
}

@end


//==============================================================================

@implementation MSchemaDataSource

- (MSchemaItem*)findItem: (NSString*)name
{
  /*
  int i, c= [_rootList count];
  for (i= 0; i < c; i++)
  {
    if ([[[_rootList objectAtIndex:i] repr] isEqualToString:name])
      return [_rootList objectAtIndex:i];
  }
   */
  return nil;
}


- (id)initWithRoot: (MSchemaDSItemType)root
              leaf: (MSchemaDSItemType)leaf
{
  self= [super init];
  if (self)
  {
    _rootType= root;
    _leafType= leaf;
	_children= [[NSMutableDictionary alloc] init];
	_items= [[NSMutableDictionary alloc] init];  
  }
  return self;
}


- (void)dealloc
{
  [_children release];
  [_items release];

  [super dealloc];
}


- (void)setTableFetcher:(id)target selector:(SEL)sel
{
  _tableFetcher= target;
  _tableFetcherAction= sel;
}


- (void)resetTree
{
  [_children removeAllObjects];
  [_items removeAllObjects];
}

- (void)setChildren: (NSMutableArray*)array
			forItem: (MSchemaItem*)item
{
  unsigned int i, c;
  NSMutableArray *children;

  children= [NSMutableArray arrayWithCapacity:[array count]];
  c= [array count];
  for (i= 0; i < c; i++)
  {
	MSchemaItem *obj= [array objectAtIndex:i];
	[_items setObject:obj forKey:[obj identifier]];
	[children addObject:[obj identifier]];
  }
  [_children setObject:children forKey:item ? [item identifier] : @"root"];
}


- (id)outlineView:(NSOutlineView *)outlineView 
            child:(int)index 
           ofItem:(id)item
{
  if (item == nil)
    return [_items objectForKey:[[_children objectForKey:@"root"] objectAtIndex: index]];
  else
  {
    id tmp= [_children objectForKey:[item identifier]];
    if (!tmp)
    {
	  [_tableFetcher performSelector:_tableFetcherAction
						  withObject:self
						  withObject:item];
	  return @"Loading...";
    }
    else
      return [_items objectForKey:[tmp objectAtIndex:index]];
  }
}

- (BOOL)outlineView:(NSOutlineView *)outlineView 
   isItemExpandable:(id)item
{
  if ([item isMemberOfClass:[MSchemaItem class]] && ((MSchemaItem*)item)->type < _leafType)
    return YES;
  else
    return NO;
}

- (int)outlineView:(NSOutlineView *)outlineView 
numberOfChildrenOfItem:(id)item
{
  if (item == nil)
    return [[_children objectForKey:@"root"] count];
  else if ([item isKindOfClass: [NSString class]]) // the Loading... string
    return 0;
  else
  {
	id tmp= [_children objectForKey:[item identifier]];
    if (!tmp)
	  return 1; // for Loading...
	else
	  return [tmp count];
  }
}

- (id)outlineView:(NSOutlineView *)outlineView 
objectValueForTableColumn:(NSTableColumn *)tableColumn 
           byItem:(id)item
{
  if ([item isMemberOfClass: [MSchemaItem class]])
    return [item repr];
  else
    return item; // NSString for Loading...
}

@end


//==============================================================================

@implementation MFilteredSchemaDataSource

- (id)initWithDataSource:(MSchemaDataSource*)parent
{
  self= [super initWithRoot:parent->_rootType leaf:parent->_leafType];
  if (self)
  {
	_parentDS= parent;
  }
  return self;
}


- (void)setRootList: (NSMutableArray*)array
{
  NSLog(@"NSFilteredSchemaDataSource should not have its rootList changed");
}


- (id)outlineView:(NSOutlineView *)outlineView 
            child:(int)index 
           ofItem:(id)item
{
  if (!_filter)
	return [_parentDS outlineView:outlineView child:index ofItem:item];
  else
	return [super outlineView:outlineView child:index ofItem:item];
  
}

- (BOOL)outlineView:(NSOutlineView *)outlineView 
   isItemExpandable:(id)item
{
  if (!_filter)
	return [_parentDS outlineView:outlineView isItemExpandable:item];
  else
	return [super outlineView:outlineView isItemExpandable:item];
}

- (int)outlineView:(NSOutlineView *)outlineView 
numberOfChildrenOfItem:(id)item
{
  if (!_filter)
	return [_parentDS outlineView:outlineView numberOfChildrenOfItem:item];
  else
	return [super outlineView:outlineView numberOfChildrenOfItem:item];
}

- (id)outlineView:(NSOutlineView *)outlineView 
objectValueForTableColumn:(NSTableColumn *)tableColumn 
           byItem:(id)item
{
  if (!_filter)
	return [_parentDS outlineView:outlineView
	objectValueForTableColumn:tableColumn
					   byItem:item];
  else
	return [super outlineView:outlineView
	objectValueForTableColumn:tableColumn
					   byItem:item];
}


- (BOOL)filterChildren:(NSArray*)children
				ofItem:(MSchemaItem*)item
			  matching:(NSString*)filter
{
  unsigned int i, c= [children count];
  NSMutableArray *array= [NSMutableArray arrayWithCapacity:1];

  for (i= 0; i < c; i++)
  {
	id ident= [children objectAtIndex:i];
	id obj= [_parentDS->_items objectForKey:ident];
	NSArray *ch= [_parentDS->_children objectForKey:ident];
	BOOL ok= NO;

	if ([[obj repr] rangeOfString:filter].location!=NSNotFound)
	  ok= YES,
		NSLog(@"%@ matche", [obj repr]);
		
	if (ch && [self filterChildren:ch ofItem:obj matching:filter])
	  ok= YES;

	if (ok)
	  [array addObject:ident];
  }
  
  [_children setObject:array forKey:[item identifier]];
  
  return [array count]>0;
}


- (BOOL)removeChildren:(NSMutableArray*)children
				ofItem:(MSchemaItem*)item
		   notMatching:(NSString*)filter
{
  int i, c= [children count];
  int matches= 0;

  for (i= c-1; i >= 0; i--)
  {
	id ident= [children objectAtIndex:i];
	id obj= [_parentDS->_items objectForKey:ident];
	NSMutableArray *ch= [_parentDS->_children objectForKey:ident];
	
	if (ch && [self removeChildren:ch ofItem:obj notMatching:filter])
	{
	  matches++;
	}
	else if ([[obj repr] rangeOfString:filter].location!=NSNotFound)
	{
	  matches++;
	}
	else
	{
	  [children removeObject:ident];
	}
  }
  
  return matches>0;
}


- (void)performSearch:(NSString*)filter
{
  int i, c;

//  NSLog(@"filter by %@", filter);
  if (!filter || [filter length] == 0)
  {
	[_filter release];
	_filter= nil;
  }
  else if (!_filter || [filter rangeOfString:_filter].location == NSNotFound)
  {
	NSArray *array;
	NSMutableArray *rootArray= nil;
	
	[_filter release];
	_filter= [filter retain];

	[_children removeAllObjects];
	array= [_parentDS->_children objectForKey:@"root"];
	c= [array count];
	for (i= 0; i < c; i++)
	{
	  if ([self filterChildren:[_parentDS->_children objectForKey:[array objectAtIndex:i]]
						ofItem:[_parentDS->_items objectForKey:[array objectAtIndex:i]]
					  matching:filter])
	  {
		if (!rootArray)
		  rootArray= [NSMutableArray arrayWithCapacity:1];

		[rootArray addObject:[array objectAtIndex:i]];
	  }
	}
	if (rootArray)
	{
	  [_children setObject:rootArray forKey:@"root"];
	  _items= _parentDS->_items;
	}
  }
  else // incremental search
  {
	NSMutableArray *array;

	[_filter release];
	_filter= [filter retain];

	array= [_children objectForKey:@"root"];
	c= [array count];
	for (i= c-1; i >= 0; i--)
	{
	  if (![self removeChildren:[_children objectForKey:[array objectAtIndex:i]]
						 ofItem:[_parentDS->_items objectForKey:[array objectAtIndex:i]]
					notMatching:filter])
	  {
		[array removeObject:[array objectAtIndex:i]];
	  }
	}
  }
}

@end
