/* $Id$
 *
 *      Author: John Winans (ANL)
 *      Date:   07-02-92
 *
 *      Experimental Physics and Industrial Control System (EPICS)
 *
 *      Copyright 1991, the Regents of the University of California,
 *      and the University of Chicago Board of Governors.
 *
 *      This software was produced under  U.S. Government contracts:
 *      (W-7405-ENG-36) at the Los Alamos National Laboratory,
 *      and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *      Initial development by:
 *              The Controls and Automation Group (AT-8)
 *              Ground Test Accelerator
 *              Accelerator Technology Division
 *              Los Alamos National Laboratory
 *
 *      Co-developed with
 *              The Controls and Computing Group
 *              Accelerator Systems Division
 *              Advanced Photon Source
 *              Argonne National Laboratory
 *
 * Modification Log:
 * -----------------
 * .01	jrw	07-02-92	created
 * .02	rcz	08-26-92	moved to libCom
 */

/* #define	DEBUG_DRIVER */

#include <ellLib.h>

#if     !defined(NULL) 
#define NULL            0
#endif

/****************************************************************************
 *
 * Initialize a new linked list header structure.
 *
 *****************************************************************************/
#ifndef DLLLIB_USE_MACROS
#ifdef __STDC__
void ellInit (ELLLIST *pList)
#else
void ellInit (plist)
ELLLIST	*plist;
#endif
{
  plist->node.next = NULL;
  plist->node.previous = NULL;
  plist->count = 0;
  return;
}
#endif
/****************************************************************************
 *
 * This function adds a node to the end of a linked list.
 *
 *****************************************************************************/
#ifdef __STDC__
void ellAdd (ELLLIST *pList, ELLNODE *pNode)
#else
void ellAdd (pList, pNode)
ELLLIST *pList;
ELLNODE *pNode;
#endif
{
  pNode->next = NULL;
  pNode->previous = pList->node.previous;

  if (pList->count)
    pList->node.previous->next = pNode;
  else
    pList->node.next = pNode;

  pList->node.previous = pNode;
  pList->count++;

  return;
}
/****************************************************************************
 *
 * This function concatinates the second linked list to the end of the first
 * list.  The second list is left empty.  Either list (or both) lists may
 * be empty at the begining of the operation.
 *
 *****************************************************************************/
#ifdef __STDC__
void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList)
#else
void ellConcat (pDstList, pAddList)
ELLLIST *pDstList;
ELLLIST *pAddList;
#endif
{
  if (pAddList->count == 0)
    return;	/* Add list is empty, nothing to add. */

  if (pDstList->count == 0)
  { /* Destination list is empty... just transfer the add list over. */
    pDstList->node.next = pAddList->node.next;
    pDstList->node.previous = pAddList->node.previous;
    pDstList->count = pAddList->count;
  }
  else
  { /* Destination list not empty... append the add list. */
    pDstList->node.previous->next = pAddList->node.next;
    pAddList->node.next->previous = pDstList->node.previous;
    pDstList->node.previous = pAddList->node.previous;
    pDstList->count += pAddList->count;
  }

  pAddList->count = 0;
  pAddList->node.next = NULL;
  pAddList->node.previous = NULL;

  return;
}
/****************************************************************************
 *
 * This function returns the number of nodes that are found in a list.
 *
 *****************************************************************************/
#ifndef DLLLIB_USE_MACROS
#ifdef __STDC__
int  ellCount (ELLLIST *pList)
#else
int  ellCount (pList)
ELLLIST *pList;
#endif
{
  return(pList->count);
}
#endif
/****************************************************************************
 *
 * This function deletes a specific node from a specified list;
 *
 *****************************************************************************/
