//------------------------------------------------------------ Feb 15 2003, 
// This is from the HypeView API https://hyperview.java.net/
// Jan 13 2013 removed destroy added finalize()
// May 5 2019 removed finalize for java 9 compliance
//   HyperLinkedList by Tony Swain http://www.facebook.com/tony.swain.77
//   A highly efficient Doubly Linked list
//
             //
             // Extending linkable allows the "system list"
             // To link this List with  the SystemListRegistry.class
             // SystemListRegistry.class allows you to get a snapshot
             // of every named list & their links. 
             // -> These are saved in the file nio_server.txt <-
             //
   //
   // All synching to be done outside the list context
   // for speed.   This avoids a synch for every element
   // When you add when adding multiple elements. 
   //

public class HyperLinkedList extends Linkable
{
Object lock;
int items;
int listFlags;
int listNumber;
static int totalLists;
volatile Linkable listBase;
volatile Linkable listTail;
volatile String listName;
SystemListNode systemListNode;
static int seed = 0;
        //-----
HyperLinkedList() 
{         
   lock = (Object)this;                           //  Default Constructor
   listName  = DEF_NAME;                          //<- Does NOT register itself with the List Register
   listFlags = LIST_CLEAR;                        //
}

HyperLinkedList(String tString)
{
   SystemListRegistry tRegistry;
   lock = (Object)this; 
   listFlags = LIST_CLEAR;
   listName = tString;
   listNumber = seed++;
   if(listName == null)                             // DOES register itself with the List Register
   {                                                //   |
      listName = "HyperLinkedList("+listNumber+')'; //   |
      name = listName;                              //   |
   }                                                //   |
   tRegistry   = SystemListRegistry.getRegistry();  //   |
   systemListNode = new SystemListNode(this);       //   |
   tRegistry.register(systemListNode);              //<--/        
   totalLists++;
}

HyperLinkedList(SystemListRegistry tRegistry)       // DOES register itself with the List Register
{
   lock = (Object)this;                             //   |
   listName = "SystemLists";                        //   |
   name = listName;                                 //   |
   listNumber = seed++;                       //   |
   systemListNode = new SystemListNode(this);       //   |
   tRegistry.register(systemListNode);              //<--/       
   //System.out.println("totalLists "+totalLists);
   totalLists++;
}

static synchronized long getNumber()
{
   return seed++;
}

synchronized void showLinks()
{
int count           = 0;
int tNumber         = 0;
Linkable tLinkable1 = listBase;
Linkable tLinkable2 = null;
   System.out.println("List \""+listName+' '+'\"'+" items = "+items+'\"');
   if(listBase == null)
   {
      //System.out.println(" EMPTY Base = "+listBase+" Tail = "+listTail);
      return; 
   }
   //System.out.println(" Base = "+listBase.name);
   if(listBase != null)          
   {                             
      System.out.println(" listBase = "+listBase.name);
   }
   else
   {
      System.out.println(" Tail = null");      
   }
   if(listTail != null)          
   {                             
      System.out.println(" Tail = "+listTail.name);
   }
   else
   {
      System.out.println(" Tail = null");      
   }
       //-- The End / endless loop detection.
   if((listBase == listTail) || (listBase.next == listBase))
   {
      tNumber++;
      System.out.println(" "+tLinkable1.name+" L# "+tNumber+" N# "+
       tLinkable1.index+" "+tNumber+" = Base * Tail "+listBase.name);
        //
      if(tLinkable1.prev != null)
      {
         System.out.println(" "+tLinkable1.name+".prev = "+tLinkable1.prev.name+" ");
      }
      else
      {
         System.out.println(" "+tLinkable1.name+".prev = NULL ");
      }
      if(tLinkable1.next != null)
      {
         System.out.println(" "+tLinkable1.name+".next = "
         +tLinkable1.next.name);
      }
      else
      {
         System.out.println(" "+tLinkable1.name+".next = NULL");
      }
   }
   else
   {
      while((tLinkable1 != null) && (count < items))
      {
         tNumber++;
        System.out.println("  <LINK "+tNumber+">");
        System.out.println(" Node "+tLinkable1.name+"  # "+tNumber);
        //  
         if(tLinkable1.prev != null)
         {
            System.out.println(" "+tLinkable1.name+".prev = "+tLinkable1.prev.name+" ");
         }
         else
         {
            System.out.println(" "+tLinkable1.name+".prev = NULL ");
         }
         if(tLinkable1.next != null)
         {
            System.out.println(" "+tLinkable1.name+".next = "
            +tLinkable1.next.name);
         }
         else
         {
            System.out.println(" "+tLinkable1.name+".next = NULL");
         }
         tLinkable2 = tLinkable1;
         tLinkable1 = null;
         tLinkable1 = tLinkable2.next;
         tLinkable2 = null;  
         System.out.println("  </LINK "+tNumber+">");
         count++;
      }
   }
   System.out.println("\r\n");
   notifyAll();
}

synchronized void stdShowLinks()
{
int count = 0;
int tNumber = 0;
Linkable tLinkable1 = listBase;
Linkable tLinkable2 = null;
   System.out.println("<HYPER_LINKED_LIST \""+listName+'\"'+'>');
   if(listBase == null)
   {
      System.out.println("<NAME> "+listName+" empty"+"</NAME>");
      //System.out.println(" <BASE>"+listBase+" </BASE>"); 
      //System.out.println(" <TAIL> "+listTail+" </TAIL>");
      return; 
   }
   System.out.println(" <BASE>"+listBase.name+" </BASE>");
   if(listTail != null)          
   {                             
      System.out.println(" <TAIL> "+listTail.name+" </TAIL>");
   }
   else
   {
      System.out.println(" <TAIL> NULL </TAIL>");      
   }
       //---------------------
   if((listBase == listTail) || (listBase.next == listBase))
   {
      tNumber++;
      System.out.println("   <LINK #"+tNumber+" == BASE & TAIL>");
      System.out.println("  -><- Node "+listBase.name+" -><-");
        //
      if(tLinkable1.prev != null)
      {
         System.out.println("      "+tLinkable1.name+".prev = "+tLinkable1.prev.name+" ");
      }
      else
      {
         System.out.println("      "+tLinkable1.name+".prev = NULL ");
      }
      if(tLinkable1.next != null)
      {
         System.out.println("      "+tLinkable1.name+".next = "+tLinkable1.next.name);
      }
      else
      {
         System.out.println("      "+tLinkable1.name+".next = NULL");
      }
      System.out.println("   </LINK "+tNumber+">");
   }
   else
   {
      while((tLinkable1 != null) && (count < items))
      {
         tNumber++;
        System.out.println("   <LINK "+tNumber+">");
        System.out.println("  Node -><- "+tLinkable1.name+" -><-");
        //
         if(tLinkable1.prev != null)
         {
            System.out.println("      "+tLinkable1.name+".prev = "+tLinkable1.prev.name+" ");
         }
         else
         {
            System.out.println("      "+tLinkable1.name+".prev = NULL ");
         }
         if(tLinkable1.next != null)
         {
            System.out.println("      "+tLinkable1.name+".next = "+tLinkable1.next.name);
         }
         else
         {
            System.out.println("      "+tLinkable1.name+".next = NULL");
         }
         tLinkable2 = tLinkable1;
         tLinkable1 = null;
         tLinkable1 = tLinkable2.next;
         tLinkable2 = null;  
         System.out.println("   </LINK "+tNumber+">");
         count++;
      }
   }
   System.out.println("</HYPER_LINKED_LIST \""+listName+'\"'+'>');
   notifyAll();
}

// Swap Linkable (A) for Linkable (B)

public void swap(Linkable removeLink,Linkable replaceLink)
{
Linkable prevLink,nextLink;
   prevLink = (Linkable)removeLink.prev;
   nextLink = (Linkable)removeLink.next;
   removeLink.next = removeLink.prev = (Linkable)null;
   if(prevLink != null)
   {
      prevLink.next    = (Linkable)replaceLink;
      replaceLink.prev = prevLink;
 

   }
   if(nextLink != null) 
   {
      nextLink.prev = (Linkable)replaceLink; 
   }
   replaceLink.next = nextLink;
   if(listBase == removeLink)
   {
      listBase = replaceLink;
   }
              //------------
   if(listTail == removeLink)
   {
      listTail = replaceLink;
   }
}

// Return a HyperLinkedList that encapsulates this Linkable()
//

static void showLinks(Linkable tLinkable)
{
int tNumber = 0;
Linkable tLinkable1 = tLinkable;
Linkable tLinkable2 = null;
char working = 0x01;
   if(tLinkable1 == null)
   {
      System.out.println("HyperLinkedList.showLinks(null Linkable());");
      //System.exit(0);
      return;
   }
   while(working != 0x00)
   {
      tNumber++;
       if(tLinkable.next == tLinkable) // race prevention on looped links 
          working = 0x00; 
      System.out.println(" "+tLinkable1.name+" L# "+tNumber+" N# "+tLinkable1.index);
        //
      if(tLinkable1.prev != null)
      {
         System.out.println(" "+tLinkable1.name+".prev = "+tLinkable1.prev.name+" ");
      }
      else
      {
         System.out.println(" "+tLinkable1.name+".prev = NULL ");
      }
      if(tLinkable1.next != null)
      {
         System.out.println(" "+tLinkable1.name+".next = "
         +tLinkable1.next.name);
      }
      else
      {
         System.out.println(" "+tLinkable1.name+".next = NULL");
      }
      tLinkable2 = tLinkable1;
      tLinkable1 = null;
      tLinkable1 = tLinkable2.next;
      tLinkable2 = null; 
         //Check base & tail are the saem
       if(tLinkable1 == null)
          working = 0x00; 
   }
  // System.out.println("</LINK "+tNumber+">");
}

public static void showLists()
{
  // OOps! I think I over-optimized ;)
  // Well at least this is an efficient function :)
   HyperLinkedList tSystemList = SystemListRegistry.getSystemList();
}


// Get a List from the SystemListRegistry

public static HyperLinkedList getList(String tName)
{
   return SystemListRegistry.getSystemList();
}

public static void saveLists()
{
HyperLinkedList tList = null;
SystemListNode tNode  = null;
SystemListRegistry tRegistry = SystemListRegistry.getRegistry();

   if(totalLists > 0)
   {
      if(tRegistry == null)
      {
         System.out.println("saveLists() SystemListRegistry() null system registry");
         return;
      }
      else
      {
         System.out.println("List Name: "+tRegistry.listName+" items "+tRegistry.items);
      }
      if(tRegistry.listBase != null)
      {
         tNode = (SystemListNode)tRegistry.listBase;
      }
      else
      {
         System.out.println("null tempnode in LinkedList`");
         return;
      }
      //System.out.println("tnode = "+tNode.toString());
      while(tNode != null)
      {
         if(tNode.parentList.items == 1)
         {
            System.out.println(
             "List -> "+tNode.parentList.listName+"  "+tNode.parentList.items+" link");
         }
         else
         {
            System.out.println(
             "HyperLinkedList("+tNode.parentList.listName+","+tNode.parentList.items+"); links");
         }
         tNode.parentList.showLinks();
         tNode = (SystemListNode)tNode.next;
      }
   }
}

void clear()
{
   
  //systemListRemove();      add this    //totalLists--; 
   listFlags         = HyperLinkedListConstants.LIST_CLEAR;
   listBase          = null;
   listTail          = null;
   items             = 0;
}

int rand(int maxVal)
{
double tdouble;
int val = 0;
   val = (int)((double)Math.random()*(double)maxVal);
   if(val < 0)
      val = 0;
   else
      if(!(val < maxVal))
         val = maxVal;
   return   val;
}

// Append a List to the end of another list

int dlStore(HyperLinkedList tList)
{
int tItems = tList.items;
   if(listBase == null)
   {
      listBase = tList.listBase;
      listTail = tList.listTail;
   }
   else
   {
      if(listTail == null)
      {
         if(tList.listTail == null)
         {
            listTail      = tList.listBase;
            listTail.prev = listBase; 
            listBase.next = listTail;
         }
         else
         {
            listTail = tList.listTail;
            tList.listBase.prev = listBase;
            listBase.next = tList.listBase;
         }
      }
      else
      {
         if(tList.listTail != null)
         {
            listTail.next       = tList.listBase; 
            tList.listBase.prev = listTail;                
            listTail            = tList.listTail;
         }
         else
         {
            listTail.next = tList.listBase;
            tList.listBase.prev = listTail;
            listTail = tList.listBase;
         }
      }
   }
   tList.listFlags         = LIST_CLEAR;
   tList.listBase          = null;
   tList.listTail          = null;
   tList.items             = 0;
   items += tItems;
   return items;
}

int dlStore(Linkable tLinkable)
{

/* Uncomment if you suspect list corruption 
   if(tLinkable.prev != null || tLinkable.next != null)
   {
      System.out.println("LST: dlstore error linkable already in list!");
      StackTrace.show();
      System.exit(0);
   }
*/

  // tLinkable.parentList = this;
   if(listBase == null)
   {
      listBase = tLinkable;
   }
   else
   {
      if(listTail == null)
      {
         listTail      = tLinkable;
         listTail.prev = listBase;
         listBase.next = listTail;
      } 
      else 
      {
         listTail.next = tLinkable;
         tLinkable.prev = listTail;
         listTail = tLinkable;
      }
   }
   items++;
   return items;
}

int dlDelete(Linkable tLinkable)
{
  // tLinkable.parentList = null;
   if(tLinkable == listBase)
   {
      if(listBase.next != null)
      {
         if(listBase.next == listTail)
         {
            listBase      = listTail;
            listTail.prev = null;
            listTail      = null;
         }
         else
         {
            listBase = listBase.next;
            listBase.prev = null;         
         }  
      }
      else
      {
         tLinkable.prev = tLinkable.next = null;
      }
   }
   else
   {
      if(tLinkable == listTail)
      {
         if(listTail.prev == listBase)
         {
            listTail.prev = null;
            listTail      = null;
            listBase.next = null;
         }
         else
         {
            listTail = listTail.prev;
            listTail.next = null;
         }
      }
      else
      {
         if(tLinkable.prev != null)
         {
            tLinkable.prev.next = tLinkable.next;
         }
         if(tLinkable.next != null)
         {
            tLinkable.next.prev = tLinkable.prev;
         }
      }
   }
   items--;
   if(items == 0)
   {
      listBase = listTail = null;
   }
   tLinkable.next = tLinkable.prev = null;
   return items;
}

//////////////////////////////////

synchronized void removeAll()
{
Linkable tLinkable1 = listBase;
Linkable tLinkable2 = null;
             //-------------
   while(tLinkable1 != null)
   {
      tLinkable1.prev  = null;
      tLinkable2       = tLinkable1.next;
      tLinkable1.next  = null;
      tLinkable1       = null;
      tLinkable1       = tLinkable2;
      tLinkable2       = null;
   }
   listBase = listTail = null;
}


public void synchEnable()
{
   listFlags |= SYNCHRONIZED;
}


void echoLinks(int x)
{
Linkable tLink = listBase;
   if(x <100) //?? Whyd I do that??? > 10 years ago I forget :)
      return;

   System.out.println(
"\r\n---------LIST_ECHO: ("+x+")"+listName+" items="+items);
   if(listBase != null)
   {
      System.out.println(" B="+listBase.name);
      if(listBase == listTail) // Ooops! its looped
      {
      System.out.println("ERROR listBase == listTail");
         int z=0;z=z/z; // Cause /0 exception this is lazy me :)
      }
   }
   else
   {
      System.out.println(" B=null");
   }
   if(listTail != null)
   {
      System.out.println(" T="+listTail.name);
   }
   else  
   {
      System.out.println(" T=null");
   } 
   int iterationCount = 0;
   while(tLink != null)
   {
      System.out.println("LINK = "+tLink.name);
      if(tLink.prev != null)
         System.out.print(iterationCount+"<- p="+tLink.prev.name);
      else
          System.out.print(" p=null,");
                   //------------
      if(tLink.next != null)
         System.out.println(" n="+tLink.next.name);
      else
          System.out.println(" n=null,");
      tLink = tLink.next;
      if(++iterationCount >25)   //Race prevention set to what you want as max
      {
         System.out.println("iteration overload");
         System.exit(1);
      }
   }
   System.out.println("\r\n---------");
}

// Detach all links from this link down.

public int detach(Linkable tLink)
{
Linkable sLink;
int tNum  = 0;
   if(tLink.prev != null)
   {
      tLink.prev.next = null;
      tLink.prev      = null;
      sLink           = (Linkable)tLink;
      while(sLink != null)
      {
         //sLink.number = tNum++;
         sLink = (Linkable)sLink.next;
      }
      items -= tNum;
      return tNum;
   }
   else
   {
      listBase = listTail = null;
      items = 0;
      return 0;
   }
}

//--- Drain every element and clear everything

public void drain()
{
Linkable tLinkable = listBase;
Linkable tLinkable2;
   while(tLinkable != null)
   {
      tLinkable2 = tLinkable.next;
      tLinkable.next = tLinkable.prev = null;
      tLinkable = tLinkable2;
      tLinkable2 = null;
   }
   items = 0;
   listBase = listTail = null;
}

public void destroy()
{

}

public void setListName(String tName) throws Exception
{
   if((listFlags & NAME_LOCKED)!=0)
   {
      throw new Exception("HyperLinkedList illegal name change");
   }
   name = tName;
}

/*
protected void finalize() throws Throwable 
{
   if((listFlags & SYSTEM_REGISTERED) !=0)
   {
      SystemListRegistry tRegistry = SystemListRegistry.getRegistry();
      tRegistry.unregister(systemListNode);  
   }
   throw new Throwable();
}
*/

}

