/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
/*******************************************************************************
* SYSTEM...: Virtual Notebook System
*
* MODULE...: pagelist.c
*
* PURPOSE..:
*   provide the data structures and functions to allow for the manipulation
*   of a pagelist/objectlist created to be sent to the VNS printing utility for
*   printing 
*
* TAG: PLL 
*
* REVISIONS:
*   Ross Dargahi                December 1990 Initial Coding
*
*******************************************************************************/

#define PAGELIST_C    /*define source name for includes*/

/*******************************************************************************
Include Files
*******************************************************************************/

#include <stdio.h>
#include <malloc.h>
#include "pagelist.h"

/*******************************************************************************
Private Defines
*******************************************************************************/

#define BOOLEAN char
#define FALSE   0
#define PRIVATE static
#define TRUE    !FALSE

/*******************************************************************************
Private Data Structures
*******************************************************************************/

typedef struct objNode_s  OBJNODE_S;
typedef struct pageNode_s PAGENODE_S;
typedef struct pageList_s PAGELIST_S;
typedef struct plNode_s   PLLNODE_S;

struct objNode_s  /*object node in a page node*/
{
  int oid;
};

struct pageNode_s  /*page node in a page list*/
{
  int        nid;
  int        pid;
  PLLNODE_S  *firstObj;
  PLLNODE_S  *lastObj;
  PLLNODE_S  *currObj;
};

struct plNode_s  /*page node in a page list*/
{
  union
   {
     PAGENODE_S aPage;
     OBJNODE_S  aObj;
   } nData;

  PLLNODE_S *next;
  PLLNODE_S *previous;
};

struct pageList_s  /*header for a page list*/
{
  PLLNODE_S *firstPage;  /*first page in the page list*/
  PLLNODE_S *lastPage;   /*last page in the page list*/
  PLLNODE_S *currPage;   /*current page in the page list*/
};


/*******************************************************************************
Private Global Declarations
*******************************************************************************/



/*******************************************************************************
Private Function Prototypes
*******************************************************************************/

PRIVATE void          deleteNode();
PRIVATE PLL_RETCODE_E gotoSpecifiedItem();
PRIVATE void          insertNode();
PRIVATE BOOLEAN       objectCompare();
PRIVATE BOOLEAN       pageCompare();

/*******************************************************************************
****************************    PUBLIC FUNCTIONS    ****************************
*******************************************************************************/

PUBLIC char *PLLcreate()

/*creates a handle that points to a header block for the page list*/

{
  return((char *)calloc(1, sizeof(PAGELIST_S)));

} /*PLLcreate*/

/******************************************************************************/
PUBLIC PLL_RETCODE_E PLLdelete(handle, type)

char       *handle;  /*handle to a page list created by PLLcreate*/
PLL_ITEM_E type;     /*work with an OBJECT or PAGE list*/

/*deletes the current object or page as specified by <type>. It then always
tries to set the current pointer to be the next item in the list, if there is
no next item it tries to set it to the previous item, if it does not exist the
list is empty. If a page that contains an object list is deleted then all the
objects on that page ARE ALSO DELETED*/

{
  PLL_LISTDATA_S data;         /*struct for deleting objects on page*/
  PAGELIST_S     *listHandle;  /*local handle to list pointer*/

  if (!(listHandle = (PAGELIST_S *)handle))  /*check for empty page list*/
     return(PLL_NULLPAGELIST);
  else if (!listHandle->firstPage)
     return(PLL_EMPTYPAGELIST);
  else
   {
     switch(type)
      {
        case PLL_OBJECT:
           if (!listHandle->currPage->nData.aPage.currObj)
              return(PLL_EMPTYOBJLIST);
           else 
              deleteNode(&listHandle->currPage->nData.aPage.firstObj,
                         &listHandle->currPage->nData.aPage.lastObj,
                         &listHandle->currPage->nData.aPage.currObj);
           break;
              
        case PLL_PAGE:
           data.oid = PLL_FIRST;

           if (!PLLgoto(handle, PLL_OBJECT, &data))
              while (!PLLdelete(handle, PLL_OBJECT));

           deleteNode(&listHandle->firstPage,
                      &listHandle->lastPage,
                      &listHandle->currPage);
           break;
      }
   }

  return(PLL_OK);

} /*PLLdelete*/

