//+------------------------------------------------------------------+
//|                                           Marci_Silfrain_RZY.mq5 |
//|                                      Generated by AI Assistant   |
//+------------------------------------------------------------------+
#property copyright "AI Assistant"
#property link      ""
#property version   "2.00"
#property indicator_chart_window

// Bollinger Bands buffers (we keep the bands as standard indicator buffers)
#property indicator_buffers 3
#property indicator_plots   3

#property indicator_label1  "BB Upper"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_DASH

#property indicator_label2  "BB Lower"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_DASH

#property indicator_label3  "BB Mid"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrDodgerBlue
#property indicator_style3  STYLE_SOLID

//--- input parameters
input int      InpBandsPeriod    = 20;            // Bollinger Bands Period
input double   InpBandsDev       = 2.0;           // Bollinger Bands Deviations
input color    InpTrendlineColor = clrMagenta;    // Spawned Trendline Color
input color    InpMeasureColor   = clrYellow;     // Measure Line Color
input color    InpTargetColor    = clrLime;       // Target Line Color

//--- indicator buffers
double         ExtUpperBuffer[];
double         ExtLowerBuffer[];
double         ExtMidBuffer[];

int            bb_handle;

//+------------------------------------------------------------------+
//| Helper to create or update a trendline object                    |
//+------------------------------------------------------------------+
void CreateOrUpdateTrendline(string name, datetime t1, double p1, datetime t2, double p2, color col, int width, int style=STYLE_SOLID)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
     }
   else
     {
      ObjectSetInteger(0, name, OBJPROP_TIME, 0, t1);
      ObjectSetDouble(0, name, OBJPROP_PRICE, 0, p1);
      ObjectSetInteger(0, name, OBJPROP_TIME, 1, t2);
      ObjectSetDouble(0, name, OBJPROP_PRICE, 1, p2);
     }
   ObjectSetInteger(0, name, OBJPROP_COLOR, col);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_BACK, true);
  }

//+------------------------------------------------------------------+
//| Create the interactive UI Button                                 |
//+------------------------------------------------------------------+
void CreateDrawButton()
  {
   string name = "RZY_Button";
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 20);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, 40);
      ObjectSetInteger(0, name, OBJPROP_XSIZE, 100);
      ObjectSetInteger(0, name, OBJPROP_YSIZE, 30);
      ObjectSetString(0, name, OBJPROP_TEXT, "Draw RZY");
      ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
      ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clrDarkSlateGray);
      ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrLightGray);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
     }
  }

