//+------------------------------------------------------------------+
//|                 Simple_System_Pure_Structure_ZeroLag.mq4         |
//|                 Forex Strategy Development                       |
//+------------------------------------------------------------------+
#property copyright "Forex Strategy Development"
#property link      ""
#property version   "2.10"
#property strict
#property indicator_chart_window

//--- Buffers
#property indicator_buffers 4

// Major Peaks (Large White)
#property indicator_color1 clrWhite
#property indicator_width1 4
#property indicator_color2 clrWhite
#property indicator_width2 4

// Minor Peaks (Medium Silver)
#property indicator_color3 clrSilver
#property indicator_width3 2
#property indicator_color4 clrSilver
#property indicator_width4 2

//--- Inputs: Major Trend (ZigZag 1)
input string Grp1 = "--- Major Peaks ---";
input int    Major_ZZ_Depth     = 360;
input int    Major_ZZ_Dev       = 90;
input int    Major_ZZ_Backstep  = 45;

//--- Inputs: Minor Continuation (ZigZag 2)
input string Grp2 = "--- Minor Peaks ---";
input int    Minor_ZZ_Depth     = 120;
input int    Minor_ZZ_Dev       = 75;
input int    Minor_ZZ_Backstep  = 15;

//--- Inputs: Notification Suite
input string Grp3 = "--- Notification Suite ---";
input bool   Enable_Alerts      = true;  // MT4 Pop-up & Sound
input bool   Enable_Push        = false; // Mobile App Notification
input bool   Enable_Email       = false; // Email Notification

//--- Global Buffers
double MajorBuyBuffer[];
double MajorSellBuffer[];
double MinorBuyBuffer[];
double MinorSellBuffer[];

//--- Memory States
datetime last_alert_time_maj = 0;
datetime last_alert_time_min = 0;
datetime G_LastBarTime       = 0;

//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, MajorBuyBuffer);   SetIndexStyle(0, DRAW_ARROW); SetIndexArrow(0, 233);
   SetIndexBuffer(1, MajorSellBuffer);  SetIndexStyle(1, DRAW_ARROW); SetIndexArrow(1, 234);
   
   SetIndexBuffer(2, MinorBuyBuffer);   SetIndexStyle(2, DRAW_ARROW); SetIndexArrow(2, 233);
   SetIndexBuffer(3, MinorSellBuffer);  SetIndexStyle(3, DRAW_ARROW); SetIndexArrow(3, 234);

   ObjectsDeleteAll(0, "PURE_ZZ_");
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, "PURE_ZZ_");
  }

//+------------------------------------------------------------------+
//| Text Label Generator                                             |
//+------------------------------------------------------------------+
void DrawLabel(string tag, datetime t, double price, string text, color clr, bool isAbove)
  {
   string objName = "PURE_ZZ_" + tag + "_" + TimeToString(t, TIME_DATE|TIME_MINUTES);
   if(ObjectFind(0, objName) >= 0) ObjectDelete(0, objName);
   
   if(ObjectCreate(0, objName, OBJ_TEXT, 0, t, price))
     {
      ObjectSetString(0, objName, OBJPROP_TEXT, text);
      ObjectSetString(0, objName, OBJPROP_FONT, "Arial");
      ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 8);
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, objName, OBJPROP_ANCHOR, isAbove ? ANCHOR_BOTTOM : ANCHOR_TOP);
      ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
     }
  }

//+------------------------------------------------------------------+
//| Central Alert Execution                                          |
//+------------------------------------------------------------------+
void TriggerAlert(string message)
  {
   string final_msg = Symbol() + " (" + EnumToString((ENUM_TIMEFRAMES)Period()) + ") - " + message;
   if(Enable_Alerts) Alert(final_msg);
   if(Enable_Push)   SendNotification(final_msg);
   if(Enable_Email)  SendMail("Simple System Alert", final_msg);
  }