/******************************************************************************/
PUBLIC PLL_RETCODE_E PLLgetData(handle, type, data)

char           *handle;  /*handle to a page list created by PLLcreate*/
PLL_ITEM_E     type;     /*work with an OBJECT or PAGE list*/
PLL_LISTDATA_S *data;    /*data to work with*/

/*Gets object or page data from the current position*/

{
  PAGELIST_S *listHandle;  /*local handle to list pointer*/

  if (!(listHandle = (PAGELIST_S *)handle))  /*check for empty page list*/
     return(PLL_NULLPAGELIST);
  else if (!listHandle->firstPage)
     return(PLL_EMPTYPAGELIST);
  else
     switch (type)
      {
        case PLL_OBJECT:
           if (!listHandle->currPage->nData.aPage.currObj)
              return(PLL_EMPTYOBJLIST);
           else
              data->oid = 
              listHandle->currPage->nData.aPage.currObj->nData.aObj.oid;
           break;

        case PLL_PAGE:
           data->nid = listHandle->currPage->nData.aPage.nid;
           data->pid = listHandle->currPage->nData.aPage.pid;
           break;
      }

  return(PLL_OK);

} /*PLLgetData*/

/******************************************************************************/
PUBLIC PLL_RETCODE_E PLLgoto(handle, type, data)

char           *handle;  /*handle to a page list created by PLLcreate*/
PLL_ITEM_E     type;     /*work with an OBJECT or PAGE list*/
PLL_LISTDATA_S *data;    /*data to work with*/

/*goes to the specified page in a page list or object in a page object list. Can
go to the first, last, next or previous items in a list if so desired*/

{
  PAGELIST_S *listHandle;  /*local handle to list pointer*/

  if (!(listHandle = (PAGELIST_S *)handle))  /*check for empty page list*/
     return(PLL_NULLPAGELIST);
  else if (!listHandle->firstPage)
     return(PLL_EMPTYPAGELIST);
  else
     switch (type)
      {
        case PLL_OBJECT:
           if (!listHandle->currPage->nData.aPage.firstObj)
              return(PLL_EMPTYOBJLIST);
           else
            {
              PLLNODE_S *currPage;

              currPage = listHandle->currPage;

              switch (data->oid)
               {
                 case PLL_FIRST:
                    currPage->nData.aPage.currObj =
                       currPage->nData.aPage.firstObj;
                    break;

                 case PLL_LAST:
                    currPage->nData.aPage.currObj =
                       currPage->nData.aPage.lastObj;
                    break;
                      
                 case PLL_NEXT:
                    if (currPage->nData.aPage.currObj->next)
                       currPage->nData.aPage.currObj = 
                          currPage->nData.aPage.currObj->next;
                    else
                       return(PLL_ATENDOFOBJLIST);
                    break;

                 case PLL_PREVIOUS:
                    if (currPage->nData.aPage.currObj->previous)
                       currPage->nData.aPage.currObj = 
                          currPage->nData.aPage.currObj->previous;
                    else
                       return(PLL_ATBEGOFOBJLIST);
                    break;

                 default:
                    return(gotoSpecifiedItem(currPage->nData.aPage.firstObj,
                                             &currPage->nData.aPage.currObj, 
                                             objectCompare, data));
                    break; 
               }
            }
                    
           break;

        case PLL_PAGE:
           switch (data->nid)
            {
              case PLL_FIRST:
                 listHandle->currPage = listHandle->firstPage;
                 break;

              case PLL_LAST:
                 listHandle->currPage = listHandle->lastPage;
                 break;
                      
              case PLL_NEXT:
                 if (listHandle->currPage->next)
                    listHandle->currPage = listHandle->currPage->next;
                 else
                    return(PLL_ATENDOFPAGELIST);
                 break;

              case PLL_PREVIOUS:
                 if (listHandle->currPage->previous)
                    listHandle->currPage = listHandle->currPage->previous;
                 else
                    return(PLL_ATBEGOFPAGELIST);
                 break;

              default:
                 return(gotoSpecifiedItem(listHandle->firstPage,
                                          &listHandle->currPage,
                                          pageCompare, data));
                 break; 
            }

           break;
      }

  return(PLL_OK);

} /*PLLgoto*/

