//+------------------------------------------------------------------+
//|                                            DealHistoryReader.mqh |
//+------------------------------------------------------------------+
#ifndef DEALHISTORYREADER_MQH
#define DEALHISTORYREADER_MQH

#include "SessionEnums.mqh"

//+------------------------------------------------------------------+
//| CDealHistoryReader                                               |
//| Reads closed deal history for a date range and populates an      |
//| array of CDealRecord entries, skipping balance and deposit       |
//| deals and recovering each deal's open time from its position.    |
//+------------------------------------------------------------------+
class CDealHistoryReader
  {
private:
   datetime              FindOpenTime(ulong position_id, datetime close_time_utc) const;

public:
                     CDealHistoryReader(void);
                    ~CDealHistoryReader(void);

   int                   Read(datetime from, datetime to, CDealRecord &records[]);
  };

//+------------------------------------------------------------------+
//| Constructor: no member state to initialize.                      |
//+------------------------------------------------------------------+
CDealHistoryReader::CDealHistoryReader(void)
  {
  }

//+------------------------------------------------------------------+
//| Destructor: no dynamic resources to release.                     |
//+------------------------------------------------------------------+
CDealHistoryReader::~CDealHistoryReader(void)
  {
  }

//+------------------------------------------------------------------+
//| Read                                                             |
//| Selects history for the given date range, iterates every deal,   |
//| keeps only DEAL_ENTRY_OUT and DEAL_ENTRY_INOUT deals, and        |
//| populates records with the fields needed downstream. Returns     |
//| the number of records populated.                                 |
//+------------------------------------------------------------------+
int CDealHistoryReader::Read(datetime from, datetime to, CDealRecord &records[])
  {
//--- scope the terminal's history cache to the requested range
   if(!::HistorySelect(from, to))
     {
      ::Print("SessionDashboard: HistorySelect failed, error ", ::GetLastError());
      return(0);
     }
//--- resize the output array to the maximum possible size
   int total = ::HistoryDealsTotal();
   ::ArrayResize(records, total);
   int      populated       = 0;
   ulong    ticket          = 0;
   long     entry_type      = 0;
   double   profit          = 0.0;
   string   symbol          = "";
   datetime close_time_utc  = 0;
   datetime open_time       = 0;
   ulong    position_id     = 0;
//--- iterate every deal in the selected history range
   for(int i = 0; i < total; i++)
     {
      ticket = ::HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;
      //--- read the entry type and skip anything that is not a closing deal
      entry_type = ::HistoryDealGetInteger(ticket, DEAL_ENTRY);
      if(entry_type != DEAL_ENTRY_OUT && entry_type != DEAL_ENTRY_INOUT)
         continue;
      //--- read the fields needed for this deal record
      profit          = ::HistoryDealGetDouble(ticket, DEAL_PROFIT);
      symbol          = ::HistoryDealGetString(ticket, DEAL_SYMBOL);
      close_time_utc  = (datetime)::HistoryDealGetInteger(ticket, DEAL_TIME);
      position_id     = (ulong)::HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
      //--- recover the open time from the earliest deal on this position
      open_time       = FindOpenTime(position_id, close_time_utc);
      //--- populate the record and advance the output count
      records[populated].ticket       = ticket;
      records[populated].open_time    = open_time;
      records[populated].close_time   = close_time_utc;
      records[populated].profit       = profit;
      records[populated].symbol       = symbol;
      records[populated].session_type = SESSION_UNKNOWN;
      populated++;
     }
//--- shrink the array down to the number of records actually populated
   ::ArrayResize(records, populated);
   return(populated);
  }

//+------------------------------------------------------------------+
//| FindOpenTime                                                     |
//| Searches the currently selected history for the earliest deal    |
//| sharing the given position id and returns its time. Falls back   |
//| to close_time_utc if no earlier deal on the position is found.   |
//+------------------------------------------------------------------+
datetime CDealHistoryReader::FindOpenTime(ulong position_id, datetime close_time_utc) const
  {
   int      total        = ::HistoryDealsTotal();
   datetime earliest     = close_time_utc;
   bool     found        = false;
   ulong    ticket       = 0;
   ulong    scanned_id   = 0;
   datetime scanned_time = 0;
//--- scan every deal for one that shares the target position id
   for(int i = 0; i < total; i++)
     {
      ticket     = ::HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;
      scanned_id = (ulong)::HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
      if(scanned_id != position_id)
         continue;
      scanned_time = (datetime)::HistoryDealGetInteger(ticket, DEAL_TIME);
      //--- keep the earliest matching time seen so far
      if(!found || scanned_time < earliest)
        {
         earliest = scanned_time;
         found    = true;
        }
     }
   return(earliest);
  }

#endif // DEALHISTORYREADER_MQH
//+------------------------------------------------------------------+