#ifndef lint
static char rcsid [] = "$RCSfile$ $Revision$ $State$";
#endif
/****************************************************************************
*   File: rtmp_dlist.c                                                      *
*                                                                           *
*       Copyright 1994 by Loral Advanced Distributed Simulation, Inc.       *
*                                                                           *
*               Loral Advanced Distributed Simulation, Inc.                 *
*               10 Moulton Street                                           *
*               Cambridge, MA 02238                                         *
*               617-873-1850                                                *
*                                                                           *
*       This software was developed by Loral under U. S. Government contracts*
*       and may be reproduced by or for the U. S. Government pursuant to    *
*       the copyright license under the clause at DFARS 252.227-7013        *
*       (OCT 1988).                                                         *
*                                                                           *
*       Contents:                                                           *
*       Created:                                                            *
*       Author: oded                                                        *
*       Remarks:                                                            *
*                                                                           *
****************************************************************************/

#include "librtmp_local.h"
#include "rtmp_dlist.h"

#define salloc(x,y) (y *)calloc(x, sizeof(y))

#define TRUE 1
#define FALSE 0

static int32 default_cmp(v1, v2)
char *v1, *v2;
{
    return ((int32)v1 - (int32)v2);
}

int32 dlist_init(dlist, cmp_func, free_func)
DLIST *dlist;
int32 (*cmp_func)();
int32 (*free_func)();
{
    dlist->count = 0;
    dlist->cmp_func = (cmp_func == NULL) ? default_cmp : cmp_func;
    dlist->free_func = free_func;
    dlist->head = NULL;         /* nsp 11/6/92 fix dlist_add_first failure */
    dlist->tail = NULL;         /* nsp 11/6/92 fix dlist_add_first failure */

    return (0);
}

DLIST * dlist_make(cmp_func, free_func)
int32 (*cmp_func)();
int32 (*free_func)();
{
    DLIST *dlist;

    if ((dlist = salloc(1, DLIST)) == NULL)
      return (NULL);

    if (dlist_init(dlist, cmp_func, free_func) < 0) {
	free(dlist);
	return (NULL);
    }

    return (dlist);
}

DNODE * dlist_make_dnode(value)
char *value;
{
    DNODE *dnode;

    if ((dnode = salloc(1, DNODE)) == NULL)
      return (NULL);

    dnode->value = value;

    return (dnode);
}

int32 dlist_free(dlist)
DLIST *dlist;
{
    DNODE *n = dlist->head;
    DNODE *hold;

    while (n) {
	if (dlist->free_func != NULL) 
	  (*dlist->free_func)(n->value);
	hold = n->next;
	cfree(n);
	n = hold;
    }

    dlist->head = dlist->tail = NULL;

    dlist->count = 0;
}

int32 dlist_length(dlist)
DLIST *dlist;
{
    return (dlist->count);
}

DNODE * dlist_nth_dnode(dlist, n)
DLIST *dlist;
int32 n;
{
    DNODE *dnode;
    int32 i;

    if (n >= dlist->count)
      return (NULL);

    for (i = 0, dnode = dlist->head; i < n; i++)
      dnode = dnode->next;

    return (dnode);
}

char * dlist_nth(dlist, n)
DLIST *dlist;
int32 n;
{
    DNODE *dnode;
    int32 i;

    if (n >= dlist->count)
      return (NULL);

    for (i = 0, dnode = dlist->head; i < n; i++)
      dnode = dnode->next;

    return (dnode->value);
}

DNODE * dlist_last_dnode(dlist)
DLIST *dlist;
{
    if (dlist->count == 0)
      return (NULL);

    return (dlist->tail);
}

char * dlist_last(dlist)
DLIST *dlist;
{
    if (dlist->count == 0)
      return (NULL);

    return (dlist->tail->value);
}

DNODE * dlist_first_dnode(dlist)
DLIST *dlist;
{
    if (dlist->count == 0)
      return (NULL);

    return (dlist->head);
}

char * dlist_first(dlist)
DLIST *dlist;
{
    if (dlist->count == 0)
      return (NULL);

    return (dlist->head->value);
}