/******************************************************************************/
PUBLIC PLL_RETCODE_E PLLinsert(handle, type, data, position, setCurrent)

char           *handle;     /*handle to a page list created by PLLcreate*/
PLL_ITEM_E     type;        /*insert an object or a page*/
PLL_LISTDATA_S *data;       /*data values to insert into the list*/
PLL_POSITION_E position;    /*position to insert at*/
BOOLEAN        setCurrent;  /*T: make new node the current one*/

/*inserts an item (page or object) into the list at the specified location*/

{
  PLLNODE_S  *currPage;
  PAGELIST_S *listHandle;  /*local handle to list pointer*/
  PLLNODE_S  *newNode;

  if (!(listHandle = (PAGELIST_S *)handle))  /*check for empty page list*/
     return(PLL_NULLPAGELIST);
  else if (newNode = (PLLNODE_S *)calloc(1, sizeof(PLLNODE_S)))
   {
     switch (type)
      {
        case PLL_OBJECT:
           if (!listHandle->firstPage)
              return(PLL_EMPTYPAGELIST);

           currPage = listHandle->currPage;
           newNode->nData.aObj.oid = data->oid;
           insertNode(&currPage->nData.aPage.firstObj,
                      &currPage->nData.aPage.lastObj,
                      &currPage->nData.aPage.currObj,
                      position,
                      newNode); 

           if (setCurrent)
              currPage->nData.aPage.currObj = newNode;
                 
           break;

        case PLL_PAGE:
           newNode->nData.aPage.nid = data->nid;
           newNode->nData.aPage.pid = data->pid;
           insertNode(&listHandle->firstPage,
                      &listHandle->lastPage,
                      &listHandle->currPage,
                      position,
                      newNode); 

           if (setCurrent)
               listHandle->currPage = newNode;
                 
           break;

      } 
   }
  else
     printf("Out Of Heap Space\n");

  return(PLL_OK);

} /*PLLinsert*/

/******************************************************************************/
PUBLIC PLL_RETCODE_E PLLkillList(handle)

char **handle;  /*handle to the page list to kill*/

/*completely deletes a page list. This includes removing the pages, the objects
and setting the handle to NULL*/

{
  PLLNODE_S *currObjNode;
  PLLNODE_S *currPageNode;
  PLLNODE_S *nextNode;

  if (!*handle)  /*check its a valid page list*/
     return(PLL_NULLPAGELIST);
  else
   {
     currPageNode = ((PAGELIST_S *)*handle)->firstPage;

     while (currPageNode)  /*traverse & delete the pages*/
      {
        currObjNode = currPageNode->nData.aPage.firstObj;

        while (currObjNode) /*traverse & delete the objects*/
         {
           nextNode = currObjNode->next;
           free(currObjNode);
           currObjNode = nextNode;
         }

        nextNode = currPageNode->next;
        free(currPageNode);
        currPageNode = nextNode;
      }

     /*free the list header & set the list pointer to NULL*/

     free(*handle);
     *handle = NULL;
   }

} /*PLLkillList*/

/*******************************************************************************
****************************    PRIVATE FUNCTIONS    ***************************
*******************************************************************************/

