/* Implementation for Objective-C Dictionary collection object

   Copyright (C) 1993 R. Andrew McCallum <mccallum@cs.rochester.edu>
   Dept. of Computer Science, U. of Rochester, Rochester, NY  14627

   This file is part of the GNU Objective-C Collection 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; if not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ 

#include <coll/Dictionary.h>
#include <coll/CollectionPrivate.h>
#include <objc/objc-api.h>

#define DEFAULT_DICTIONARY_CAPACITY 32

@implementation Dictionary

+ initialize
{
  if (self == [Dictionary class])
    [self setVersion:-1];	/* alpha release */
  return self;
}

// MANAGING CAPACITY;

/* Eventually we will want to have better capacity management,
   potentially keep default capacity as a class variable. */

+ (unsigned) defaultCapacity
{
  return DEFAULT_DICTIONARY_CAPACITY;
}
  
// INITIALIZING;

/* This is the designated initializer of this class */
- initDescription: (const char *)contentType 
    keyDescription: (const char *)keyType
    capacity: (unsigned)aCapacity
{
  compare_func_type key_compare_func;
  hash_func_type key_hash_func;

  [super initDescription:contentType
	 keyDescription:keyType];
  /* Need to make sure that aCapacity is power of 2 */
  /* I don't think this is very portable... Change this switch. */
  switch (*_keyDescription) 
    {
    case _C_CHARPTR: 
    case _C_ATOM: 
      key_compare_func = (compare_func_type)coll_compare_strings;
      key_hash_func = (hash_func_type)coll_hash_string;
      break;
      
    case _C_ID: 
    case _C_CLASS:		/* I can send classes isEqual:? */
      key_compare_func = (compare_func_type)coll_compare_objects;
      key_hash_func = (hash_func_type)coll_hash_object;
      break;
      
    case _C_PTR: 
    case _C_SEL:
      key_compare_func = (compare_func_type)coll_compare_ptrs;
      key_hash_func = (hash_func_type)coll_hash_ptr;
      break;
      
    case _C_INT: 
    case _C_UINT: 
    case _C_FLT:		/* Yipes */
      key_compare_func = (compare_func_type)coll_compare_ints;
      key_hash_func = (hash_func_type)coll_hash_int;
      break;

    case _C_LNG: 
    case _C_ULNG: 
      key_compare_func = (compare_func_type)coll_compare_long_ints;
      key_hash_func = (hash_func_type)coll_hash_int;
      break;
      
    default : 
      [self error:"Element type (%s) not supported.\n", _keyDescription];
    }
  _contents_hash = hash_new(aCapacity,
			    (hash_func_type)key_hash_func,
			    (compare_func_type)key_compare_func);
  return self;
}

/* To make sure that former KeyedCollection init'ers go through
   Dictionary init, we override the designated initializer for 
   KeyedCollection. */ 
- initDescription: (const char *)contentType 
    keyDescription: (const char *)keyType
{
  return [self initDescription:contentType
	       keyDescription:keyType
	       capacity:[[self class] defaultCapacity]];
}

- initDescription: (const char *)type 
    capacity: (unsigned)aCapacity
{
  return [self initDescription:type keyDescription:"@" capacity:aCapacity];
}

- initCapacity: (unsigned)aCapacity
{
  return [self initDescription:"@" capacity:aCapacity];
}


- free
{
  hash_delete(_contents_hash);
  return [super free];
}

// ADDING OR REPLACING;

- addElement: (elt)anElement
{
  return [self shouldNotImplement:_cmd];
  /* or should I make up some default behavior here? 
     Base it on object conforming to <Associating> protocol, perhaps */
}

- insertElement: (elt)newContentElement atKey: (elt)aKey
{
  node_ptr node = hash_node_for_key(_contents_hash, aKey.void_ptr_t);
  if (node)
    /* Casting constant here.  trouble? */
    (const void*)node->value = newContentElement.void_ptr_t;
  else
    hash_add(&_contents_hash, aKey.void_ptr_t, 
	     (void*)newContentElement.char_ptr_t);
  return;
}


// REMOVING;

- (elt) removeElementAtKey: (elt)aKey
{
  node_ptr node = hash_node_for_key(_contents_hash, aKey.void_ptr_t);
  elt ret;

  ret.void_ptr_t = 0;
  if (node)
    {
      ret = node->value;
      hash_remove(_contents_hash, aKey.void_ptr_t);
    }
  return ret;
}

- (elt) removeElement: (elt)oldElement
{
  elt key = [self keyElementOfElement:oldElement];
  return [self removeElementAtKey:key];
}


// GETTING ELEMENTS;

- (elt) elementAtKey: (elt)aKey
{
  return hash_value_for_key(_contents_hash, aKey.void_ptr_t);
}



// TESTING;

- (BOOL) includesKey: (elt)aKey
{
  if (hash_value_for_key(_contents_hash, aKey.void_ptr_t))
    return YES;
  else
    return NO;
}

// ENUMERATIONS;

- (BOOL) getNextKey: (elt*)aKeyPtr content: (elt*)anElementPtr 
  withEnumState: (void**)enumState
{
  *enumState = hash_next(_contents_hash, *enumState);
  if (*enumState)
    {
      *aKeyPtr = ((node_ptr)(*enumState))->key;
      *anElementPtr = ((node_ptr)(*enumState))->value;
      return YES;
    }
  return NO;
}

- withKeysAndContentsCall: (void(*)(const elt,elt))aFunc 
    whileTrue: (BOOL *)flag
{
  node_ptr node = 0;

  while (flag && (node = hash_next(_contents_hash, node)))
    aFunc(node->key, node->value);
  return self;
}


@end