#ifdef __STDC__
void ellDelete (ELLLIST *pList, ELLNODE *pNode)
#else
void ellDelete (pList, pNode)
ELLLIST *pList;
ELLNODE *pNode;
#endif
{
  if (pList->node.previous == pNode)
    pList->node.previous = pNode->previous;
  else
    pNode->next->previous = pNode->previous;

  if (pList->node.next == pNode)
    pList->node.next = pNode->next;
  else
    pNode->previous->next = pNode->next;

  pList->count--;

  return;
}
/****************************************************************************
 *
 * This function extracts a sublist that starts with pStartNode and ends with
 * pEndNode from pSrcList and places it in pDstList.
 *
 * WRS is unclear as to what happens when pDstList is non-empty at the start
 * of the operation.  We will place the extracted list at the END of pDstList
 * when it is non-empty.
 *
 *****************************************************************************/
#ifdef __STDC__
void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList)
#else
void ellExtract (pSrcList, pStartNode, pEndNode, pDstList)
ELLLIST *pSrcList;
ELLNODE *pStartNode;
ELLNODE *pEndNode;
ELLLIST *pDstList;
#endif
{
  ELLNODE	*pnode;
  int	count;

  /* Cut the list out of the source list (update count later) */
  if (pStartNode->previous != NULL)
    pStartNode->previous->next = pEndNode->next;
  else
    pSrcList->node.next = pEndNode->next;

  if (pEndNode->next != NULL)
  {
    pEndNode->next->previous = pStartNode->previous;
    pEndNode->next = NULL;
  }
  else
    pSrcList->node.previous = pStartNode->previous;

  /* Place the sublist into the destination list */
  pStartNode->previous = pDstList->node.previous;
  if (pDstList->count)
    pDstList->node.previous->next = pStartNode;
  else
    pDstList->node.next = pStartNode;

  pDstList->node.previous = pEndNode;

  /* Adjust the counts */
  pnode = pStartNode;
  count = 1;
  while(pnode != pEndNode)
  {
    pnode = pnode->next;
    count++;
  }
  pSrcList->count -= count;
  pDstList->count += count;

  return;
}
/****************************************************************************
 *
 * This function returns the first node in the specified list.  The node is 
 * NOT removed from the list.  If the list is empty, NULL will be returned.
 *
 *****************************************************************************/
#ifndef DLLLIB_USE_MACROS
#ifdef __STDC__
ELLNODE *ellFirst (ELLLIST *pList)
#else
ELLNODE *ellFirst (pList)
ELLLIST *pList;
#endif
{
  return(pList->node.next);
}
#endif
/****************************************************************************
 *
 * This function returns the first node in the specified list.  The node is
 * removed from the list.  If the list is empty, NULL will be returned.
 *
 *****************************************************************************/
#ifdef __STDC__
ELLNODE *ellGet (ELLLIST *pList)
#else
ELLNODE *ellGet (pList)
ELLLIST *pList;
#endif
{
  ELLNODE	*pnode = pList->node.next;

  if (pnode != NULL)
    ellDelete(pList, pnode);

  return(pnode);
}
/****************************************************************************
 *
 * This function inserts the specified node pNode after pPrev in the list
 * plist.  If pPrev is NULL, then pNode will be inserted at the head of the
 * list.
 *
 *****************************************************************************/
#ifdef __STDC__
void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode)
#else
void ellInsert (plist, pPrev, pNode)
ELLLIST *plist;
ELLNODE *pPrev;
ELLNODE *pNode;
#endif
{
  if (pPrev != NULL)
  {
    pNode->previous = pPrev;
    pNode->next = pPrev->next;
    pPrev->next = pNode;
  }
  else
  {
    pNode->previous = NULL;
    pNode->next = plist->node.next;
    plist->node.next = pNode;
  }
  if (pNode->next == NULL)
    plist->node.previous = pNode;
  else
    pNode->next->previous = pNode;

  plist->count++;

  return;
}
/****************************************************************************
 *
 * This function returns the last node in a list.  The node is NOT removed 
 * from the list.  If the list is empty, NULL will be returned.
 *
 *****************************************************************************/
