/*

  sshadt.c

  Author: Antti Huima <huima@ssh.fi>

  Copyright (c) 1999 SSH Communications Security, Finland
  All rights reserved.

  Created Wed Sep  8 17:41:05 1999.

  */

#include "sshincludes.h"
#include "sshadt_i.h"
#include "sshadt_list.h"
#include "sshadt_map.h"
#include "sshdebug.h"

#define SSH_DEBUG_MODULE "SshADT"

/********************************************************** Default methods. */

int ssh_adt_default_compare(void *obj1, void *obj2,
                            void *context)
{
  if (obj1 < obj2) return -1;
  if (obj1 > obj2) return 1;
  return 0;
}

void *ssh_adt_default_duplicate(void *obj, void *context)
{
  return obj;
}

void ssh_adt_default_copy(void *dst, size_t d_size,
                          void *src, void *context)
{
  SSH_DEBUG(9, ("Copying %d bytes from %p to %p.", d_size));

  memcpy(dst, src, d_size);
}

void ssh_adt_default_destroy(void *obj, void *context)
{
  return;
}

void ssh_adt_default_init(void *obj, size_t size, void *context)
{
  memset(obj, 0, size);
}

void ssh_adt_no_init(void *data, void *context)
{
  return;
}

SshUInt32 ssh_adt_default_hash(void *obj, void *context)
{
  return (SshUInt32)obj;
}

/**************************************** Initialize and destroy containers. */

static void set_default_values(SshADTStandardFields *f)
{
  f->app_methods.compare = ssh_adt_default_compare;
  f->app_methods.copy = ssh_adt_default_copy;
  f->app_methods.duplicate = ssh_adt_default_duplicate;
  f->app_methods.destroy = ssh_adt_default_destroy;
  f->app_methods.init = ssh_adt_default_init;
  f->app_methods.hash = ssh_adt_default_hash;
  f->app_methods.context = NULL;
}

static void init_toplevel_container(SshADTContainer c,
                                    SshADTContainerType type,
                                    va_list args)
{
  SshADTArgumentType t;
  SshADTParameters pars;
  SshADTContainerPars *ptr;
  SshADTStaticData *static_data;

  SSH_PRECOND(type != NULL);

  memset(&pars, 0, sizeof(pars));

  set_default_values(&(pars.top.f));
  set_default_values(&(pars.domain.f));
  set_default_values(&(pars.range.f));

  ptr = &(pars.top);

  ptr->type = type;

  while ((t = va_arg(args, SshADTArgumentType)) != SSH_ADT_ARGS_END)
    {
      switch (t)
        {
        case SSH_ADT_CONTEXT:
          ptr->f.app_methods.context = va_arg(args, void *);
          break;

        case SSH_ADT_COMPARE:
          ptr->f.app_methods.compare = va_arg(args, SshADTCompareFunc);
          break;

        case SSH_ADT_COPY:
          ptr->f.app_methods.copy = va_arg(args, SshADTCopyFunc);
          break;

        case SSH_ADT_DUPLICATE:
          ptr->f.app_methods.duplicate = va_arg(args, SshADTDuplicateFunc);
          break;

        case SSH_ADT_DESTROY:
          ptr->f.app_methods.destroy = va_arg(args, SshADTDestroyFunc);
          break;

        case SSH_ADT_INIT:
          ptr->f.app_methods.init = va_arg(args, SshADTInitFunc);
          break;

        case SSH_ADT_HASH:
          ptr->f.app_methods.hash = va_arg(args, SshADTHashFunc);
          break;

        case SSH_ADT_SIZE:
          ptr->flags |= SSH_ADT_FLAG_ALLOCATE;
          ptr->f.default_object_size = va_arg(args, size_t);
          break;
          
        case SSH_ADT_HEADER:
          ptr->flags |= SSH_ADT_FLAG_CONTAINED_HEADER;     
          ptr->f.header_offset = va_arg(args, SshUInt32);
          break;

        default:
          SSH_NOTREACHED;
        }
    }

  static_data = pars.top.type;
  
  (*(static_data->methods.container_init))(c, ptr);
}

SshADTContainer ssh_adt_create_generic(SshADTContainerType type, ...)
{
  va_list args;
  SshADTContainer c;

  c = ssh_xmalloc(sizeof(*c));

  va_start(args, type);
  init_toplevel_container(c, type, args);
  va_end(args);

  return c;
}

void ssh_adt_destroy(SshADTContainer container)
{
  (*(container->static_data->methods.destroy))(container);
  ssh_xfree(container);
}

#if 0
void ssh_adt_init_generic(SshADTContainer container,
                          SshADTContainerType type, ...)
{
  va_list args;

  va_start(args, type);
  init_toplevel_container(container, type, args);
  va_end(args);
}

void ssh_adt_uninit(SshADTContainer container)
{
  SSH_ADT_CALL_DESTROY_HOOK(container, destroy);
  SSH_ADT_CALL(container, FALSE, container_uninit, (container));
  ssh_xfree(container->hooks);
}
#endif

/************************************************************* Record magic. */

size_t ssh_adt_default_size(SshADTContainer container)
{
  return SSH_ADT_DEFAULT_SIZE(container);
}

void *ssh_adt_duplicate_object(SshADTContainer container, void *object)
{
  return
    SSH_ADT_CALL_APP_MANDATORY(container, duplicate,
                               (object, SSH_ADT_APPCTX(container)));
}

void *ssh_adt_detach_object(SshADTContainer container, void *object)
{
  SshADTHandle handle = ssh_adt_get_handle_to(container, object);
  return ssh_adt_detach(container, handle);
}

void ssh_adt_delete_object(SshADTContainer container, void *object)
{
  SshADTHandle handle = ssh_adt_get_handle_to(container, object);
  ssh_adt_delete(container, handle);
}

/************************* Genlist functions, for some internal use perhaps. */

void ssh_adt_genlist_merge(void *from_list, void **to_list,
                           int next_ptr_offset)
{
  while (*to_list != NULL)
    {
      *to_list = (unsigned char *)(*to_list) + next_ptr_offset;
    }
}

void ssh_adt_genlist_add(void **listptr, void *object, int next_ptr_offset)
{
  unsigned char *o = object;
  *((void **)(o + next_ptr_offset)) = *listptr;
  *listptr = object;
}

void ssh_adt_genlist_remove(void **listptr, void *object, int next_ptr_offset)
{
  while ((*listptr) != object)
    {
      listptr = (void **)((unsigned char *)(*listptr) + next_ptr_offset);
    }
  *listptr = *((void **)((unsigned char *)object + next_ptr_offset));  
}

/******************************************************************** Hooks. */

void ssh_adt_initialize_hooks(SshADTContainer container)
{
  ssh_xfree(container->hooks);
  container->hooks = ssh_xmalloc(sizeof(*container->hooks));
  container->hooks->insert      = NULL;
  container->hooks->detach      = NULL;
  container->hooks->map         = NULL;
  container->hooks->unmap       = NULL;
  container->hooks->reallocated = NULL;
  container->hooks->destroy     = NULL;
}

void ssh_adt_uninitialize_hooks(SshADTContainer container)
{
  ssh_xfree(container->hooks);
  container->hooks = NULL;
}

