#property version     "2.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

//--- Plot 1: Upper Trendline
#property indicator_label1  "Upper"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrTeal
#property indicator_style1  STYLE_DASH
#property indicator_width1  1

//--- Plot 2: Lower Trendline
#property indicator_label2  "Lower"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_DASH
#property indicator_width2  1

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input int    InpLength     = 14;      // Swing Detection Lookback
input double InpMult       = 1.0;     // Slope Steepness (0 = levels)
input string InpCalcMethod = "Atr";   // Slope Method: Atr | Stdev | Linreg
input bool   InpBackpaint  = true;    // Backpainting (offset trendlines to past)
input color  InpUpColor    = clrTeal; // Up Trendline Color
input color  InpDnColor    = clrRed;  // Down Trendline Color
input bool   InpShowExt    = true;    // Show Extended Lines

//+------------------------------------------------------------------+
//| Indicator Buffers                                                |
//+------------------------------------------------------------------+
double UpperBuffer[];
double LowerBuffer[];

//+------------------------------------------------------------------+
//| Persistent State (equivalent to Pine var declarations)           |
//+------------------------------------------------------------------+
double g_upper    = 0.0;
double g_lower    = 0.0;
double g_slope_ph = 0.0;
double g_slope_pl = 0.0;
int    g_upos     = 0;
int    g_dnos     = 0;

// Track previous bar's upos/dnos to detect transitions (non-repainting signals)
int    g_upos_prev = 0;
int    g_dnos_prev = 0;

//+------------------------------------------------------------------+
//| OnInit                                                           |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, UpperBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, LowerBuffer, INDICATOR_DATA);

   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   PlotIndexSetInteger(0, PLOT_LINE_COLOR, InpUpColor);
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, InpDnColor);

   IndicatorSetString(INDICATOR_SHORTNAME,
                      "LuxAlgo - Trendlines with Breaks (" +
                      IntegerToString(InpLength) + ")");

   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| ATR over [shift, shift+period)                                   |
//+------------------------------------------------------------------+
double CalcATR(const int shift, const int period)
  {
   double atr = 0.0;
   for(int i = shift; i < shift + period; i++)
     {
      double hi   = iHigh(_Symbol,  _Period, i);
      double lo   = iLow(_Symbol,   _Period, i);
      double prev = iClose(_Symbol, _Period, i + 1);
      double tr   = MathMax(hi - lo,
                    MathMax(MathAbs(hi - prev), MathAbs(lo - prev)));
      atr += tr;
     }
   return atr / period;
  }

//+------------------------------------------------------------------+
//| Population StdDev of Close over [shift, shift+period)           |
//+------------------------------------------------------------------+
double CalcStdev(const int shift, const int period)
  {
   double sum = 0.0, sum2 = 0.0;
   for(int i = shift; i < shift + period; i++)
     {
      double c = iClose(_Symbol, _Period, i);
      sum  += c;
      sum2 += c * c;
     }
   double mean = sum / period;
   double var  = sum2 / period - mean * mean;
   return (var > 0.0) ? MathSqrt(var) : 0.0;
  }

//+------------------------------------------------------------------+
//| LinReg slope proxy (matches Pine Linreg formula)                 |
//| |SMA(src*n) - SMA(src)*SMA(n)| / Variance(n) / 2                |
//+------------------------------------------------------------------+
double CalcLinreg(const int shift, const int period)
  {
   double sumC = 0, sumN = 0, sumCN = 0, sumN2 = 0;
   for(int i = 0; i < period; i++)
     {
      double idx = i;                                          // local bar index
      double c   = iClose(_Symbol, _Period, shift + period - 1 - i);
      sumC  += c;
      sumN  += idx;
      sumCN += c * idx;
      sumN2 += idx * idx;
     }
   double meanC  = sumC  / period;
   double meanN  = sumN  / period;
   double meanCN = sumCN / period;
   double meanN2 = sumN2 / period;
   double varN   = meanN2 - meanN * meanN;
   if(varN == 0.0) return 0.0;
   return MathAbs(meanCN - meanC * meanN) / varN / 2.0;
  }