#ifndef DLLLIB_USE_MACROS
#ifdef __STDC__
ELLNODE *ellLast (ELLLIST *pList)
#else
ELLNODE *ellLast (pList)
ELLLIST *pList;
#endif
{
  return(pList->node.previous);
}
#endif
/****************************************************************************
 *
 * This function returns the node following pNode.  The node is NOT remodev from
 * the list.  If pNode is the last element in the list, NULL will be returned.
 *
 *****************************************************************************/
#ifndef DLLLIB_USE_MACROS
#ifdef __STDC__
ELLNODE *ellNext (ELLNODE *pNode)
#else
ELLNODE *ellNext (pNode)
ELLNODE *pNode;
#endif
{
  return(pNode->next);
}
#endif
/****************************************************************************
 *
 * This function returns the nodeNum'th element in pList.  If there is no 
 * nodeNum'th node in the list, NULL will be returned.
 *
 *****************************************************************************/
#ifdef __STDC__
ELLNODE *ellNth (ELLLIST *pList, int nodeNum)
#else
ELLNODE *ellNth (pList, nodeNum)
ELLLIST *pList;
int nodeNum;
#endif
{
  ELLNODE *pnode;

  if ((nodeNum < 1)||(pList->count == 0))
    return(NULL);

  if (nodeNum > pList->count/2)
  {
    if (nodeNum > pList->count)
      return(NULL);

    pnode = pList->node.previous;
    nodeNum = pList->count - nodeNum;
    while(nodeNum)
    {
      pnode = pnode->previous;
      nodeNum--;
    }
    return(pnode);
  }

  pnode = pList->node.next;
  while(--nodeNum > 0)
    pnode = pnode->next;

  return(pnode);
}
/****************************************************************************
 *
 * This function returns the node that preceeds pNode.  If pNode is the first
 * element in the list, NULL will be returned.
 *
 *****************************************************************************/
#ifndef DLLLIB_USE_MACROS
#ifdef __STDC__
ELLNODE *ellPrevious (ELLNODE *pNode)
#else
ELLNODE *ellPrevious (pNode)
ELLNODE *pNode;
#endif
{
  return(pNode->previous);
}
#endif
/****************************************************************************
 *
 * This function returns the node, nStep nodes forward from pNode.  If there is
 * no node that many steps from pNode, NULL will be returned.
 *
 *****************************************************************************/
#ifdef __STDC__
ELLNODE *ellNStep (ELLNODE *pNode, int nStep)
#else
ELLNODE *ellNStep (pNode, nStep)
ELLNODE *pNode;
int nStep;
#endif
{
  if (nStep > 0)
  {
    while ((pNode != NULL) && nStep)
    {
      pNode = pNode->next;
      nStep--;
    }
  }
  else
  {
    while ((pNode != NULL) && nStep)
    {
      pNode = pNode->previous;
      nStep++;
    }
  }
  return(pNode);
}
/****************************************************************************
 *
 * This function returns the node number of pNode within pList.  If the node is
 * not in pList, -1 is returned.  Note that the first node is 1.
 *
 *****************************************************************************/
#ifdef __STDC__
int  ellFind (ELLLIST *pList, ELLNODE *pNode)
#else
int  ellFind (pList, pNode)
ELLLIST *pList;
ELLNODE *pNode;
#endif
{
  ELLNODE *got = pList->node.next;
  int	count = 1;

  while ((got != pNode) && (got != NULL))
  {
    got = got->next;
    count++;
  }
  if (got == NULL)
    return(-1);
    
  return(count);
}
/****************************************************************************
 *
 * This function frees the nodes in a list.  It makes the list into an empty
 * list, and free()'s all the nodes that are (were) in that list.
 *
 * NOTE: the nodes in the list are free()'d on the assumption that the node
 * structures were malloc()'d one-at-a-time and that the ELLNODE structure is
 * the first thing in the "rest of" the node structure.  
 * In other words, this is a pretty worthless function.
 *
 *****************************************************************************/