static DLIST * add_dnode(dlist, prev, new)
DLIST *dlist;
DNODE *prev;
DNODE *new;
{
    if (prev == NULL) {
	new->next = dlist->head;
	new->previous = NULL;
	if (dlist->head) {
	    dlist->head->previous = new;
	    dlist->head = new;
	} else {
	    dlist->tail = new;
	    dlist->head = new;
	}
    } else {
	new->previous = prev;
	new->next = prev->next;
	if (prev->next)
	  prev->next->previous = new;
	else
	  dlist->tail = new;
	prev->next = new;
    }

    dlist->count++;

    return (dlist);
}

DLIST * dlist_add_dnode(dlist, dnode)
DLIST *dlist;
DNODE *dnode;
{
    return (add_dnode(dlist, dlist->tail, dnode));
}

DLIST * dlist_add(dlist, value)
DLIST *dlist;
char *value;
{
    return (add_dnode(dlist, dlist->tail, dlist_make_dnode(value)));
}

DLIST * dlist_add_first_dnode(dlist, dnode)
DLIST *dlist;
DNODE *dnode;
{
    return (add_dnode(dlist, NULL, dnode));
}

DLIST * dlist_add_first(dlist, value)
DLIST *dlist;
char *value;
{
    return (add_dnode(dlist, NULL, dlist_make_dnode(value)));
}

DLIST * dlist_append(dlist1, dlist2)
DLIST *dlist1;
DLIST *dlist2;
{
    DNODE *n;

    for (n = dlist2->head; n != NULL; n = n->next)
      dlist_add(dlist1, n->value);

    return (dlist1);
}

static DNODE * delete_dnode(dlist, n, do_free)
DLIST *dlist;
DNODE *n;
int32 do_free;
{
    DNODE *next = n->next;
    
    if (n->previous)
      n->previous->next = n->next;
    if (n->next)
      n->next->previous = n->previous;

    if (n == dlist->head)
      dlist->head = n->next;
    if (n == dlist->tail)
      dlist->tail = n->previous;
    
    dlist->count--;
    
    if (do_free && (dlist->free_func != NULL))
      (*dlist->free_func)(n->value);

    if (do_free)
      cfree(n);
    
    return (next);
}

static DLIST * delete_dlist_internal(dlist, value, func, filter_mode, do_free)
DLIST *dlist;
char *value;
int32 (*func)();
int32 filter_mode;
int32 do_free;
{
    DNODE *n = dlist->head;
    int32 do_delete = FALSE;

    while(n) {
	if (func != NULL)
	  do_delete = ((*func)(n->value, value) == 0);
	else
	  do_delete = ((*dlist->cmp_func)(n->value, value) == 0);
	 
	if (do_delete) {
	    n = delete_dnode(dlist, n, do_free);
	    if (!filter_mode)
	      return (dlist);
	} else
	  n = n->next;
    }

    return (dlist);
}

DLIST * dlist_delete_dnode(dlist, dnode)
DLIST *dlist;
DNODE *dnode;
{
    delete_dnode(dlist, dnode, FALSE);
    
    return (dlist);
}

DLIST * dlist_delete(dlist, value)
DLIST *dlist;
char *value;
{
    return (delete_dlist_internal(dlist, value, NULL, FALSE, TRUE));
}

DLIST * dlist_delete_with_function(dlist, value, function)
DLIST *dlist;
char *value;
int32 (*function)();
{
    return (delete_dlist_internal(dlist, value, function, FALSE, TRUE));
}

DLIST * dlist_filter(dlist, value)
DLIST *dlist;
char *value;
{
    return (delete_dlist_internal(dlist, value, NULL, TRUE, TRUE));
}

DLIST * dlist_filter_with_function(dlist, value, function)
DLIST *dlist;
char *value;
int32 (*function)();
{
    return (delete_dlist_internal(dlist, value, function, TRUE, TRUE));
}

DLIST * dlist_delete_first(dlist)
DLIST *dlist;
{
    DNODE *first = dlist->head;

    if (first)
      delete_dnode(dlist, first, TRUE);
    
    return (dlist);
}

DLIST * dlist_delete_last(dlist)
DLIST *dlist;
{
    DNODE *last = dlist->tail;

    if (last)
      delete_dnode(dlist, last, TRUE);
    
    return (dlist);
}

char * dlist_car(dlist)
DLIST *dlist;
{
    return (dlist_first(dlist));
}