//+------------------------------------------------------------------+
//| Pivot High: returns price if the bar at (shift+length) is the   |
//| highest high in the window [shift, shift+2*length], else 0.     |
//+------------------------------------------------------------------+
double PivotHigh(const int shift, const int length)
  {
   int    center      = shift + length;
   double centerHigh  = iHigh(_Symbol, _Period, center);
   for(int i = center - length; i <= center + length; i++)
     {
      if(i == center) continue;
      if(iHigh(_Symbol, _Period, i) >= centerHigh)
         return 0.0;
     }
   return centerHigh;
  }

//+------------------------------------------------------------------+
//| Pivot Low                                                        |
//+------------------------------------------------------------------+
double PivotLow(const int shift, const int length)
  {
   int    center     = shift + length;
   double centerLow  = iLow(_Symbol, _Period, center);
   for(int i = center - length; i <= center + length; i++)
     {
      if(i == center) continue;
      if(iLow(_Symbol, _Period, i) <= centerLow)
         return 0.0;
     }
   return centerLow;
  }

//+------------------------------------------------------------------+
//| Draw / update a "B" signal arrow                                 |
//| isBuy=true  → up arrow at Low  (Upper Break / BUY)              |
//| isBuy=false → down arrow at High (Lower Break / SELL)           |
//+------------------------------------------------------------------+
void DrawSignalArrow(const string   name,
                     const datetime barTime,
                     const double   price,
                     const bool     isBuy,
                     const color    clr)
  {
   if(ObjectFind(0, name) >= 0)
      ObjectDelete(0, name);

   ObjectCreate(0, name, OBJ_ARROW_LABEL, 0, barTime, price);

   // Arrow codes: 233 = up arrow, 234 = down arrow
   ObjectSetInteger(0, name, OBJPROP_ARROWCODE, isBuy ? 233 : 234);
   ObjectSetInteger(0, name, OBJPROP_COLOR,     clr);
   ObjectSetInteger(0, name, OBJPROP_WIDTH,     2);
   ObjectSetInteger(0, name, OBJPROP_ANCHOR,    isBuy ? ANCHOR_TOP : ANCHOR_BOTTOM);
   ObjectSetString (0, name, OBJPROP_TEXT,      "B");
   ObjectSetString (0, name, OBJPROP_TOOLTIP,
                    isBuy
                    ? "BUY  — Price broke down-trendline upward"
                    : "SELL — Price broke up-trendline downward");
  }

//+------------------------------------------------------------------+
//| Draw / update an extended dashed trendline object               |
//+------------------------------------------------------------------+
void DrawExtLine(const string   name,
                 const datetime t1,  const double p1,
                 const datetime t2,  const double p2,
                 const color    clr)
  {
   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,   clr);
   ObjectSetInteger(0, name, OBJPROP_STYLE,   STYLE_DASH);
   ObjectSetInteger(0, name, OBJPROP_WIDTH,   1);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
   ObjectSetInteger(0, name, OBJPROP_BACK,    true);
  }