#ifdef __STDC__
void ellFree (ELLLIST *pList)
#else
void ellFree (pList)
ELLLIST *pList;
#endif
{
  ELLNODE *nnode = pList->node.next;
  ELLNODE *pnode;

  while (nnode != NULL)
  {
    pnode = nnode;
    nnode = nnode->next;
    free(pnode);
  }
  pList->node.next = NULL;
  pList->node.previous = NULL;
  pList->count = 0;

  return;
}
#ifdef DEBUG_DRIVER
/****************************************************************************
 *
 * This is a bunch of code that can be used to test the ellLib code.  It is
 * provided here so that you may use it if and when you decide to alter 
 * something in this file, you need not re-create it for testing purposes.
 *
 * In general, I believe that you will end up with a core dump if the tests
 * fail.  If all tests complete properly, there will be a:
 *            printf("All tests completed successful\n") 
 * to assure you of it.
 *
 *****************************************************************************/

#include <malloc.h>
#undef NULL
#include <stdio.h>

struct	myNode {
  ELLNODE	ellNode;
  int	list;
  int	num;
};
main()
{
  ELLLIST	list1;
  ELLLIST	list2;
  int	i1, i2, i3;
  struct myNode	*pmyNode, *pfirst, *pn1;

  printf("\nellInit() check...  ");
  list1.count = 27;
  list1.node.next = (ELLNODE *) 0x01010101;
  list1.node.previous = (ELLNODE *) 0x10101010;

  ellInit(&list1);
  ellInit(&list2);

  if ((list1.count != 0)||(list1.node.next != NULL)||(list1.node.previous != NULL))
  {
    printf("FAILED!!!");
    printf("list1.count %d, list1.node.next %08.8X, list1.node.previous %08.8X\n", list1.count, list1.node.next, list1.node.previous);
    exit(1);
  }
  printf("ok");


  /* First build a couple lists and fill them with nodes. */
  printf("\nellAdd() check...  ");
  i1 = 2;
  pmyNode = (struct myNode *) malloc(sizeof(struct myNode));
  pmyNode->ellNode.next = (ELLNODE *) 0x10101010;
  pmyNode->ellNode.previous = (ELLNODE *) 0x10101010;

  ellAdd(&list1, pmyNode);
  pmyNode->list = 1;
  pmyNode->num = 1;

  if ((list1.count != 1)||(list1.node.next != (ELLNODE *)pmyNode)||
      (list1.node.previous != (ELLNODE *)pmyNode)||(pmyNode->ellNode.next != NULL)||
      (pmyNode->ellNode.previous != NULL))
  {
    printf("FAILED!!!!!\n");
    exit(2);
  }
  pfirst = pmyNode;
  while(i1 <= 21)	/* put 21 nodes into LIST1 */
  {
    pmyNode = (struct myNode *) malloc(sizeof(struct myNode));
    ellAdd(&list1, pmyNode);
    pmyNode->list = 1;
    pmyNode->num = i1;
    i1++;
  }
  if ((list1.count != 21)||(list1.node.next != (ELLNODE *)pfirst)||
      (list1.node.previous != (ELLNODE *)pmyNode)||(pmyNode->ellNode.next != NULL))
  {
    printf("FAILED!!!\n");
    exit(3);
  }
  printf("ok\nellFirst() check...  ");

  if (ellFirst(&list1) != (ELLNODE *)pfirst)
  {
    printf("FAILED!!!\n");
    exit(5);
  }
  printf("ok\nellLast() check...  ");
  if (ellLast(&list1) != (ELLNODE *)pmyNode)
  {
    printf("FAILED!!!\n");
    exit(6);
  }
  printf("ok\nellNext() check...  ");
  if ((ellNext(pmyNode) != NULL)||(ellNext(pfirst) != (ELLNODE *)(pfirst->ellNode.next)))
  {
    printf("FAILED!!!\n");
    exit(7);
  }
  printf("ok\nellNth() check...  ");
  if ((ellNth(&list1, 0) != NULL)||
      ((pn1 = (struct myNode *)ellNth(&list1, 21)) != pmyNode)||
      (ellNth(&list1, 22) != NULL)||
      (ellNth(&list1, 1) != (ELLNODE *)(pfirst))||
      (ellNth(&list1, 2) != (ELLNODE *)(pfirst->ellNode.next))||
      (ellNth(&list1, 20) != (ELLNODE *)(pmyNode->ellNode.previous)))
  {
    printf("FAILED!!!\n");
    exit(8);
  }
  printf("ok\nellPrevious() check...  ");

  if ((ellPrevious(pmyNode) != (ELLNODE *)(pmyNode->ellNode.previous))||
     (ellPrevious(pfirst) != NULL)||
     (ellPrevious(pfirst->ellNode.next) != (ELLNODE *)pfirst))
  {
    printf("FAILED!!!\n");
    exit(9);
  }
  printf("ok\nellGet() check...  ");
  if (((pn1 = (struct myNode *)ellGet(&list1)) != pfirst)||
      (list1.node.next != pfirst->ellNode.next))
  {
    printf("FAILED!!!\n");
    exit(10);
  }
  ellAdd(&list2, pn1);
  if (((pn1 = (struct myNode *)ellGet(&list2)) != pfirst)||
      (list2.node.next != NULL)||(list2.node.previous != NULL))
  {
    printf("FAILED!!!\n");
    exit(11);
  }
  printf("ok\nellCount() check...  ");
  if ((ellCount(&list1) != 20)||(ellCount(&list2) != 0))
  {
    printf("FAILED!!!\n");
    exit(4);
  }
  printf("ok\nellConcat() check...  ");
  ellConcat(list1, list2);
  if ((ellCount(&list1) != 20)||(ellCount(&list2) != 0)||
      (list1.node.previous != (ELLNODE *)pmyNode))
  {
    printf("FAILED!!! (12)\n");
    exit(12);
  }
  ellAdd(&list2, pn1);
  ellConcat(&list1, &list2);
  if ((ellCount(&list1) != 21)||(ellCount(&list2) != 0)||
      (list1.node.previous != (ELLNODE *)pn1)||
      (list2.node.next != NULL)||(list2.node.previous!=NULL))
  {
    printf("FAILED!!! (13)\n");
    printf("list1.count = %d list2.count = %d\n", ellCount(&list1), ellCount(&list2));
    exit(13);
  }
  printf("ok\nellDelete() check...  ");
  ellDelete(&list1, pn1);
  if (ellCount(&list1) != 20)
  {
    printf("FAILED!!! (14)\n");
    exit(14);
  }
  printf("ok\nellFind() check...  ");
  ellAdd(&list2, pn1);
  ellConcat(&list2, &list1);
  i1 = -23;
  if (ellFind(&list1, pn1) != -1)	/* empty list */
  {
    printf("FAILED!!! (15)\n");
    exit(15);
  }
  if ((i1 = ellFind(&list2, pn1)) != pn1->num)	/* first node */
  {
    printf("FAILED!!! (16)\n");
    exit(16);
  }
  pn1 = (struct myNode *)ellNth(&list2, 18);
  if ((i1 = ellFind(&list2, pn1)) != 18)	/* 18th node */
  {
    printf("FAILED!!! (17)\n");
    exit(17);
  }
  printf("ok\nellInsert() check...  ");
  ellDelete(&list2, pn1);
  ellInsert(&list2, NULL, pn1);			/* move node 18 to top */
  if ((ellCount(&list2) != 21)||(((struct myNode *)(list2.node.next))->num != 18))
  {
    printf("FAILED!!! (18)\n");
    exit (18);
  }
  if (ellFind(&list2, ellNth(&list2, 21)) != 21)
  { /* Run thru all pointers to check integrity */
    printf("FAILED!!! (19)\n");
    exit(19);
  }
  pn1 = (struct myNode *)ellGet(&list2);
  pmyNode = (struct myNode *)ellNth(&list2, 17);
  ellInsert(&list2, pmyNode, pn1);
  if (ellFind(&list2, ellNth(&list2, 21)) != 21)
  { /* Run thru all pointers to check integrity */
    printf("FAILED!!! (20)\n");
    exit(20);
  }
  if ((((struct myNode *)(ellFirst(&list2)))->num != 1)||
      (((struct myNode *)(ellNth(&list2,17)))->num != 17)||
      (((struct myNode *)(ellNth(&list2,18)))->num != 18))
  {
    printf("FAILED!!! (21)\n");
    exit(21);
  }
  pn1 = (struct myNode *)ellLast(&list2);
  pmyNode = (struct myNode *) malloc(sizeof(struct myNode));
  ellInsert(&list2, pn1, pmyNode);
  if ((ellCount(&list2) != 22)||(ellFind(&list2, ellNth(&list2, 22)) != 22))
  {
    printf("FAILED!!! (33)\n");
    exit(33);
  }
  ellDelete(&list2, pmyNode);
  free(pmyNode);

  printf("ok\nellExtract() check...  ");
  ellExtract(&list2, ellNth(&list2,9), ellNth(&list2, 19), &list1);
  if ((ellCount(&list2) != 10)||(ellCount(&list1) != 11))
  {
    printf("FAILED!!! (22)\n");
    exit(22);
  }
  if ((ellFind(&list2, ellNth(&list2, 10)) != 10)||
      (ellFind(&list1, ellNth(&list1, 11)) != 11))
  {
    printf("FAILED!!! (23)\n");
    exit(23);
  }
  printf("ok\nellFree() check...  ");
  ellFree(&list2);
  if (ellCount(&list2) != 0)
  {
    printf("FAILED!!! (24)\n");
    exit(24);
  }
  printf("ok\nellNStep() check...  ");
  pn1 = (struct myNode *)ellFirst(&list1);
  i1 = 1;
  while(pn1 != NULL)
  {
    pn1->num = i1++;
    pn1 = (struct myNode *)ellNext(pn1);
  }
  pn1 = (struct myNode *)ellFirst(&list1);
  if (pn1 == NULL)
  {
    printf("FAILED!!! (30)\n");
    exit(30);
  }
  pmyNode = (struct myNode *)ellNStep(pn1, 3);
  if (pmyNode == NULL)
  {
    printf("FAILED!!! (27)\n");
    exit(27);
  }
  if (pmyNode->num != 4)
  {
    printf("FAILED!!! (25)\n");
    printf("got number %d\n", pmyNode->num);
    exit(25);

  }
  pmyNode = (struct myNode *)ellNStep(pn1, 30);
  if (pmyNode != NULL)
  {
    printf("FAILED!!! (26)\n");
    exit(26);
  }
  pmyNode = (struct myNode *)ellNStep(pn1, 10);
  if (pmyNode == NULL)
  {
    printf("FAILED!!! (28)\n");
    exit(28);
  }
  if (pmyNode->num != 11)
  {
    printf("FAILED!!! (29)\n");
    exit(29);
  }
  pmyNode = (struct myNode *)ellNStep(pmyNode, 0);
  if (pmyNode->num != 11)
  {
    printf("FAILED!!! (31)\n");
    exit(31);
  }
  pmyNode = (struct myNode *)ellNStep(pmyNode, -4);
  if (pmyNode->num != 7)
  {
    printf("FAILED!!! (32)\n");
    exit(32);
  }
  printf("ok\n\nAll tests completed successful\n");
}
#endif
