/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program 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 General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/****************************************************************************\
*        C O N F I D E N T I A L --- W E S T W O O D   S T U D I O S         *
******************************************************************************
Project Name: 
File Name   : linkedlist.h
Author      : Neal Kettler
Start Date  : June 19, 1997
Last Update : June 19, 1997

Linked list template.  This is a fairly standard doubly linked list that
allows insertion and removal at any point in the list.  A current pointer
is used to quickly access items when they are examined sequentially.
Copies of the data are stored instead of a pointer to the original.

If you want to store pointers then the template should be of a pointer type.
\****************************************************************************/

#ifndef LINKEDLIST_HEADER
#define LINKEDLIST_HEADER    

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "wstypes.h"

template <class T>
class LNode
{
 public:
  T               Node;
  LNode<T>       *Next;
  LNode<T>       *Prev;
};        

template <class T>
class LinkedList
{
 public:
                   LinkedList();
                   LinkedList(LinkedList<T> &other);
                  ~LinkedList();

  // Remove all entries from the lsit 
  void             clear(void);

  // Add a node after the zero based 'pos'
  bit8             add(IN T &node,sint32 pos, OUT T **newnodeptr=NULL);
  bit8             addTail(IN T &node, OUT T **newnodeptr=NULL);
  bit8             addHead(IN T &node, OUT T **newnodeptr=NULL);

  // Remove a node
  bit8             remove(OUT T &node,sint32 pos);
  bit8             remove(sint32 pos);
  bit8             removeHead(OUT T &node);
  bit8             removeTail(OUT T &node); 


  // Get a node without removing from the list
  bit8             get(OUT T &node,sint32 pos);
  bit8             getHead(OUT T &node);
  bit8             getTail(OUT T &node);        

  // Get a pointer to the internally managed data (careful!)
  bit8             getPointer(OUT T **node, sint32 pos);

  // Get the number of entries in the list
  sint32           length(void);

  // Print information on the list
  void             print(IN FILE *out);

  // assignment operator
  LinkedList<T>   &operator=(LinkedList<T> &other);

 private:
  sint32           Entries;    // Number of entries
  LNode<T>        *Head;       // Head of the list
  LNode<T>        *Tail;       // Tail of the list

  LNode<T>        *Current;    // Current pointer & index for speed only
  sint32           CurIndex;
};


//Create the empty list 
template <class T>
LinkedList<T>::LinkedList()
{
  Entries=0;
  Head=Tail=Current=NULL;
  CurIndex=-1;  // Not valid when 0 entries
}

// copy constructor
template <class T>
LinkedList<T>::LinkedList(LinkedList<T> &other)
{
  Entries=0;
  Head=Tail=Current=NULL;
  CurIndex=-1;  // Not valid when 0 entries
  (*this)=other;
}

//Free all the memory...
template <class T>
LinkedList<T>::~LinkedList()
{
  clear();          // Remove the entries
}

// assignment operator
template <class T>
LinkedList<T> &LinkedList<T>::operator=(LinkedList<T> &other)
{
  T node;
  clear();
  for (int i=0; i<other.length(); i++)
  {
    other.get(node,i);
    addTail(node);
  } 
  return(*this);
}


// Remove all the entries and free the memory
template <class T>
void LinkedList<T>::clear()
{
  LNode<T> *temp,*del;

  temp=Head;
  while (temp) {
    del=temp;
    temp=temp->Next;
    delete(del);
  }
  Entries=0;
  CurIndex=-1;
  Head=Tail=Current=NULL;
}            

// When adding into a position, the new node goes at the zero based slot
// specified by pos. All other nodes get moved one slot down.
template <class T>
bit8 LinkedList<T>::add(IN T &node,sint32 pos, OUT T **newnodeptr)
{
  LNode<T> *temp;
  LNode<T> *item;

  if (pos<0)
    pos=0;
  if (pos>Entries)
    pos=Entries;

  item=(LNode<T> *)new LNode<T>;
  assert(item!=NULL);
  item->Node=node;  // copy the passed in object
  item->Prev=NULL;
  item->Next=NULL;

  if (newnodeptr)
    *newnodeptr=&(item->Node);

  if ((pos==0)||(pos==Entries)) {  // Both cases can be true for a new list!
    if (pos==0) {
      item->Next=Head;
      if (Head)
        Head->Prev=item; 
      Head=item;
    }
    if (pos==Entries) {
      item->Prev=Tail;
      if (Tail)
        Tail->Next=item;
      Tail=item;
    }
    Entries++;
    Current=item;
    CurIndex=pos;
    return(TRUE);
  }

  // If control is here, we know the new node is not an endpoint

  // Check for possible speedup, so we don't have to scan the list
  if (pos==CurIndex) {
    item->Next=Current;
    item->Prev=Current->Prev;
    Current->Prev=item;
    item->Prev->Next=item;
    Current=item;
    Entries++;
    return(TRUE);
  }
  // Check the other possible speedup (adding after CurIndex) 
  if (pos==CurIndex+1) {
    item->Next=Current->Next;
    item->Prev=Current;
    Current->Next=item;
    item->Next->Prev=item;
    Current=item;
    CurIndex++;
    Entries++;
    return(TRUE);
  }    

  // If control reaches here we have to scan the whole thing
  temp=Head->Next;  // Can start at node '1' because head was special cased
  for (int i=1; i<pos; i++) {
    temp=temp->Next; 
    assert(temp!=NULL);
  }
  item->Next=temp;
  item->Prev=temp->Prev;
  temp->Prev=item;
  item->Prev->Next=item; 
  Current=item;
  CurIndex=pos;
  Entries++;

  return(TRUE);
}