//+------------------------------------------------------------------+
//| OnCalculate                                                      |
//+------------------------------------------------------------------+
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[])
  {
   // Need at least 3x length bars for reliable pivots + slope calcs
   int minBars = InpLength * 3 + 2;
   if(rates_total < minBars)
      return 0;

   // On first full calc reset state
   int startBar = (prev_calculated <= minBars) ? minBars : prev_calculated - 1;

   for(int bar = startBar; bar < rates_total; bar++)
     {
      int shift = rates_total - 1 - bar;  // shift: 0=newest bar

      double src = close[bar];

      //--- Slope calculation -------------------------------------------
      double slope = 0.0;
      if(InpCalcMethod == "Atr")
         slope = CalcATR(shift, InpLength)    / InpLength * InpMult;
      else if(InpCalcMethod == "Stdev")
         slope = CalcStdev(shift, InpLength)  / InpLength * InpMult;
      else
         slope = CalcLinreg(shift, InpLength)              * InpMult;

      //--- Pivot detection --------------------------------------------
      // Pivot sits InpLength bars BEFORE current bar in time
      // (center of the pivot window)
      double ph = 0.0, pl = 0.0;
      if(shift + InpLength < rates_total - 1)
        {
         ph = PivotHigh(shift, InpLength);
         pl = PivotLow(shift,  InpLength);
        }

      //--- Update persistent slope ------------------------------------
      if(ph != 0.0) g_slope_ph = slope;
      if(pl != 0.0) g_slope_pl = slope;

      //--- Update trendline anchor levels -----------------------------
      if(ph != 0.0)  g_upper = ph;
      else           g_upper = g_upper - g_slope_ph;

      if(pl != 0.0)  g_lower = pl;
      else           g_lower = g_lower + g_slope_pl;

      //--- Projected values at current bar ----------------------------
      double upperVal = g_upper - g_slope_ph * InpLength;
      double lowerVal = g_lower + g_slope_pl * InpLength;

      //--- Fill indicator buffers -------------------------------------
      if(InpBackpaint)
        {
         UpperBuffer[bar] = (ph != 0.0) ? EMPTY_VALUE : g_upper;
         LowerBuffer[bar] = (pl != 0.0) ? EMPTY_VALUE : g_lower;
        }
      else
        {
         UpperBuffer[bar] = (ph != 0.0) ? EMPTY_VALUE : upperVal;
         LowerBuffer[bar] = (pl != 0.0) ? EMPTY_VALUE : lowerVal;
        }

      //--- Extended lines (drawn at pivot bar) ------------------------
      if(InpShowExt)
        {
         if(ph != 0.0)
           {
            datetime t1 = time[bar - InpLength];    // pivot bar time (backpaint offset)
            datetime t2 = (bar - InpLength + 1 < rates_total)
                          ? time[bar - InpLength + 1] : time[bar];
            double   p1 = InpBackpaint ? ph               : upperVal;
            double   p2 = InpBackpaint ? ph - slope        : g_upper - g_slope_ph * (InpLength + 1);
            DrawExtLine("TB_UPTL", t1, p1, t2, p2, InpUpColor);
           }
         if(pl != 0.0)
           {
            datetime t1 = time[bar - InpLength];
            datetime t2 = (bar - InpLength + 1 < rates_total)
                          ? time[bar - InpLength + 1] : time[bar];
            double   p1 = InpBackpaint ? pl               : lowerVal;
            double   p2 = InpBackpaint ? pl + slope        : g_lower + g_slope_pl * (InpLength + 1);
            DrawExtLine("TB_DNTL", t1, p1, t2, p2, InpDnColor);
           }
        }

      //--- Breakout state (upos / dnos) --------------------------------
      // Save previous bar's state before updating
      g_upos_prev = g_upos;
      g_dnos_prev = g_dnos;

      if(ph != 0.0)          g_upos = 0;
      else if(src > upperVal) g_upos = 1;

      if(pl != 0.0)          g_dnos = 0;
      else if(src < lowerVal) g_dnos = 1;

      //--- "B" Signal arrows (real-time, non-repainting) ---------------
      // Only draw on the current live bar (bar == rates_total - 1)
      if(bar == rates_total - 1)
        {
         // BUY  — upward breakout (upos rose from 0 → 1)
         if(g_upos > g_upos_prev)
           {
            string nm = "TB_BUY_" + IntegerToString((int)time[bar]);
            DrawSignalArrow(nm, time[bar], low[bar], true, InpUpColor);
            Alert(_Symbol, " [LuxAlgo TrendBreaker] BUY — Upward Breakout @ ",
                  DoubleToString(close[bar], _Digits));
           }

         // SELL — downward breakout (dnos rose from 0 → 1)
         if(g_dnos > g_dnos_prev)
           {
            string nm = "TB_SELL_" + IntegerToString((int)time[bar]);
            DrawSignalArrow(nm, time[bar], high[bar], false, InpDnColor);
            Alert(_Symbol, " [LuxAlgo TrendBreaker] SELL — Downward Breakout @ ",
                  DoubleToString(close[bar], _Digits));
           }
        }
     }

   return rates_total;
  }

//+------------------------------------------------------------------+
//| OnDeinit — remove all drawn objects                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, "TB_BUY_");
   ObjectsDeleteAll(0, "TB_SELL_");
   ObjectDelete(0, "TB_UPTL");
   ObjectDelete(0, "TB_DNTL");
  }
//+------------------------------------------------------------------+