DLIST * dlist_cdr(dlist)
DLIST *dlist;
{
    return (dlist_delete_first(dlist));
}

DLIST * dlist_push(dlist, value)
DLIST *dlist;
char *value;
{
    return (dlist_add_first(dlist, value));
}

DLIST * dlist_push_new(dlist, value)
DLIST *dlist;
char *value;
{
    DNODE *n;

    for (n = dlist->head; n != NULL; n = n->next)
      if ((*dlist->cmp_func)(n->value, value) == 0)
	return (dlist);
    
    return (dlist_add_first(dlist, value));
}

char * dlist_pop(dlist)
DLIST *dlist;
{
    char *value = dlist_first(dlist);
    
    dlist_delete_first(dlist);
    return (value);
}

char * dlist_find(dlist, value)
DLIST *dlist;
char *value;
{
    DNODE *n;

    for (n = dlist->head; n != NULL; n = n->next)
      if ((*dlist->cmp_func)(n->value, value) == 0)
	return (n->value);

    return (NULL);
}

DLIST * dlist_insert(dlist, value, order)
DLIST *dlist;
char *value;
int32 order;
{
    DNODE *n, *prev_n;
    int new_order;

    for (n = dlist->head, prev_n = NULL; n != NULL; prev_n = n, n = n->next) {
	new_order = (*dlist->cmp_func)(value, n->value);
	if (new_order < 0)
	  new_order = -1;
	else if (new_order > 0)
	  new_order = 1;
	else
	  new_order = 0;
	
	if (order != new_order)
	  return (add_dnode(dlist, prev_n, dlist_make_dnode(value)));
    }

    return (add_dnode(dlist, prev_n, dlist_make_dnode(value)));
}

DNODE * dlist_next_dnode(dlist, dnode)
DLIST *dlist;
DNODE **dnode;
{
    if (*dnode == NULL)
      *dnode = dlist->head;
    else
      *dnode = (*dnode)->next;
    
    return (*dnode);
}

char * dlist_next(dlist, dnode)
DLIST *dlist;
DNODE **dnode;
{
    if (*dnode == NULL)
      *dnode = dlist->head;
    else
      *dnode = (*dnode)->next;
    
    if (*dnode == NULL)
      return (NULL);
    else
      return ((*dnode)->value);
}

char * dlist_delete_next(dlist, dnode)
DLIST *dlist;
DNODE **dnode;
{
    if (*dnode == NULL) {
	*dnode = dlist->head;
	return ((*dnode)->value);
    } else
      *dnode = delete_dnode(dlist, *dnode, TRUE);

    if (*dnode != NULL)
      return ((*dnode)->value);
    else
      return (NULL);
}

/*VARARGS*/
int32 dlist_map(dlist, function, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
DLIST *dlist;
int32 (*function)();
char *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7;
{
    DNODE *n;
    int32 rc;

    for (n = dlist->head; n != NULL; n = n->next)
      /*SUPPRESS 62*/
      if ((rc = (*function)(n->value,arg1,arg2,arg3,arg4,arg5,arg6,arg7)) != 0)
	return (rc);

    return(0);
}

/*VARARGS*/
int32 dlist_map_dnode(dlist, function, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
DLIST *dlist;
int32 (*function)();
char *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7;
{
    DNODE *n;
    int32 rc;

    for (n = dlist->head; n != NULL; n = n->next)
      /*SUPPRESS 62*/
      if ((rc = (*function)(n,arg1,arg2,arg3,arg4,arg5,arg6,arg7)) != 0)
	return (rc);

    return(0);
}

static int32 dnode_describe(dnode, fp, i) 
DNODE *dnode;
FILE *fp; 
int32 *i; 
{
    fprintf(fp, "Dnode %d: value=%d, dnode=0x%x, previous=0x%x, next=0x%x\n",
	    *i, dnode->value, dnode, dnode->previous, dnode->next); 
    (*i)++;

    return (0);
}

int32 dlist_describe(dlist, fp)
DLIST *dlist;
FILE *fp;
{
    int32 i = 0;

    if (fp == NULL)
      fp = stdout;

    fprintf(fp, "length=%d, head=0x%x, tail=0x%x\n",
	    dlist->count, dlist->head, dlist->tail);
    dlist_map_dnode(dlist, dnode_describe, fp, &i);

    return (0);
}
