//+------------------------------------------------------------------+
//|                                                   LinkedList.mqh |
//|                             Copyright 2000-2026, MetaQuotes Ltd. |
//|                                                     www.mql5.com |
//+------------------------------------------------------------------+
#include <Generic\Interfaces\ICollection.mqh>
#include <Generic\Internal\EqualFunction.mqh>
//+------------------------------------------------------------------+
//| Class CLinkedListNode<T>.                                        |
//| Usage: Represents a node of linked list.                         |
//+------------------------------------------------------------------+
template<typename T>
class CLinkedListNode
  {
protected:
   CLinkedList<T>*m_list;
   CLinkedListNode<T>*m_next;
   CLinkedListNode<T>*m_prev;
   T                 m_item;

public:
                     CLinkedListNode(T value): m_item(value) { }
                     CLinkedListNode(CLinkedList<T>*list,T value): m_list(list),m_item(value) { }
                    ~CLinkedListNode(void) { }
   //--- methods of access to protected data
   CLinkedList<T>*   List(void)                         { return(m_list); }
   void              List(CLinkedList<T>*value)         { m_list=value;   }
   CLinkedListNode<T>*Next(void)                        { return(m_next); }
   void              Next(CLinkedListNode<T>*value)     { m_next=value;   }
   CLinkedListNode<T>*Previous(void)                    { return(m_prev); }
   void              Previous(CLinkedListNode<T>*value) { m_prev=value;   }
   T                 Value(void)                        { return(m_item); }
   void              Value(T value)                     { m_item=value;   }
  };
//+------------------------------------------------------------------+
//| Class CLinkedList<T>.                                            |
//| Usage: Represents a doubly linked list.                          |
//+------------------------------------------------------------------+
template<typename T>
class CLinkedList: public ICollection<T>
  {
protected:
   CLinkedListNode<T>*m_head;
   int               m_count;

public:
                     CLinkedList(void);
                     CLinkedList(ICollection<T>*collection);
                     CLinkedList(T &array[]);
                    ~CLinkedList(void);
   //--- methods of filling data 
   bool              Add(T value);
   CLinkedListNode<T>*AddAfter(CLinkedListNode<T>*node,T value);
   bool              AddAfter(CLinkedListNode<T>*node,CLinkedListNode<T>*new_node);
   CLinkedListNode<T>*AddBefore(CLinkedListNode<T>*node,T value);
   bool              AddBefore(CLinkedListNode<T>*node,CLinkedListNode<T>*new_node);
   CLinkedListNode<T>*AddFirst(T value);
   bool              AddFirst(CLinkedListNode<T>*node);
   CLinkedListNode<T>*AddLast(T value);
   bool              AddLast(CLinkedListNode<T>*node);
   //--- methods of access to protected data
   int               Count(void);
   CLinkedListNode<T>*Head(void) {return(m_head);}
   CLinkedListNode<T>*First(void);
   CLinkedListNode<T>*Last(void);
   bool              Contains(T item);
   //--- methods of copy data from collection   
   int               CopyTo(T &dst_array[],const int dst_start=0);
   //--- methods of cleaning and deleting
   void              Clear(void);
   bool              Remove(T item);
   bool              Remove(CLinkedListNode<T>*node);
   bool              RemoveFirst(void);
   bool              RemoveLast(void);
   //--- method for searching
   CLinkedListNode<T>*Find(T value);
   CLinkedListNode<T>*FindLast(T value);

private:
   bool              ValidateNode(CLinkedListNode<T>*node);
   bool              ValidateNewNode(CLinkedListNode<T>*node);
   void              InternalInsertNodeBefore(CLinkedListNode<T>*node,CLinkedListNode<T>*new_node);
   void              InternalInsertNodeToEmptyList(CLinkedListNode<T>*new_node);
   void              InternalRemoveNode(CLinkedListNode<T>*node);
  };
//+------------------------------------------------------------------+
//| Initializes a new instance of the CLinkedList<T> class that is   |
//| empty.                                                           |
//+------------------------------------------------------------------+
template<typename T>
CLinkedList::CLinkedList(void): m_count(0)
  {
  }
//+------------------------------------------------------------------+
//| Initializes a new instance of the CLinkedList<T> class that      |
//| contains elements copied from the specified array and has        |
//| sufficient capacity to accommodate the number of elements copied.|
//+------------------------------------------------------------------+
template<typename T>
CLinkedList::CLinkedList(T &array[]): m_count(0)
  {
   for(int i=0; i<ArraySize(array); i++)
      AddLast(array[i]);
  }