// Add to the first node, all others get shifted down one slot
template <class T>
bit8 LinkedList<T>::addHead(IN T &node, OUT T **newnodeptr)  
{
  return(add(node,0,newnodeptr));
}


// Append to the end of the list
template <class T>
bit8 LinkedList<T>::addTail(IN T &node, OUT T **newnodeptr)
{
  return(add(node,length(),newnodeptr));
}  


// Remove at the zero based index specified by 'pos'.  When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 LinkedList<T>::remove(OUT T &node, sint32 pos) 
{
  ////////LNode<T> *temp;
  LNode<T> *item;

  if (Entries==0)
    return(FALSE);

  if (pos<0)
    pos=0;
  if (pos>=Entries)
    pos=Entries-1;    

  if ((pos==0)||(pos==Entries-1)) {  // Both can be true for a 1 item list
    if (pos==0) {
      item=Head;
      if (item->Next)
        item->Next->Prev=NULL;
      Head=item->Next;
      node=item->Node;
      Current=Head;
      CurIndex=0;
    }
    if (pos==Entries-1) {
      item=Tail;
      if (item->Prev)
        item->Prev->Next=NULL;
      Tail=item->Prev;
      node=item->Node;
      Current=Tail;
      CurIndex=Entries-2;
    }
    delete(item);
    Entries--;

    if (Entries==0) { // Super paranoia check
      assert(Current==NULL);
      assert(CurIndex==-1);
      assert(Head==NULL);
      assert(Tail==NULL);
    }
    return(TRUE);
  }     
  // If control is here, we know the target node is not an endpoint

  // Check for possible speedup, so we don't have to scan the list
  if (pos==CurIndex) {
    item=Current;
    item->Prev->Next=item->Next;
    item->Next->Prev=item->Prev;
    Current=item->Next;
    // CurIndex stays the same
    node=item->Node;
    delete(item);
    Entries--;
    return(TRUE);
  }

  // Check the other possible speedup (removing after CurIndex)
  if (pos==CurIndex+1) {
    item=Current->Next;
    item->Prev->Next=item->Next;
    item->Next->Prev=item->Prev;
    Current=item->Next;
    CurIndex++;
    node=item->Node;
    delete(item);
    Entries--;
    return(TRUE);
  }


  // If control reaches here we have to scan the whole thing
  item=Head->Next;  // Can start at node '1' because head was special cased
  for (int i=1; i<pos; i++) {
    item=item->Next;
    assert(item!=NULL);
  }

  item->Prev->Next=item->Next;
  item->Next->Prev=item->Prev;
  Current=item->Next;
  CurIndex=pos;
  node=item->Node;
  delete(item);
  Entries--;

  return(TRUE);  
}




// Remove at the zero based index specified by 'pos'.  When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 LinkedList<T>::remove(sint32 pos)
{
  T temp_node;
  return(remove(temp_node,pos));
}


// Remove the first node of the list
template <class T>
bit8 LinkedList<T>::removeHead(OUT T &node)     
{
  return(remove(node,0));
}


// Remove the last node of the list
template <class T>
bit8 LinkedList<T>::removeTail(OUT T &node)
{
  return(remove(node,Entries-1));
}   



template <class T>
bit8 LinkedList<T>::get(OUT T &node, sint32 pos)
{
  T *objptr;
  bool retval=getPointer(&objptr,pos);
  if (retval && objptr)
    node=*objptr;

  return(retval);
}


template <class T>
bit8 LinkedList<T>::getPointer(OUT T **node,sint32 pos)
{
  if ((node==0)||(Entries==0))
    return(FALSE);

  LNode<T> *item;

  if (pos<0)
  {
    //pos=0;
    return(FALSE);
  }
  if (pos>=Entries)
  {
    //pos=Entries-1;
    return(FALSE);
  }

  if (pos==0) {
    *node=&(Head->Node);
    return(TRUE);
  } else if (pos==Entries-1) {
    *node=&(Tail->Node);
    return(TRUE);
  }
  // If control reaches here, we know target is not an endpoint

  // Check for possible speedup, so we don't have to scan the list
  if (pos==CurIndex) {
    *node=&(Current->Node);
    return(TRUE);
  } else if (pos==CurIndex+1) {
    *node=&(Current->Next->Node);
    CurIndex++;
    Current=Current->Next;
    return(TRUE);
  } else if (pos==CurIndex-1) {
    *node=&(Current->Prev->Node);
    CurIndex--;
    Current=Current->Prev;
    return(TRUE);
  }

  // If control reaches here we have to scan the whole thing
  item=Head->Next;  // Can start at node '1' because head was special cased
  for (int i=1; i<pos; i++) {
    item=item->Next;
    assert(item!=NULL);
  }
  *node=&(item->Node);
  CurIndex=pos;
  Current=item;

  return(TRUE);  
}


// Remove the first node of the list
template <class T>
bit8 LinkedList<T>::getHead(OUT T &node)
{
  return(get(node,0));
}


// Remove the last node of the list
template <class T>
bit8 LinkedList<T>::getTail(OUT T &node)
{
  return(get(node,Entries-1));
}      


template <class T>
void LinkedList<T>::print(IN FILE *out)
{
  LNode<T> *temp;

  fprintf(out,"--------------------\n");
  fprintf(out,"Entries = %d\n",length());
  fprintf(out,"H = %8p   C = %8p (%d)   T = %8p\n",Head,Current,CurIndex,Tail); 

  temp=Head;
  while (temp) {
    fprintf(out,"  %8p<-((%8p))->%8p  \n",temp->Prev,temp,temp->Next);
    temp=temp->Next;
  }   

  fprintf(out,"--------------------\n");
}            

// Return the current length of the list
template <class T>
sint32 LinkedList<T>::length(void) {
  return(Entries);
}    

#endif