//+------------------------------------------------------------------+
//|                                            NewsWindowChecker.mqh |
//+------------------------------------------------------------------+
#ifndef NEWSWINDOWCHECKER_MQH
#define NEWSWINDOWCHECKER_MQH

#include "NewsEvent.mqh"
#include "CurrencyExtractor.mqh"

//+------------------------------------------------------------------+
//| CNewsWindowChecker                                               |
//| Evaluates whether a given time falls inside the news window of   |
//| any event record that passes the impact filter and the currency  |
//| filter, and produces the human-readable reason string.           |
//+------------------------------------------------------------------+
class CNewsWindowChecker
  {
private:
   string             m_reason;
   CCurrencyExtractor m_extractor;

public:
                     CNewsWindowChecker(void);
                    ~CNewsWindowChecker(void);

   bool              Check(const CNewsEvent &events[], int count, const string symbol,
                           int minutes_before, int minutes_after, ENUM_NEWS_IMPACT min_impact);
   bool              CheckAt(const CNewsEvent &events[], int count, const string symbol,
                             int minutes_before, int minutes_after, ENUM_NEWS_IMPACT min_impact,
                             const datetime now);
   string            GetReason(void) const { return(m_reason); }
  };

//+------------------------------------------------------------------+
//| Constructor: Starts with a clear reason string.                  |
//+------------------------------------------------------------------+
CNewsWindowChecker::CNewsWindowChecker(void)
  {
//--- no evaluation has happened yet
   m_reason = "CLEAR";
  }

//+------------------------------------------------------------------+
//| Destructor: No resources are owned, so nothing to release.       |
//+------------------------------------------------------------------+
CNewsWindowChecker::~CNewsWindowChecker(void)
  {
  }

//+------------------------------------------------------------------+
//| Check                                                            |
//| Evaluates the event array against the current server time. This  |
//| is the production entry point; it delegates to CheckAt() with    |
//| TimeCurrent() so the boundary logic stays testable.              |
//+------------------------------------------------------------------+
bool CNewsWindowChecker::Check(const CNewsEvent &events[], int count, const string symbol,
                               int minutes_before, int minutes_after, ENUM_NEWS_IMPACT min_impact)
  {
//--- evaluate at the current server time
   return(CheckAt(events, count, symbol, minutes_before, minutes_after, min_impact, ::TimeCurrent()));
  }

//+------------------------------------------------------------------+
//| CheckAt                                                          |
//| Evaluates the event array at an explicit time. Applies the       |
//| impact filter, then the currency filter, then the inclusive time |
//| window [event - pre * 60, event + post * 60]. On the first match |
//| it stores the formatted reason string and returns true; when no  |
//| event record matches it stores "CLEAR" and returns false.        |
//+------------------------------------------------------------------+
bool CNewsWindowChecker::CheckAt(const CNewsEvent &events[], int count, const string symbol,
                                 int minutes_before, int minutes_after, ENUM_NEWS_IMPACT min_impact,
                                 const datetime now)
  {
//--- scans every event record until the first match
   for(int i = 0; i < count; i++)
     {
      //--- impact filter: cheapest test first
      if(events[i].impact < min_impact)
         continue;
      //--- currency filter: the event must touch the base or quote
      if(!m_extractor.MatchesCurrency(symbol, events[i].currency))
         continue;
      //--- computes the inclusive news window boundaries
      datetime window_start = events[i].event_time - (datetime)(minutes_before * 60);
      datetime window_end   = events[i].event_time + (datetime)(minutes_after * 60);
      if(now < window_start || now > window_end)
         continue;
      //--- inside the news window: build the reason string
      long delta_seconds = (long)events[i].event_time - (long)now;
      if(delta_seconds >= 0)
        {
         //--- pre-event case: the event is still ahead
         int minutes_ahead = (int)(delta_seconds / 60);
         m_reason = ::StringFormat("BLOCKED: %s in %d minutes", events[i].title, minutes_ahead);
        }
      else
        {
         //--- post-event case: the event has already passed
         int minutes_ago = (int)(-delta_seconds / 60);
         m_reason = ::StringFormat("BLOCKED: %s ended %d minutes ago", events[i].title, minutes_ago);
        }
      return(true);
     }
//--- no event record matched: trading is allowed
   m_reason = "CLEAR";
   return(false);
  }

#endif // NEWSWINDOWCHECKER_MQH
//+------------------------------------------------------------------+