//+------------------------------------------------------------------+
//| Initializes a new instance of the CLinkedList<T> class that      |
//| contains elements copied from the specified collection and has   |
//| sufficient capacity to accommodate the number of elements copied.|
//+------------------------------------------------------------------+
template<typename T>
CLinkedList::CLinkedList(ICollection<T>*collection): m_count(0)
  {
//--- check collection
   if(CheckPointer(collection)!=POINTER_INVALID)
     {
      T array[];
      int size=collection.CopyTo(array,0);
      for(int i=0; i<size; i++)
         AddLast(array[i]);
     }
  }
//+------------------------------------------------------------------+
//| Destructor.                                                      |
//+------------------------------------------------------------------+
template<typename T>
CLinkedList::~CLinkedList(void)
  {
   Clear();
  }
//+------------------------------------------------------------------+
//| Adds an value to the end of the list.                            |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::Add(T value)
  {
   return(CheckPointer(AddLast(value))!=POINTER_INVALID);
  }
//+------------------------------------------------------------------+
//| Adds a new node containing the specified value after the         |
//| specified existing node in the CLinkedList<T>.                   |
//+------------------------------------------------------------------+
template<typename T>
CLinkedListNode<T>*CLinkedList::AddAfter(CLinkedListNode<T>*node,T value)
  {
//--- check node
   if(!ValidateNode(node))
      return(NULL);
//--- create new node
   CLinkedListNode<T>*result=new CLinkedListNode<T>(node.List(),value);
//--- insert node to the list
   InternalInsertNodeBefore(node.Next(),result);
   return(result);
  }
//+------------------------------------------------------------------+
//| Adds the specified new node after the specified existing node in |
//| the LinkedList<T>.                                               |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::AddAfter(CLinkedListNode<T>*node,CLinkedListNode<T>*new_node)
  {
//--- check node
   if(!ValidateNode(node))
      return(false);
//--- check new node
   if(!ValidateNewNode(new_node))
      return(false);
//--- insert node to the list
   InternalInsertNodeBefore(node.Next(),new_node);
//--- set the current list as list for new node 
   new_node.List(GetPointer(this));
   return(true);
  }
//+------------------------------------------------------------------+
//| Adds a new node containing the specified value before the        |
//| specified existing node in the CLinkedList<T>.                   |
//+------------------------------------------------------------------+
template<typename T>
CLinkedListNode<T>*CLinkedList::AddBefore(CLinkedListNode<T>*node,T value)
  {
//--- check node
   if(!ValidateNode(node))
      return(NULL);
//--- create new node
   CLinkedListNode<T>*result=new CLinkedListNode<T>(node.List(),value);
//--- insert node to the list
   InternalInsertNodeBefore(node,result);
   if(node==m_head)
      m_head=result;
   return(result);
  }
//+------------------------------------------------------------------+
//| Adds the specified new node before the specified existing node in|
//| the LinkedList<T>.                                               |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::AddBefore(CLinkedListNode<T>*node,CLinkedListNode<T>*new_node)
  {
//--- check node
   if(!ValidateNode(node))
      return(false);
//--- check new node
   if(!ValidateNewNode(new_node))
      return(false);
//--- insert node to the list
   InternalInsertNodeBefore(node,new_node);
//--- set the current list as list for new node 
   new_node.List(GetPointer(this));
   if(node==m_head)
      m_head=new_node;
   return(true);
  }
//+------------------------------------------------------------------+
//| Adds a new node containing the specified value at the start of   |
//| the CLinkedList<T>.                                              |
//+------------------------------------------------------------------+
template<typename T>
CLinkedListNode<T>*CLinkedList::AddFirst(T value)
  {
//--- create new node
   CLinkedListNode<T>*node=new CLinkedListNode<T>(GetPointer(this),value);
//--- check head node
   if(CheckPointer(m_head)==POINTER_INVALID)
     {
      //--- insert node to the empty list   
      InternalInsertNodeToEmptyList(node);
     }
   else
     {
      //--- insert node to the list    
      InternalInsertNodeBefore(m_head,node);
      m_head=node;
     }
   return(node);
  }
