//+------------------------------------------------------------------+
//|                                                  TradeLoader.mqh |
//+------------------------------------------------------------------+

#ifndef TRADELOADER_MQH
#define TRADELOADER_MQH

#include "TradeRecord.mqh"

//+------------------------------------------------------------------+
//| Reads closed trade history and fills an array of CTradeRecord.   |
//+------------------------------------------------------------------+
class CTradeLoader
  {
private:
   string            m_symbol;  // symbol filter; empty string = all symbols
   long              m_magic;   // magic filter; 0 = all magic numbers

   double            FetchSL(ulong deal_ticket, ulong order_ticket);
   double            FetchTP(ulong deal_ticket, ulong order_ticket);

public:
                     CTradeLoader(void);
                    ~CTradeLoader(void);

   int               Load(datetime from, datetime to,
                          const string &symbol, long magic,
                          CTradeRecord &out[]);
  };

//+------------------------------------------------------------------+
//| Constructor — initializes filter fields to neutral values.       |
//+------------------------------------------------------------------+
CTradeLoader::CTradeLoader(void)
  {
   m_symbol = ""; // empty = no symbol filter
   m_magic  = 0;  // zero = no magic filter
  }

//+------------------------------------------------------------------+
//| Destructor — no heap resources to release.                       |
//+------------------------------------------------------------------+
CTradeLoader::~CTradeLoader(void)
  {
//--- CTradeLoader owns no dynamically allocated objects
  }

//+------------------------------------------------------------------+
//| Returns the SL for a deal, falling back to the order record.     |
//+------------------------------------------------------------------+
double CTradeLoader::FetchSL(ulong deal_ticket, ulong order_ticket)
  {
   double sl = ::HistoryDealGetDouble(deal_ticket, DEAL_SL);
   if(sl == 0.0 && order_ticket > 0)
      sl = ::HistoryOrderGetDouble(order_ticket, ORDER_SL); // second pass via order
   return(sl);
  }

//+------------------------------------------------------------------+
//| Returns the TP for a deal, falling back to the order record.     |
//+------------------------------------------------------------------+
double CTradeLoader::FetchTP(ulong deal_ticket, ulong order_ticket)
  {
   double tp = ::HistoryDealGetDouble(deal_ticket, DEAL_TP);
   if(tp == 0.0 && order_ticket > 0)
      tp = ::HistoryOrderGetDouble(order_ticket, ORDER_TP); // second pass via order
   return(tp);
  }

//+------------------------------------------------------------------+
//| Selects history and fills out[] with reconstructed trade records.|
//+------------------------------------------------------------------+
int CTradeLoader::Load(datetime from, datetime to,
                       const string &symbol, long magic,
                       CTradeRecord &out[])
  {
   m_symbol = symbol;
   m_magic  = magic;
   ::ArrayResize(out, 0); // start with an empty output array

   if(!::HistorySelect(from, to))
     {
      ::Print("CTradeLoader::Load: HistorySelect failed");
      return(0);
     }

   int total_deals = (int)::HistoryDealsTotal();

//--- first pass: collect all entry deals, keyed by position ID
   ulong entry_tickets[];
   ulong position_ids[];
   int   entry_count = 0;

   ::ArrayResize(entry_tickets, total_deals);
   ::ArrayResize(position_ids,  total_deals);

   for(int i = 0; i < total_deals; i++)
     {
      ulong ticket = ::HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;

      //--- apply symbol filter
      if(m_symbol != "" &&
         ::HistoryDealGetString(ticket, DEAL_SYMBOL) != m_symbol)
         continue;

      //--- apply magic filter
      if(m_magic != 0 &&
         ::HistoryDealGetInteger(ticket, DEAL_MAGIC) != m_magic)
         continue;

      ENUM_DEAL_ENTRY entry_type =
         (ENUM_DEAL_ENTRY)::HistoryDealGetInteger(ticket, DEAL_ENTRY);

      if(entry_type == DEAL_ENTRY_IN)
        {
         entry_tickets[entry_count] = ticket;
         position_ids[entry_count]  =
            ::HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
         entry_count++;
        }
     }

//--- second pass: match exit deals to entry deals by position ID
   for(int i = 0; i < total_deals; i++)
     {
      ulong ticket = ::HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;

      ENUM_DEAL_ENTRY entry_type =
         (ENUM_DEAL_ENTRY)::HistoryDealGetInteger(ticket, DEAL_ENTRY);

      if(entry_type != DEAL_ENTRY_OUT)
         continue;

      ulong pos_id =
         ::HistoryDealGetInteger(ticket, DEAL_POSITION_ID);

      //--- locate the matching entry deal by position ID
      int match = -1;
      for(int j = 0; j < entry_count; j++)
        {
         if(position_ids[j] == pos_id)
           {
            match = j;
            break;
           }
        }

      if(match < 0)
         continue; // orphaned exit deal; no matching entry found

      ulong entry_tkn  = entry_tickets[match];
      ulong order_tkn  =
         ::HistoryDealGetInteger(ticket, DEAL_ORDER);

      int idx = ::ArraySize(out);
      ::ArrayResize(out, idx + 1);

      out[idx].entry_ticket  = entry_tkn;
      out[idx].exit_ticket   = ticket;
      out[idx].entry_time    =
         (datetime)::HistoryDealGetInteger(entry_tkn, DEAL_TIME);
      out[idx].exit_time     =
         (datetime)::HistoryDealGetInteger(ticket, DEAL_TIME);
      out[idx].entry_price   =
         ::HistoryDealGetDouble(entry_tkn, DEAL_PRICE);
      out[idx].exit_price    =
         ::HistoryDealGetDouble(ticket, DEAL_PRICE);
      out[idx].profit        =
         ::HistoryDealGetDouble(ticket, DEAL_PROFIT);
      out[idx].volume        =
         ::HistoryDealGetDouble(entry_tkn, DEAL_VOLUME);
      out[idx].symbol        =
         ::HistoryDealGetString(entry_tkn, DEAL_SYMBOL);
      out[idx].comment       =
         ::HistoryDealGetString(ticket, DEAL_COMMENT);
      out[idx].direction     =
         (ENUM_DEAL_TYPE)::HistoryDealGetInteger(entry_tkn, DEAL_TYPE);
      out[idx].magic         =
         ::HistoryDealGetInteger(entry_tkn, DEAL_MAGIC);

      //--- SL/TP: two-pass lookup (deal record first, order record fallback)
      out[idx].stop_loss   = FetchSL(entry_tkn, order_tkn);
      out[idx].take_profit = FetchTP(entry_tkn, order_tkn);
     }

   return(::ArraySize(out));
  }

#endif // TRADELOADER_MQH
//+------------------------------------------------------------------+