//+------------------------------------------------------------------+
//|                                               IndicatorCache.mqh |
//| CIndicatorCache: lazy-loading indicator handle manager.          |
//| Composite key lookup, ref-counted acquisition and release,       |
//| factory-pattern handle creation, and deterministic FlushAll().   |
//+------------------------------------------------------------------+
#ifndef INDICATORCACHE_MQH
#define INDICATORCACHE_MQH

#include "IndicatorEntry.mqh"
#include "CacheStatistics.mqh"

#define CACHE_MEMORY_PER_HANDLE_KB 48.0

//+------------------------------------------------------------------+
//| CIndicatorCache                                                  |
//| Purpose: Lazy-loading resource manager coordinating re-usable    |
//|          technical indicator handles to prevent duplicate memory |
//+------------------------------------------------------------------+
class CIndicatorCache
  {
private:
   SCacheEntry          m_entries[];       // Flat array of cache entries
   int                  m_count;           // Current active entry count
   int                  m_max_capacity;    // Maximum allowed entries
   bool                 m_logging_enabled; // Controls journal output
   CCacheStatistics     m_stats;           // Embedded statistics tracker

   string               BuildKey(string symbol, ENUM_TIMEFRAMES tf,
                                 ENUM_INDICATOR_TYPE ind_type, string params);
   int                  FindEntry(string key) const;
   int                  CreateHandle(string symbol, ENUM_TIMEFRAMES tf,
                                     ENUM_INDICATOR_TYPE ind_type, string params);
   void                 LogMessage(string message);
   void                 RemoveEntry(int index);

public:
                        CIndicatorCache(int max_capacity, bool enable_logging);
                       ~CIndicatorCache(void);

   int                  AcquireHandle(string symbol, ENUM_TIMEFRAMES tf,
                                      ENUM_INDICATOR_TYPE ind_type, string params);
   void                 ReleaseHandle(string symbol, ENUM_TIMEFRAMES tf,
                                      ENUM_INDICATOR_TYPE ind_type, string params);
   void                 FlushAll(void);

   int                  GetActiveCount(void)  const;
   CCacheStatistics    *GetStatistics(void);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CIndicatorCache::CIndicatorCache(int max_capacity, bool enable_logging)
   :  m_count(0),
      m_max_capacity(max_capacity),
      m_logging_enabled(enable_logging)
  {
   ArrayResize(m_entries, 0);
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CIndicatorCache::~CIndicatorCache(void)
  {
   FlushAll();
  }

//+------------------------------------------------------------------+
//| BuildKey                                                         |
//| Purpose: Formulates a unique composite identity reference string |
//+------------------------------------------------------------------+
string CIndicatorCache::BuildKey(string symbol, ENUM_TIMEFRAMES tf,
                                 ENUM_INDICATOR_TYPE ind_type, string params)
  {
   return(symbol + "_" +
          EnumToString(tf) + "_" +
          IntegerToString((int)ind_type) + "_" +
          params);
  }

//+------------------------------------------------------------------+
//| FindEntry                                                        |
//| Purpose: Scans structural indexes for matched primary key pairs  |
//+------------------------------------------------------------------+
int CIndicatorCache::FindEntry(string key) const
  {
   for(int i = 0; i < m_count; i++)
     {
      if(m_entries[i].m_key == key)
        {
         return(i);
        }
     }
   return(-1);
  }

//+------------------------------------------------------------------+
//| CreateHandle                                                     |
//| Purpose: Instantiates terminal objects using factory mechanics   |
//+------------------------------------------------------------------+
int CIndicatorCache::CreateHandle(string symbol, ENUM_TIMEFRAMES tf,
                                  ENUM_INDICATOR_TYPE ind_type, string params)
  {
   int handle = INVALID_HANDLE;

   switch((int)ind_type)
     {
      case INDT_ATR:
        {
         int period = (int)StringToInteger(params);
         handle = iATR(symbol, tf, period);
         break;
        }
      case INDT_MA:
        {
         string parts[];
         int    part_count = StringSplit(params, '_', parts);
         if(part_count >= 4)
           {
            int period  = (int)StringToInteger(parts[0]);
            int shift   = (int)StringToInteger(parts[1]);
            int method  = (int)StringToInteger(parts[2]);
            int applied = (int)StringToInteger(parts[3]);
            handle = iMA(symbol, tf, period, shift,
                         (ENUM_MA_METHOD)method,
                         (ENUM_APPLIED_PRICE)applied);
           }
         break;
        }
      case INDT_RSI:
        {
         string parts[];
         int    part_count = StringSplit(params, '_', parts);
         if(part_count >= 2)
           {
            int period  = (int)StringToInteger(parts[0]);
            int applied = (int)StringToInteger(parts[1]);
            handle = iRSI(symbol, tf, period, (ENUM_APPLIED_PRICE)applied);
           }
         break;
        }
      case INDT_MACD:
        {
         string parts[];
         int    part_count = StringSplit(params, '_', parts);
         if(part_count >= 4)
           {
            int fast_period   = (int)StringToInteger(parts[0]);
            int slow_period   = (int)StringToInteger(parts[1]);
            int signal_period = (int)StringToInteger(parts[2]);
            int applied       = (int)StringToInteger(parts[3]);
            handle = iMACD(symbol, tf, fast_period, slow_period,
                           signal_period, (ENUM_APPLIED_PRICE)applied);
           }
         break;
        }
      case INDT_BBANDS:
        {
         string parts[];
         int    part_count = StringSplit(params, '_', parts);
         if(part_count >= 4)
           {
            int    period    = (int)StringToInteger(parts[0]);
            int    shift     = (int)StringToInteger(parts[1]);
            double deviation = StringToDouble(parts[2]);
            int    applied   = (int)StringToInteger(parts[3]);
            handle = iBands(symbol, tf, period, shift,
                            deviation, (ENUM_APPLIED_PRICE)applied);
           }
         break;
        }
      case INDT_STOCH:
        {
         string parts[];
         int    part_count = StringSplit(params, '_', parts);
         if(part_count >= 4)
           {
            int k_period = (int)StringToInteger(parts[0]);
            int d_period = (int)StringToInteger(parts[1]);
            int slowing  = (int)StringToInteger(parts[2]);
            int method   = (int)StringToInteger(parts[3]);
            handle = iStochastic(symbol, tf, k_period, d_period,
                                 slowing, (ENUM_MA_METHOD)method,
                                 STO_LOWHIGH);
           }
         break;
        }
      case INDT_CCI:
        {
         string parts[];
         int    part_count = StringSplit(params, '_', parts);
         if(part_count >= 2)
           {
            int period  = (int)StringToInteger(parts[0]);
            int applied = (int)StringToInteger(parts[1]);
            handle = iCCI(symbol, tf, period, (ENUM_APPLIED_PRICE)applied);
           }
         break;
        }
      case INDT_ADX:
        {
         int period = (int)StringToInteger(params);
         handle = iADX(symbol, tf, period);
         break;
        }
      case INDT_ICHIMOKU:
        {
         string parts[];
         int    part_count = StringSplit(params, '_', parts);
         if(part_count >= 3)
           {
            int tenkan  = (int)StringToInteger(parts[0]);
            int kijun   = (int)StringToInteger(parts[1]);
            int senkou  = (int)StringToInteger(parts[2]);
            handle = iIchimoku(symbol, tf, tenkan, kijun, senkou);
           }
         break;
        }
      case INDT_DEMA:
        {
         string parts[];
         int    part_count = StringSplit(params, '_', parts);
         if(part_count >= 2)
           {
            int period  = (int)StringToInteger(parts[0]);
            int applied = (int)StringToInteger(parts[1]);
            handle = iDEMA(symbol, tf, period, 0,
                           (ENUM_APPLIED_PRICE)applied);
           }
         break;
        }
      default:
         break;
     }

   return(handle);
  }

//+------------------------------------------------------------------+
//| LogMessage                                                       |
//+------------------------------------------------------------------+
void CIndicatorCache::LogMessage(string message)
  {
   if(m_logging_enabled)
     {
      Print(message);
     }
  }

//+------------------------------------------------------------------+
//| RemoveEntry                                                      |
//| Purpose: Performs dynamic reallocation to collapse index holes   |
//+------------------------------------------------------------------+
void CIndicatorCache::RemoveEntry(int index)
  {
   if(index < 0 || index >= m_count)
     {
      return;
     }

   for(int i = index; i < m_count - 1; i++)
     {
      m_entries[i] = m_entries[i + 1];
     }

   m_count--;
   ArrayResize(m_entries, m_count);
  }

//+------------------------------------------------------------------+
//| AcquireHandle                                                    |
//| Purpose: Requests handle reference; constructs new if uncached   |
//+------------------------------------------------------------------+
int CIndicatorCache::AcquireHandle(string symbol, ENUM_TIMEFRAMES tf,
                                   ENUM_INDICATOR_TYPE ind_type, string params)
  {
   string key   = BuildKey(symbol, tf, ind_type, params);
   int    index = FindEntry(key);

   if(index >= 0)
     {
      m_entries[index].m_ref_count++;
      m_stats.RecordHit();
      LogMessage("[HIT] " + key + " refs=" + IntegerToString(m_entries[index].m_ref_count));
      return(m_entries[index].m_handle);
     }

   //--- Cache miss validation checks
   if(m_count >= m_max_capacity)
     {
      LogMessage("[CACHE] Capacity limit reached (" + IntegerToString(m_max_capacity) + "). Cannot create: " + key);
      return(INVALID_HANDLE);
     }

   int handle = CreateHandle(symbol, tf, ind_type, params);
   if(handle == INVALID_HANDLE)
     {
      LogMessage("[ERROR] Handle creation failed for: " + key);
      return(INVALID_HANDLE);
     }

   //--- Commit new tracking record
   ArrayResize(m_entries, m_count + 1);
   m_entries[m_count].m_key            = key;
   m_entries[m_count].m_handle         = handle;
   m_entries[m_count].m_ref_count      = 1;
   m_entries[m_count].m_symbol         = symbol;
   m_entries[m_count].m_timeframe      = tf;
   m_entries[m_count].m_indicator_type = ind_type;
   m_entries[m_count].m_params         = params;
   m_entries[m_count].m_memory_est_kb  = CACHE_MEMORY_PER_HANDLE_KB;
   m_count++;

   m_stats.RecordMiss(CACHE_MEMORY_PER_HANDLE_KB);
   LogMessage("[CREATE] " + key);

   return(handle);
  }

//+-------------------------------------------------------------------------+
//| ReleaseHandle                                                           |
//| Purpose: Decrements tracking counters, releasing memory if unreferenced |
//+-------------------------------------------------------------------------+
void CIndicatorCache::ReleaseHandle(string symbol, ENUM_TIMEFRAMES tf,
                                    ENUM_INDICATOR_TYPE ind_type, string params)
  {
   string key   = BuildKey(symbol, tf, ind_type, params);
   int    index = FindEntry(key);

   if(index < 0)
     {
      LogMessage("[WARN] ReleaseHandle called for unknown key: " + key);
      return;
     }

   m_entries[index].m_ref_count--;
   LogMessage("[RELEASE] " + key + " refs=" + IntegerToString(m_entries[index].m_ref_count));

   if(m_entries[index].m_ref_count <= 0)
     {
      IndicatorRelease(m_entries[index].m_handle);
      m_stats.RecordRelease(m_entries[index].m_memory_est_kb);
      LogMessage("[DESTROY] " + key);
      RemoveEntry(index);
     }
  }

//+------------------------------------------------------------------+
//| FlushAll                                                         |
//| Purpose: Forces release cleanup across entire retention array    |
//+------------------------------------------------------------------+
void CIndicatorCache::FlushAll(void)
  {
   for(int i = m_count - 1; i >= 0; i--)
     {
      IndicatorRelease(m_entries[i].m_handle);
      LogMessage("[FLUSH] " + m_entries[i].m_key + " refs=" + IntegerToString(m_entries[i].m_ref_count));
     }

   ArrayResize(m_entries, 0);
   m_count = 0;
   LogMessage("[FLUSH] Cache cleared. All handles released.");
  }

//+------------------------------------------------------------------+
//| GetActiveCount                                                   |
//+------------------------------------------------------------------+
int CIndicatorCache::GetActiveCount(void) const
  {
   return(m_count);
  }

//+------------------------------------------------------------------+
//| GetStatistics                                                    |
//+------------------------------------------------------------------+
CCacheStatistics* CIndicatorCache::GetStatistics(void)
  {
   return(&m_stats);
  }

#endif // INDICATORCACHE_MQH
//+------------------------------------------------------------------+