//+------------------------------------------------------------------+
//|                                              ChartZoneDrawer.mqh |
//+------------------------------------------------------------------+
#ifndef CHARTZONEDRAWER_MQH
#define CHARTZONEDRAWER_MQH

#include "NewsEvent.mqh"
#include "CurrencyExtractor.mqh"

//+------------------------------------------------------------------+
//| CChartZoneDrawer                                                 |
//| Draws shaded rectangle zones on the chart for every high-impact  |
//| event window of the current trading day, using distinct colors   |
//| for the pre-event buffer and the post-event buffer, and removes  |
//| them cleanly by prefix.                                          |
//+------------------------------------------------------------------+
class CChartZoneDrawer
  {
private:
   string             m_prefix;
   color              m_pre_color;
   color              m_post_color;
   CCurrencyExtractor m_extractor;

   bool              SameDay(const datetime a, const datetime b) const;
   void              DrawZone(const string name, const datetime t1, const datetime t2,
                              const double p1, const double p2, const color zone_color);

public:
                     CChartZoneDrawer(void);
                    ~CChartZoneDrawer(void);

   void              DrawDayZones(const CNewsEvent &events[], int count, const string symbol,
                                  int minutes_before, int minutes_after);
   void              ClearZones(void);
  };

//+------------------------------------------------------------------+
//| Constructor: Fixes the object name prefix and the zone colors.   |
//+------------------------------------------------------------------+
CChartZoneDrawer::CChartZoneDrawer(void)
  {
//--- the prefix keeps our objects separable from everything else
   m_prefix     = "NFZ_";
   m_pre_color  = clrMistyRose;
   m_post_color = clrLavender;
  }

//+------------------------------------------------------------------+
//| Destructor: Removes all zones this drawer created.               |
//+------------------------------------------------------------------+
CChartZoneDrawer::~CChartZoneDrawer(void)
  {
//--- leaves the chart clean when the owner is destroyed
   ClearZones();
  }

//+------------------------------------------------------------------+
//| DrawDayZones                                                     |
//| Clears previous zones, then draws a pre-event and a post-event   |
//| rectangle for every high-impact event record of the current      |
//| trading day that passes the currency filter for the symbol. The  |
//| rectangles span the full visible price range of the chart.       |
//+------------------------------------------------------------------+
void CChartZoneDrawer::DrawDayZones(const CNewsEvent &events[], int count, const string symbol,
                                    int minutes_before, int minutes_after)
  {
//--- redrawing must never stack duplicates
   ClearZones();
//--- span the full visible price range of the chart
   double   price_max = ::ChartGetDouble(0, CHART_PRICE_MAX);
   double   price_min = ::ChartGetDouble(0, CHART_PRICE_MIN);
   datetime today     = ::TimeCurrent();
//--- draws a zone pair for each qualifying event record
   for(int i = 0; i < count; i++)
     {
      //--- only high-impact events are drawn
      if(events[i].impact != NEWS_IMPACT_HIGH)
         continue;
      //--- currency filter for the chart symbol
      if(!m_extractor.MatchesCurrency(symbol, events[i].currency))
         continue;
      //--- restricts to the current trading day
      if(!SameDay(events[i].event_time, today))
         continue;
      //--- computes the 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);
      //--- pre-event buffer zone in the warm color
      string pre_name = ::StringFormat("%s%d_PRE", m_prefix, i);
      DrawZone(pre_name, window_start, events[i].event_time, price_min, price_max, m_pre_color);
      //--- post-event buffer zone in the cool color
      string post_name = ::StringFormat("%s%d_POST", m_prefix, i);
      DrawZone(post_name, events[i].event_time, window_end, price_min, price_max, m_post_color);
     }
//--- requests a repaint so the zones appear immediately
   ::ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| ClearZones                                                       |
//| Deletes every chart object whose name starts with the drawer's   |
//| prefix, walking the object list backward so deletion does not    |
//| invalidate the indices still to be visited.                      |
//+------------------------------------------------------------------+
void CChartZoneDrawer::ClearZones(void)
  {
//--- walks backward because deletion reindexes the object list
   int total = ::ObjectsTotal(0, -1, -1);
   for(int i = total - 1; i >= 0; i--)
     {
      string name = ::ObjectName(0, i, -1, -1);
      //--- delete only objects that carry our prefix
      if(::StringFind(name, m_prefix) == 0)
         ::ObjectDelete(0, name);
     }
//--- requests a repaint so the removal is visible
   ::ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| SameDay                                                          |
//| Returns true when two datetimes fall on the same calendar day.   |
//+------------------------------------------------------------------+
bool CChartZoneDrawer::SameDay(const datetime a, const datetime b) const
  {
//--- decomposes both datetimes into calendar components
   MqlDateTime da;
   MqlDateTime db;
   ::TimeToStruct(a, da);
   ::TimeToStruct(b, db);
//--- compares year, month, and day
   return(da.year == db.year && da.mon == db.mon && da.day == db.day);
  }

//+------------------------------------------------------------------+
//| DrawZone                                                         |
//| Creates one filled background rectangle between two time and     |
//| price coordinate pairs, non-selectable and hidden from the       |
//| object list so it never interferes with manual chart work.       |
//+------------------------------------------------------------------+
void CChartZoneDrawer::DrawZone(const string name, const datetime t1, const datetime t2,
                                const double p1, const double p2, const color zone_color)
  {
//--- creates the rectangle anchored by time and price
   if(!::ObjectCreate(0, name, OBJ_RECTANGLE, 0, t1, p1, t2, p2))
      return;
//--- styles it as a filled background zone
   ::ObjectSetInteger(0, name, OBJPROP_COLOR, zone_color);
   ::ObjectSetInteger(0, name, OBJPROP_FILL, true);
   ::ObjectSetInteger(0, name, OBJPROP_BACK, true);
   ::ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ::ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
  }

#endif // CHARTZONEDRAWER_MQH
//+------------------------------------------------------------------+