//+------------------------------------------------------------------+
//|                                               PatternLibrary.mqh |
//|                                  Copyright 2026, MetaQuotes Ltd. |
//|                          https://www.mql5.com/en/users/lynnchris |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.00"

#ifndef PART83_PATTERNLIBRARY_MQH
#define PART83_PATTERNLIBRARY_MQH


#include "PatternData.mqh"
#include "PatternStorage.mqh"

//+------------------------------------------------------------------+
//| Owns the in-memory sample collection and coordinates persistence |
//+------------------------------------------------------------------+
class CPatternLibrary
  {
private:
   SPatternSample    m_samples[];
   ulong             m_next_id;
   int               m_lock;
   ENUM_PATTERN_TYPE m_type;
   int               FindSampleIndex(ulong id);
   bool              LoadAllFromDisk();
public:
                     CPatternLibrary(ENUM_PATTERN_TYPE type);
                    ~CPatternLibrary();
   bool              IsReady() const { return(m_lock != INVALID_HANDLE); }
   bool              AddSample(const SPatternSample &sample);
   bool              RemoveSample(ulong id);
   bool              GetSample(ulong id, SPatternSample &out);
   int               GetSampleCount() const;
   void              GetAllSamples(SPatternSample &samples[]);
   void              Clear();
   ulong             GetNewID();
  };

//+------------------------------------------------------------------+
//| Initializes a pattern library and loads stored samples           |
//+------------------------------------------------------------------+
CPatternLibrary::CPatternLibrary(ENUM_PATTERN_TYPE type)
  {
//--- Initialize the collection before loading its disk representation
   m_type = type;
   m_next_id = 1;
   ArrayResize(m_samples, 0);
   m_lock = AcquirePatternLock(m_type);
   if(m_lock != INVALID_HANDLE)
      LoadAllFromDisk();
  }
//+------------------------------------------------------------------+
//| Completes pattern-library destruction                            |
//+------------------------------------------------------------------+
CPatternLibrary::~CPatternLibrary()
  {
   if(m_lock != INVALID_HANDLE)
      FileClose(m_lock);
  }

//+------------------------------------------------------------------+
//| Returns the number of samples held in memory                     |
//+------------------------------------------------------------------+
int CPatternLibrary::GetSampleCount() const
  {
   return(ArraySize(m_samples));
  }

//+------------------------------------------------------------------+
//| Returns and advances the next available sample ID                |
//+------------------------------------------------------------------+
ulong CPatternLibrary::GetNewID()
  {
   if(!IsReady() || m_next_id >= 2147483647)
      return(0);
   return(m_next_id++);
  }

//+------------------------------------------------------------------+
//| Load every stored sample for this pattern type                   |
//+------------------------------------------------------------------+
bool CPatternLibrary::LoadAllFromDisk()
  {
//--- Discover and sort IDs so the in-memory order is deterministic
   Clear();
   ulong ids[];
   GetSampleIDsFromFolder(m_type, ids);
   ArraySort(ids);
   for(int i = 0; i < ArraySize(ids); i++)
     {
      //--- Append valid files and advance the next available ID
      if(ids[i] >= m_next_id)
         m_next_id = ids[i] + 1;
      SPatternSample sample;
      if(LoadSampleFromFile(m_type, ids[i], sample))
        {
         int sz = ArraySize(m_samples);
         if(ArrayResize(m_samples, sz + 1) != sz + 1)
            return(false);
         m_samples[sz] = sample;
         if(ids[i] >= m_next_id)
            m_next_id = ids[i] + 1;
        }
      else
         Print("Failed to load sample ID: ", ids[i]);
     }
   return(true);
  }

//+------------------------------------------------------------------+
//| Add a unique sample to memory and persistent storage             |
//+------------------------------------------------------------------+
bool CPatternLibrary::AddSample(const SPatternSample &sample)
  {
//--- Reject duplicate IDs before changing the collection
   if(!IsReady() || sample.pattern_type != m_type || FindSampleIndex(sample.id) >= 0)
      return(false);
   int sz = ArraySize(m_samples);
   if(ArrayResize(m_samples, sz + 1) != sz + 1)
      return(false);
   m_samples[sz] = sample;
//--- Roll back the in-memory append if disk persistence fails
   if(!SaveSampleToFile(sample))
     {
      ArrayResize(m_samples, sz);
      return(false);
     }
   return(true);
  }

//+------------------------------------------------------------------+
//| Remove a sample from disk and compact the in-memory array        |
//+------------------------------------------------------------------+
bool CPatternLibrary::RemoveSample(ulong id)
  {
//--- Require both an existing ID and successful file deletion
   int idx = FindSampleIndex(id);
   if(!IsReady() || idx < 0 || !DeleteSampleFile(m_type, id))
      return(false);
   int sz = ArraySize(m_samples);
//--- Shift later elements left to close the removed array slot
   for(int i = idx; i < sz - 1; i++)
      m_samples[i] = m_samples[i + 1];
   ArrayResize(m_samples, sz - 1);
   return(true);
  }

//+------------------------------------------------------------------+
//| Copy one sample identified by ID                                 |
//+------------------------------------------------------------------+
bool CPatternLibrary::GetSample(ulong id, SPatternSample &out)
  {
   int idx = FindSampleIndex(id);
   if(idx < 0)
      return(false);
   out = m_samples[idx];
   return(true);
  }

//+------------------------------------------------------------------+
//| Copy the complete sample collection for a caller                 |
//+------------------------------------------------------------------+
void CPatternLibrary::GetAllSamples(SPatternSample &samples[])
  {
   ArrayResize(samples, ArraySize(m_samples));
   for(int i = 0; i < ArraySize(m_samples); i++)
      samples[i] = m_samples[i];
  }

//+------------------------------------------------------------------+
//| Clear the in-memory collection                                   |
//+------------------------------------------------------------------+
void CPatternLibrary::Clear()
  {
   ArrayResize(m_samples, 0);
  }

//+------------------------------------------------------------------+
//| Return the array index for an ID, or -1 when it is absent        |
//+------------------------------------------------------------------+
int CPatternLibrary::FindSampleIndex(ulong id)
  {
   for(int i = 0; i < ArraySize(m_samples); i++)
      if(m_samples[i].id == id)
         return(i);
   return(-1);
  }

#endif
//+------------------------------------------------------------------+