//+------------------------------------------------------------------+
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[])
  {
   if(rates_total < Major_ZZ_Depth) return(0);

   int limit = 0;

   if(prev_calculated == 0)
     {
      ArrayInitialize(MajorBuyBuffer, EMPTY_VALUE);
      ArrayInitialize(MajorSellBuffer, EMPTY_VALUE);
      ArrayInitialize(MinorBuyBuffer, EMPTY_VALUE);
      ArrayInitialize(MinorSellBuffer, EMPTY_VALUE);
      ObjectsDeleteAll(0, "PURE_ZZ_");
      limit = rates_total - 1;
      G_LastBarTime = time[0];
     }
   else
     {
      // ZERO-LAG THROTTLE: 
      // If it's a brand new candle, run the deep historical check. 
      // If it's just a live tick inside the current candle, only check the last 3 bars.
      if(time[0] != G_LastBarTime)
        {
         limit = MathMax(rates_total - prev_calculated, Major_ZZ_Depth * 3);
         G_LastBarTime = time[0];
        }
      else
        {
         limit = 3; // Live tick repainting zone
        }
     }

   double pt = Point * ((Point == 0.001 || Point == 0.00001) ? 10.0 : 1.0);
   double pTol = pt * 5.0; 
   
   double hug_offset   = pt * 1.0;   
   double stack_offset = pt * 15.0;  

   for(int i = limit; i >= 0; i--)
     {
      MajorBuyBuffer[i] = EMPTY_VALUE; MajorSellBuffer[i] = EMPTY_VALUE;
      MinorBuyBuffer[i] = EMPTY_VALUE; MinorSellBuffer[i] = EMPTY_VALUE;

      double zz_major = iCustom(Symbol(), 0, "ZigZag", Major_ZZ_Depth, Major_ZZ_Dev, Major_ZZ_Backstep, 0, i);
      double zz_minor = iCustom(Symbol(), 0, "ZigZag", Minor_ZZ_Depth, Minor_ZZ_Dev, Minor_ZZ_Backstep, 0, i);
      
      bool overlap_on_this_bar = (zz_major > 0.0 && zz_minor > 0.0);

      // --- 1. STAMP MAJOR PEAKS ---
      if(zz_major > 0.0 && zz_major != EMPTY_VALUE)
        {
         if(MathAbs(zz_major - low[i]) <= pTol)
           {
            MajorBuyBuffer[i] = low[i] - hug_offset;
            DrawLabel("MajB", time[i], MajorBuyBuffer[i] - (pt*5), "[MAJOR PEAK]", clrWhite, false);
            
            if(i == 1 && time[i] != last_alert_time_maj)
              { TriggerAlert("MAJOR BULLISH PEAK FORMED!"); last_alert_time_maj = time[i]; }
           }
           
         if(MathAbs(zz_major - high[i]) <= pTol)
           {
            MajorSellBuffer[i] = high[i] + hug_offset;
            DrawLabel("MajS", time[i], MajorSellBuffer[i] + (pt*5), "[MAJOR PEAK]", clrWhite, true);
            
            if(i == 1 && time[i] != last_alert_time_maj)
              { TriggerAlert("MAJOR BEARISH PEAK FORMED!"); last_alert_time_maj = time[i]; }
           }
        }

      // --- 2. STAMP MINOR PEAKS ---
      if(zz_minor > 0.0 && zz_minor != EMPTY_VALUE)
        {
         double applied_offset = overlap_on_this_bar ? stack_offset : hug_offset;
         
         if(MathAbs(zz_minor - low[i]) <= pTol)
           {
            MinorBuyBuffer[i] = low[i] - applied_offset;
            DrawLabel("MinB", time[i], MinorBuyBuffer[i] - (pt*5), "[MINOR PEAK]", clrSilver, false);
            
            if(i == 1 && time[i] != last_alert_time_min && !overlap_on_this_bar) 
              { TriggerAlert("MINOR BULLISH PEAK FORMED!"); last_alert_time_min = time[i]; }
           }
           
         if(MathAbs(zz_minor - high[i]) <= pTol)
           {
            MinorSellBuffer[i] = high[i] + applied_offset;
            DrawLabel("MinS", time[i], MinorSellBuffer[i] + (pt*5), "[MINOR PEAK]", clrSilver, true);
            
            if(i == 1 && time[i] != last_alert_time_min && !overlap_on_this_bar)
              { TriggerAlert("MINOR BEARISH PEAK FORMED!"); last_alert_time_min = time[i]; }
           }
        }
     }

   return(rates_total);
  }
//+------------------------------------------------------------------+