﻿//+------------------------------------------------------------------+
//|                                              PersistentStore.mqh |
//|                 Lightweight Persistent Key-Value Storage Engine  |
//+------------------------------------------------------------------+
#ifndef PERSISTENT_STORE_MQH
#define PERSISTENT_STORE_MQH

//--- Includes
#include "PersistentValue.mqh"
#include "PersistentParser.mqh"
#include "PersistentSerializer.mqh"
#include "PersistentCache.mqh"

//+------------------------------------------------------------------+
//| CPersistentStore                                                 |
//| Unified interface for persistent key-value storage.              |
//| Manages the lifecycle of the backing flat file and the in-memory |
//| cache. Provides Get, Set, Delete, Exists, and typed accessors.   |
//+------------------------------------------------------------------+
class CPersistentStore
  {
private:
   CPersistentCache  m_cache;        // In-memory hash map cache
   string            m_file_name;    // Backing flat file name
   bool              m_initialized;  // True after successful Init()

   bool              LoadFile(void);
   bool              SaveFile(void);

public:
                     CPersistentStore(void);
                    ~CPersistentStore(void);

   bool              Init(const string file_name);
   void              Deinit(void);

   bool              Set(const string key, const string value);
   bool              SetInt(const string key, const int value);
   bool              SetLong(const string key, const long value);
   bool              SetDouble(const string key, const double value,
                               const int digits = 8);
   bool              SetBool(const string key, const bool value);
   bool              SetDatetime(const string key, const datetime value);

   bool              Get(const string key, string &out_value);
   bool              GetInt(const string key, int &out_value);
   bool              GetLong(const string key, long &out_value);
   bool              GetDouble(const string key, double &out_value);
   bool              GetBool(const string key, bool &out_value);
   bool              GetDatetime(const string key, datetime &out_value);

   bool              Delete(const string key);
   bool              Exists(const string key);
   int               Count(void) const;
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPersistentStore::CPersistentStore(void)
  {
   m_file_name   = "";
   m_initialized = false;
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPersistentStore::~CPersistentStore(void)
  {
  }

//+------------------------------------------------------------------+
//| Initialize the store with a backing file name                    |
//+------------------------------------------------------------------+
bool CPersistentStore::Init(const string file_name)
  {
   m_file_name   = file_name;
   m_initialized = false;

   if(StringLen(m_file_name) == 0)
     {
      Print("[PersistentStore] Init failed: empty file name");
      return(false);
     }

//--- Load existing entries from disk if the file exists
   if(FileIsExist(m_file_name))
     {
      if(!LoadFile())
        {
         PrintFormat("[PersistentStore] Init failed: could not load [%s]",
                     m_file_name);
         return(false);
        }
     }
   else
     {
      PrintFormat("[PersistentStore] New store: [%s] does not exist yet",
                  m_file_name);
     }

   m_initialized = true;
   PrintFormat("[PersistentStore] Initialized — file: %s | Entries: %d",
               m_file_name, m_cache.Count());
   return(true);
  }

//+------------------------------------------------------------------+
//| Release resources and clear the cache                            |
//+------------------------------------------------------------------+
void CPersistentStore::Deinit(void)
  {
   m_cache.Clear();
   m_initialized = false;
   Print("[PersistentStore] Deinitialized");
  }

//+------------------------------------------------------------------+
//| Load key-value pairs from the flat file into the cache           |
//+------------------------------------------------------------------+
bool CPersistentStore::LoadFile(void)
  {
   m_cache.Clear();

//--- Open file for reading in text mode
   int handle = FileOpen(m_file_name, FILE_READ | FILE_TXT | FILE_ANSI);
   if(handle == INVALID_HANDLE)
     {
      PrintFormat("[PersistentStore] LoadFile: cannot open [%s] error=%d",
                  m_file_name, GetLastError());
      return(false);
     }

   int loaded = 0;

//--- Read each line until end of file
   while(!FileIsEnding(handle))
     {
      string line = FileReadString(handle);

      string key, value;
      if(!CPersistentParser::ParseLine(line, key, value))
         continue; // Skip invalid lines silently

      m_cache.Set(key, value);
      loaded++;
     }

   FileClose(handle);
   PrintFormat("[PersistentStore] Loaded %d entries from [%s]",
               loaded, m_file_name);
   return(true);
  }

//+------------------------------------------------------------------+
//| Rewrite the flat file from the current cache contents            |
//+------------------------------------------------------------------+
bool CPersistentStore::SaveFile(void)
  {
   if(!m_initialized)
      return(false);

//--- Retrieve all cache entries as parallel arrays
   string keys[];
   string values[];
   m_cache.GetAllPairs(keys, values);
   int count = m_cache.Count();

//--- Open file for writing, truncating any existing content
   int handle = FileOpen(m_file_name,
                         FILE_WRITE | FILE_TXT | FILE_ANSI);
   if(handle == INVALID_HANDLE)
     {
      PrintFormat("[PersistentStore] SaveFile: cannot open [%s] error=%d",
                  m_file_name, GetLastError());
      return(false);
     }

//--- Write all entries
   bool ok = CPersistentSerializer::WriteLines(handle, keys, values, count);

   FileClose(handle);

   if(ok)
      PrintFormat("[PersistentStore] Saved %d entries to [%s]",
                  count, m_file_name);

   return(ok);
  }

//+------------------------------------------------------------------+
//| Store a string value; update cache and rewrite file              |
//+------------------------------------------------------------------+
bool CPersistentStore::Set(const string key, const string value)
  {
   if(!m_initialized)
      return(false);

   if(!m_cache.Set(key, value))
      return(false);

   PrintFormat("[PersistentStore] Set [%s] = %s", key, value);
   return(SaveFile());
  }

//+------------------------------------------------------------------+
//| Store an integer value                                           |
//+------------------------------------------------------------------+
bool CPersistentStore::SetInt(const string key, const int value)
  {
   return(Set(key, CPersistentSerializer::SerializeInt(value)));
  }

//+------------------------------------------------------------------+
//| Store a long integer value                                       |
//+------------------------------------------------------------------+
bool CPersistentStore::SetLong(const string key, const long value)
  {
   return(Set(key, CPersistentSerializer::SerializeLong(value)));
  }

//+------------------------------------------------------------------+
//| Store a double value                                             |
//+------------------------------------------------------------------+
bool CPersistentStore::SetDouble(const string key, const double value,
                                 const int digits = 8)
  {
   return(Set(key, CPersistentSerializer::SerializeDouble(value, digits)));
  }

//+------------------------------------------------------------------+
//| Store a boolean value                                            |
//+------------------------------------------------------------------+
bool CPersistentStore::SetBool(const string key, const bool value)
  {
   return(Set(key, CPersistentSerializer::SerializeBool(value)));
  }

//+------------------------------------------------------------------+
//| Store a datetime value                                           |
//+------------------------------------------------------------------+
bool CPersistentStore::SetDatetime(const string key, const datetime value)
  {
   return(Set(key, CPersistentSerializer::SerializeDatetime(value)));
  }

//+------------------------------------------------------------------+
//| Retrieve a raw string value by key                               |
//+------------------------------------------------------------------+
bool CPersistentStore::Get(const string key, string &out_value)
  {
   if(!m_initialized)
      return(false);

   bool found = m_cache.Get(key, out_value);
   if(found)
      PrintFormat("[PersistentStore] Get [%s] = %s", key, out_value);
   else
      PrintFormat("[PersistentStore] Get [%s] — key not found", key);

   return(found);
  }

//+------------------------------------------------------------------+
//| Retrieve an integer value by key                                 |
//+------------------------------------------------------------------+
bool CPersistentStore::GetInt(const string key, int &out_value)
  {
   string raw;
   if(!Get(key, raw))
      return(false);

   CPersistentValue pv;
   pv.FromString(raw);
   out_value = pv.AsInt();
   return(true);
  }

//+------------------------------------------------------------------+
//| Retrieve a long integer value by key                             |
//+------------------------------------------------------------------+
bool CPersistentStore::GetLong(const string key, long &out_value)
  {
   string raw;
   if(!Get(key, raw))
      return(false);

   CPersistentValue pv;
   pv.FromString(raw);
   out_value = pv.AsLong();
   return(true);
  }

//+------------------------------------------------------------------+
//| Retrieve a double value by key                                   |
//+------------------------------------------------------------------+
bool CPersistentStore::GetDouble(const string key, double &out_value)
  {
   string raw;
   if(!Get(key, raw))
      return(false);

   CPersistentValue pv;
   pv.FromString(raw);
   out_value = pv.AsDouble();
   return(true);
  }

//+------------------------------------------------------------------+
//| Retrieve a boolean value by key                                  |
//+------------------------------------------------------------------+
bool CPersistentStore::GetBool(const string key, bool &out_value)
  {
   string raw;
   if(!Get(key, raw))
      return(false);

   CPersistentValue pv;
   pv.FromString(raw);
   out_value = pv.AsBool();
   return(true);
  }

//+------------------------------------------------------------------+
//| Retrieve a datetime value by key                                 |
//+------------------------------------------------------------------+
bool CPersistentStore::GetDatetime(const string key, datetime &out_value)
  {
   string raw;
   if(!Get(key, raw))
      return(false);

   CPersistentValue pv;
   pv.FromString(raw);
   out_value = pv.AsDatetime();
   return(true);
  }

//+------------------------------------------------------------------+
//| Remove a key-value pair; update cache and rewrite file           |
//+------------------------------------------------------------------+
bool CPersistentStore::Delete(const string key)
  {
   if(!m_initialized)
      return(false);

   if(!m_cache.Delete(key))
     {
      PrintFormat("[PersistentStore] Delete [%s] — key not found", key);
      return(false);
     }

   PrintFormat("[PersistentStore] Deleted [%s]", key);
   return(SaveFile());
  }

//+------------------------------------------------------------------+
//| Return true if the key exists in the store                       |
//+------------------------------------------------------------------+
bool CPersistentStore::Exists(const string key)
  {
   if(!m_initialized)
      return(false);

   return(m_cache.Exists(key));
  }

//+------------------------------------------------------------------+
//| Return the number of entries currently in the store              |
//+------------------------------------------------------------------+
int CPersistentStore::Count(void) const
  {
   return(m_cache.Count());
  }

#endif // PERSISTENT_STORE_MQH
//+------------------------------------------------------------------+