//+------------------------------------------------------------------+
//| Adds the specified new node at the start of the CLinkedList<T>.  |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::AddFirst(CLinkedListNode<T>*node)
  {
//--- check node
   if(!ValidateNewNode(node))
      return(false);
//--- check head node
   if(CheckPointer(m_head)==POINTER_INVALID)
     {
      //--- insert node to the empty list   
      InternalInsertNodeToEmptyList(node);
     }
   else
     {
      //--- insert node to the list    
      InternalInsertNodeBefore(m_head,node);
      m_head=node;
     }
//--- set the current list as list for node 
   node.List(GetPointer(this));
   return(true);
  }
//+------------------------------------------------------------------+
//| Adds a new node containing the specified value at the end of the |
//| CLinkedList<T>.                                                  |
//+------------------------------------------------------------------+
template<typename T>
CLinkedListNode<T>*CLinkedList::AddLast(T value)
  {
//--- create new node
   CLinkedListNode<T>*node=new CLinkedListNode<T>(GetPointer(this),value);
//--- check head node
   if(CheckPointer(m_head)==POINTER_INVALID)
     {
      //--- insert node to the empty list   
      InternalInsertNodeToEmptyList(node);
     }
   else
     {
      //--- insert node to the list    
      InternalInsertNodeBefore(m_head,node);
     }
   return(node);
  }
//+------------------------------------------------------------------+
//| Adds the specified new node at the end of the CLinkedList<T>.    |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::AddLast(CLinkedListNode<T>*node)
  {
//--- check node
   if(!ValidateNewNode(node))
      return(false);
//--- check head node
   if(CheckPointer(m_head)==POINTER_INVALID)
     {
      //--- insert node to the empty list      
      InternalInsertNodeToEmptyList(node);
     }
   else
     {
      //--- insert node to the list      
      InternalInsertNodeBefore(m_head,node);
     }
//--- set the current list as list for node 
   node.List(GetPointer(this));
   return(true);
  }
//+------------------------------------------------------------------+
//| Determines whether an element is in the linked list.             |
//+------------------------------------------------------------------+
template<typename T>
int CLinkedList::Count(void)
  {
   return(m_count);
  }
//+------------------------------------------------------------------+
//| Gets the first node of the CLinkedList<T>.                       |
//+------------------------------------------------------------------+
template<typename T>
CLinkedListNode<T>*CLinkedList::First(void)
  {
   return(m_head);
  }
//+------------------------------------------------------------------+
//| Gets the last node of the CLinkedList<T>.                        |
//+------------------------------------------------------------------+
template<typename T>
CLinkedListNode<T>*CLinkedList::Last(void)
  {
   return(CheckPointer(m_head)!=POINTER_INVALID ? m_head.Previous() : NULL);
  }
//+------------------------------------------------------------------+
//| Determines whether a value is in the CLinkedList<T>.             |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::Contains(T item)
  {
   return(CheckPointer(Find(item))!=POINTER_INVALID);
  }
//+------------------------------------------------------------------+
//| Copies a range of elements from the linkedlist to a compatible   |
//| one-dimensional array.                                           |
//+------------------------------------------------------------------+
template<typename T>
int CLinkedList::CopyTo(T &dst_array[],const int dst_start=0)
  {
//--- resize array
   if(dst_start+m_count>ArraySize(dst_array))
      ArrayResize(dst_array,dst_start+m_count);
//--- check start index
   if(dst_start>ArraySize(dst_array))
      return(0);
//--- start copy
   CLinkedListNode<T>*node=m_head;
   if(CheckPointer(node)!=POINTER_INVALID)
     {
      int dst_index=dst_start;
      do
        {
         dst_array[dst_index++]=node.Value();
         node=node.Next();
        }
      while(dst_index<ArraySize(dst_array) && node!=m_head);
      return(dst_index-dst_start);
     }
//--- list is empty
   return(0);
  }
//+------------------------------------------------------------------+
//| Removes all nodes from the CLinkedList<T>.                       |
//+------------------------------------------------------------------+
template<typename T>
void CLinkedList::Clear(void)
  {
//--- check count
   if(m_count>0)
     {
      //--- check head node   
      if(CheckPointer(m_head)!=POINTER_INVALID)
        {
         while(m_head.Next()!=m_head)
           {
            CLinkedListNode<T>*node=m_head.Next();
            m_head.Next(node.Next());
            delete node;
           }
         delete m_head;
        }
      //--- reset count
      m_count=0;
     }
  }
//+------------------------------------------------------------------+
//| Removes the first occurrence of the specified value from the     |
//| CLinkedList<T>.                                                  |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::Remove(T item)
  {
//--- find node with specified value
   CLinkedListNode<T>*node=Find(item);
   if(CheckPointer(node)!=POINTER_INVALID)
     {
      //--- remove node
      InternalRemoveNode(node);
      return(true);
     }
   return(false);
  }