//+------------------------------------------------------------------+
//| Clean up orphans (if parent trendline is deleted)                |
//+------------------------------------------------------------------+
void CleanupOrphans()
  {
   int total = ObjectsTotal(0, 0, -1);
   for(int i = total - 1; i >= 0; i--)
     {
      string name = ObjectName(0, i, 0, -1);
      if(StringSubstr(name, 0, 4) == "RZY_")
        {
         int len = StringLen(name);
         if(len > 8 && StringSubstr(name, len - 8) == "_Measure")
           {
            string parent = StringSubstr(name, 0, len - 8);
            if(ObjectFind(0, parent) < 0) ObjectDelete(0, name);
           }
         else if(len > 7 && StringSubstr(name, len - 7) == "_Target")
           {
            string parent = StringSubstr(name, 0, len - 7);
            if(ObjectFind(0, parent) < 0) ObjectDelete(0, name);
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Update measurements based on user-drawn trendlines               |
//+------------------------------------------------------------------+
void UpdateRZYMovements()
  {
   int total = ObjectsTotal(0, 0, OBJ_TREND);
   for(int i = total - 1; i >= 0; i--)
     {
      string name = ObjectName(0, i, 0, OBJ_TREND);
      // Process lines created by button which start with RZY_
      if(StringSubstr(name, 0, 4) == "RZY_" && StringFind(name, "_Measure") < 0 && StringFind(name, "_Target") < 0)
        {
         datetime t1 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
         double p1 = ObjectGetDouble(0, name, OBJPROP_PRICE, 0);
         datetime t2 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 1);
         double p2 = ObjectGetDouble(0, name, OBJPROP_PRICE, 1);

         // Ensure t1 is the older time
         if(t1 > t2)
           {
            datetime temp_t = t1; t1 = t2; t2 = temp_t;
            double temp_p = p1; p1 = p2; p2 = temp_p;
           }

         if(t2 <= t1) continue; // Prevent divide by zero if points are at the same time

         int i1 = iBarShift(_Symbol, _Period, t1);
         int i2 = iBarShift(_Symbol, _Period, t2);
         if(i1 == -1 || i2 == -1) continue;

         // In MT5, 0 is current bar, so older bar has larger index
         if(i1 < i2) { int temp = i1; i1 = i2; i2 = temp; }

         bool is_downtrend = (p1 > p2);

         if(is_downtrend)
           {
            // Find lowest low
            int lowest_bar = i2;
            double lowest_val = iLow(_Symbol, _Period, lowest_bar);
            for(int k = i2 + 1; k <= i1; k++)
              {
               double low_k = iLow(_Symbol, _Period, k);
               if(low_k < lowest_val)
                 {
                  lowest_val = low_k;
                  lowest_bar = k;
                 }
              }

            datetime t_low = iTime(_Symbol, _Period, lowest_bar);
            double slope = (p2 - p1) / (double)(t2 - t1);
            double tl_val = p1 + slope * (t_low - t1);
            double distance = tl_val - lowest_val;
            double target = lowest_val - distance;

            string measure_name = name + "_Measure";
            string target_name = name + "_Target";

            CreateOrUpdateTrendline(measure_name, t_low, tl_val, t_low, lowest_val, InpMeasureColor, 2, STYLE_SOLID);

            datetime t_target = t2 + (t2 - t1);
            CreateOrUpdateTrendline(target_name, t_low, target, t_target, target, InpTargetColor, 2, STYLE_SOLID);
           }
         else
           {
            // Uptrend (sloping up)
            int highest_bar = i2;
            double highest_val = iHigh(_Symbol, _Period, highest_bar);
            for(int k = i2 + 1; k <= i1; k++)
              {
               double high_k = iHigh(_Symbol, _Period, k);
               if(high_k > highest_val)
                 {
                  highest_val = high_k;
                  highest_bar = k;
                 }
              }

            datetime t_high = iTime(_Symbol, _Period, highest_bar);
            double slope = (p2 - p1) / (double)(t2 - t1);
            double tl_val = p1 + slope * (t_high - t1);
            double distance = highest_val - tl_val;
            double target = highest_val + distance;

            string measure_name = name + "_Measure";
            string target_name = name + "_Target";

            CreateOrUpdateTrendline(measure_name, t_high, tl_val, t_high, highest_val, InpMeasureColor, 2, STYLE_SOLID);

            datetime t_target = t2 + (t2 - t1);
            CreateOrUpdateTrendline(target_name, t_high, target, t_target, target, InpTargetColor, 2, STYLE_SOLID);
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0, ExtUpperBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, ExtLowerBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, ExtMidBuffer, INDICATOR_DATA);

   bb_handle = iBands(_Symbol, _Period, InpBandsPeriod, 0, InpBandsDev, PRICE_CLOSE);
   if(bb_handle == INVALID_HANDLE)
     {
      Print("Failed to create Bollinger Bands indicator handle");
      return(INIT_FAILED);
     }

   CreateDrawButton();
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, "RZY_");
   IndicatorRelease(bb_handle);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| 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[])
  {
   if(rates_total < InpBandsPeriod)
      return(0);

//--- Copy Bollinger Bands
   int to_copy = rates_total - prev_calculated;
   if(prev_calculated > 0) to_copy++;
   else to_copy = rates_total;
   
   if(to_copy > 0)
     {
      CopyBuffer(bb_handle, 1, 0, to_copy, ExtUpperBuffer);
      CopyBuffer(bb_handle, 2, 0, to_copy, ExtLowerBuffer);
      CopyBuffer(bb_handle, 0, 0, to_copy, ExtMidBuffer);
     }

   CreateDrawButton(); // Ensure button is recreated if accidentally deleted

//--- Maintain user-drawn lines on new ticks/bars
   UpdateRZYMovements();
   CleanupOrphans();

   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Chart Event Handler                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Check if "Draw RZY" Button was clicked
   if(id == CHARTEVENT_OBJECT_CLICK && sparam == "RZY_Button")
     {
      // Generate a unique name for the new trendline
      string line_name = "RZY_" + IntegerToString((int)TimeLocal()) + "_" + IntegerToString(MathRand());
      
      // Calculate default positions (place it slightly to the left of the current screen)
      int visible_bars = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
      datetime t1 = iTime(_Symbol, _Period, visible_bars / 2);
      datetime t2 = iTime(_Symbol, _Period, visible_bars / 4);
      double p1 = iHigh(_Symbol, _Period, visible_bars / 2);
      double p2 = iLow(_Symbol, _Period, visible_bars / 4);

      // Create the trendline
      ObjectCreate(0, line_name, OBJ_TREND, 0, t1, p1, t2, p2);
      ObjectSetInteger(0, line_name, OBJPROP_COLOR, InpTrendlineColor);
      ObjectSetInteger(0, line_name, OBJPROP_WIDTH, 2);
      ObjectSetInteger(0, line_name, OBJPROP_RAY_RIGHT, false);
      ObjectSetInteger(0, line_name, OBJPROP_SELECTABLE, true);
      ObjectSetInteger(0, line_name, OBJPROP_SELECTED, true);

      // Force button state back to unclicked
      ObjectSetInteger(0, "RZY_Button", OBJPROP_STATE, false);
      ChartRedraw();
      
      // Calculate lines immediately
      UpdateRZYMovements();
     }

//--- Update measurements if user moves or edits a trendline
   if(id == CHARTEVENT_OBJECT_DRAG || id == CHARTEVENT_OBJECT_CHANGE)
     {
      UpdateRZYMovements();
      ChartRedraw();
     }

//--- Perform cleanup if objects are deleted
   if(id == CHARTEVENT_OBJECT_DELETE)
     {
      CleanupOrphans();
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+