//+------------------------------------------------------------------+
//|     Support and Resistance with Push Notifications               |
//|                Optimized by NZQ                                  |
//+------------------------------------------------------------------+
#property copyright "Barry Stander"
#property link      "http://myweb.absa.co.za/stander/4meta/"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 Tomato
#property indicator_color2 DeepSkyBlue

//---- Input Parameters
extern bool EnableAlerts = true;  // Toggle push notifications on/off

//---- Indicator Buffers
double v1[];
double v2[];

// Global variables to store last alerted bar indexes to avoid duplicate alerts
int lastAlertResistanceBar = -1;
int lastAlertSupportBar    = -1;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   IndicatorBuffers(2);

   // Resistance settings
   SetIndexStyle(0, DRAW_ARROW, STYLE_DOT, 0, Tomato);
   SetIndexArrow(0, 119);
   SetIndexBuffer(0, v1);
   SetIndexLabel(0, "Resistance");

   // Support settings
   SetIndexStyle(1, DRAW_ARROW, STYLE_DOT, 0, DeepSkyBlue);
   SetIndexArrow(1, 119);
   SetIndexBuffer(1, v2);
   SetIndexLabel(1, "Support");

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   // Check if we have enough bars
   if(rates_total < 2)
      return(0);

   // Determine starting bar for processing
   int startBar = prev_calculated > 0 ? prev_calculated - 1 : 0;

   // Loop through bars to calculate fractals and update buffers
   for(int i = startBar; i < rates_total; i++)
     {
      // Get fractal values for current bar
      double fractalUp   = iFractals(NULL, 0, MODE_UPPER, i);
      double fractalDown = iFractals(NULL, 0, MODE_LOWER, i);

      // Set resistance buffer if valid fractal is found, otherwise set EMPTY_VALUE
      if(fractalUp > 0)
         v1[i] = high[i];
      else
         v1[i] = EMPTY_VALUE;

      // Set support buffer if valid fractal is found, otherwise set EMPTY_VALUE
      if(fractalDown > 0)
         v2[i] = low[i];
      else
         v2[i] = EMPTY_VALUE;
     }

   // Check for cross conditions on the most recent closed bar (rates_total - 2)
   int signalBar = rates_total - 2; // use the last fully closed bar

   // Ensure the bar index is valid
   if(signalBar >= 0 && EnableAlerts)
     {
      // Price cross above resistance: current high exceeds resistance line on that bar
      if(v1[signalBar] != EMPTY_VALUE && high[signalBar] > v1[signalBar])
        {
         // Avoid duplicate alert for the same bar
         if(lastAlertResistanceBar != signalBar)
           {
            string msgRes = "Price crossed above resistance at " + DoubleToString(v1[signalBar], Digits);
            SendNotification(msgRes);
            lastAlertResistanceBar = signalBar;
           }
        }

      // Price cross below support: current low falls below support line on that bar
      if(v2[signalBar] != EMPTY_VALUE && low[signalBar] < v2[signalBar])
        {
         // Avoid duplicate alert for the same bar
         if(lastAlertSupportBar != signalBar)
           {
            string msgSup = "Price crossed below support at " + DoubleToString(v2[signalBar], Digits);
            SendNotification(msgSup);
            lastAlertSupportBar = signalBar;
           }
        }
     }

   return(rates_total);
  }
  
//+------------------------------------------------------------------+
// Changelog 2025-02-22: BY NZQ
//  - Converted to modern MQL4 standards (OnInit & OnCalculate)
//  - Added proper handling of EMPTY_VALUE when no fractal is found
//  - Implemented safe looping with prev_calculated to avoid
//    redundant calculations and out-of-bounds errors
//  - Improved overall code structure and comments for clarity
//  - Added push notification alerts for price crossing resistance
//    and support levels with an input parameter to toggle alerts on/off
//+------------------------------------------------------------------+