//+------------------------------------------------------------------+
//| Removes the specified node from the LinkedList<T>.               |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::Remove(CLinkedListNode<T>*node)
  {
//--- check node
   if(ValidateNode(node))
     {
      //--- remove node
      InternalRemoveNode(node);
      return(true);
     }
   return(false);
  }
//+------------------------------------------------------------------+
//| Removes the node at the start of the CLinkedList<T>.             |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::RemoveFirst(void)
  {
//--- check head node
   if(CheckPointer(m_head)==POINTER_INVALID)
      return(false);
//--- remove head node
   InternalRemoveNode(m_head);
   return(true);
  }
//+------------------------------------------------------------------+
//| Removes the node at the end of the CLinkedList<T>.               |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::RemoveLast(void)
  {
//--- check head node
   if(CheckPointer(m_head)==POINTER_INVALID)
      return(false);
//--- remove last node
   InternalRemoveNode(m_head.Previous());
   return(true);
  }
//+------------------------------------------------------------------+
//| Finds the first node that contains the specified value.          |
//+------------------------------------------------------------------+
template<typename T>
CLinkedListNode<T>*CLinkedList::Find(T value)
  {
   CLinkedListNode<T>*node=m_head;
//--- start search specified value in the list
   if(CheckPointer(node)!=POINTER_INVALID)
     {
      do
        {
         //--- use default equals function
         if(::Equals(node.Value(),value))
            return(node);
         node=node.Next();
        }
      while(node!=m_head);
     }
   return(NULL);
  }
//+------------------------------------------------------------------+
//| Finds the last node that contains the specified value.           |
//+------------------------------------------------------------------+
template<typename T>
CLinkedListNode<T>*CLinkedList::FindLast(T value)
  {
//--- check head node
   if(CheckPointer(m_head)==POINTER_INVALID)
      return(NULL);
//--- get last node
   CLinkedListNode<T> *last = m_head.Previous();
   CLinkedListNode<T> *node = last;
//--- start search from the end of the list
   if(node!=NULL)
     {
      do
        {
         //--- use default equals function
         if(::Equals(node.Value(),value))
            return(node);
         node=node.Previous();
        }
      while(node!=last);
     }
   return(NULL);
  }
//+------------------------------------------------------------------+
//| Validation of node on not null and belongs in the current list.  |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::ValidateNode(CLinkedListNode<T>*node)
  {
   return(CheckPointer(node)!=POINTER_INVALID && node.List()==GetPointer(this));
  }
//+------------------------------------------------------------------+
//| Validation of new node on not null.                              |
//+------------------------------------------------------------------+
template<typename T>
bool CLinkedList::ValidateNewNode(CLinkedListNode<T>*node)
  {
   return(CheckPointer(node)!=POINTER_INVALID && node.List()==NULL);
  }
//+------------------------------------------------------------------+
//| Insert node before the specified node.                           |
//+------------------------------------------------------------------+
template<typename T>
void CLinkedList::InternalInsertNodeBefore(CLinkedListNode<T>*node,CLinkedListNode<T>*new_node)
  {
//--- set node befor the specified node
   new_node.Next(node);
   new_node.Previous(node.Previous());
   node.Previous().Next(new_node);
   node.Previous(new_node);
//--- increment count
   m_count++;
  }
//+------------------------------------------------------------------+
//| Add first node to the list.                                      |
//+------------------------------------------------------------------+
template<typename T>
void CLinkedList::InternalInsertNodeToEmptyList(CLinkedListNode<T>*new_node)
  {
//--- set node as head of the list
   new_node.Next(new_node);
   new_node.Previous(new_node);
   m_head=new_node;
//--- increment count
   m_count++;
  }
//+------------------------------------------------------------------+
//| Remove specified node from the list.                             |
//+------------------------------------------------------------------+
template<typename T>
void CLinkedList::InternalRemoveNode(CLinkedListNode<T>*node)
  {
//--- check node
   if(node.Next()==node)
     {
      //--- resets the head of the list
      m_head=NULL;
     }
   else
     {
      //--- detach node from the list
      node.Next().Previous(node.Previous());
      node.Previous().Next(node.Next());
      if(m_head==node)
         m_head=node.Next();
     }
//--- decrement count and delete node
   m_count--;
   delete node;
  }
//+------------------------------------------------------------------+