PRIVATE PLL_RETCODE_E gotoSpecifiedItem(startNode, currNode, compFunc, data)

PLLNODE_S      *startNode;
PLLNODE_S      **currNode;
BOOLEAN        (*compFunc)();
PLL_LISTDATA_S *data;

/*tries to goto the specified page or object if it exists, uses the forward/
backward search hieuristic (for lack of anything better)*/

{
  BOOLEAN  found;

  found = FALSE;

  while (startNode && !(found = (*compFunc)(startNode, data)))
     startNode = startNode->next;

  if (found)
   {
     *currNode = startNode;
     return(PLL_OK);
   }
  else
     return(PLL_NOSUCHITEM);
  

} /*gotoSpecifiedItem*/

/******************************************************************************/
PRIVATE void deleteNode(first, last, current)

PLLNODE_S      **first;    /*header pointer to first node in list*/
PLLNODE_S      **last;     /*header pointer to last node in list*/
PLLNODE_S      **current;  /*header pointer to current node in list*/

/*delete the current node from the linked list and adjust all pointers
accordingly*/

{
  PLLNODE_S *temp;

  temp = *current;

  if (*first == *last) /*only one node in list*/
     *first = *last = *current = NULL;
  else if (*current == *first) /*delete the first item in the list*/
   {
     *first = *current = temp->next;
     temp->next->previous = NULL; 
   }
  else if (*current == *last)  /*delete the last item in the list*/
   {
     *last = *current = temp->previous;
     temp->previous->next = NULL;
   }
  else /*deleting an interior node*/
   {
     temp->previous->next = temp->next;
     temp->next->previous = temp->previous;
     *current = temp->next;
   }

  free(temp);

} /*deleteNode*/

/******************************************************************************/
PRIVATE void insertNode(first, last, current, position, data)

PLLNODE_S      **first;    /*header pointer to first node in list*/
PLLNODE_S      **last;     /*header pointer to last node in list*/
PLLNODE_S      **current;  /*header pointer to current node in list*/
PLL_POSITION_E position;   /*position to insert at*/
PLLNODE_S      *data;      /*data to insert*/

/*inserts a node into a linked list. the node can be an object or a page this
routine does not care which it is*/

{
  if (*first == NULL && *last == NULL)  /*empty list*/
     *first = *last = *current = data; 
  else
     switch (position)
      {
        case PLL_FIRST:
           (*first)->previous = data;
           data->next = *first;
           *first = data;
           break;

        case PLL_LAST:
           (*last)->next = data;
           data->previous = *last;
           *last = data;
           break;

        case PLL_NEXT:
           if (*current == *last)
              *last = data;
           else
              (*current)->next->previous = data;

           data->next = (*current)->next;
           (*current)->next = data;
           data->previous = *current;
           break;
           
        case PLL_PREVIOUS:
           if (*current == *first)
              *first = data;
           else
              (*current)->previous->next = data;

           data->previous = (*current)->previous;
           (*current)->previous = data;
           data->next = *current;
           break;
      }

} /*insertNode*/

/******************************************************************************/
PRIVATE BOOLEAN objectCompare(dataPtr, compData)

PLLNODE_S      *dataPtr;
PLL_LISTDATA_S *compData;

{
  if (dataPtr->nData.aObj.oid == compData->oid)
     return(TRUE);
  else
     return(FALSE);

} /*objectCompare*/

/******************************************************************************/
PRIVATE BOOLEAN pageCompare(dataPtr, compData)

PLLNODE_S      *dataPtr;
PLL_LISTDATA_S *compData;

{
  if (dataPtr->nData.aPage.nid == compData->nid &&
      dataPtr->nData.aPage.pid == compData->pid)
     return(TRUE);
  else
     return(FALSE);

} /*pageCompare*/

/*******************************************************************************
Private undefines
*******************************************************************************/
#undef PAGELIST_C
