//+------------------------------------------------------------------+
//|                                             SuperOsc LuxAlgo MTF |
//|                              Converted from MQL4 to MQL5          |
//+------------------------------------------------------------------+
#property copyright "Converted to MQL5"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   5

#property indicator_minimum -120
#property indicator_maximum 120

// Main up (green)
#property indicator_label1  "MainUp"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrLime
#property indicator_width1  1

// Main down (red)
#property indicator_label2  "MainDn"
#property indicator_type2   DRAW_HISTOGRAM
#property indicator_color2  clrRed
#property indicator_width2  1

// Histogram (gray)
#property indicator_label3  "Histogram"
#property indicator_type3   DRAW_HISTOGRAM
#property indicator_color3  clrGray
#property indicator_width3  1

// AMA signal (yellow)
#property indicator_label4  "Signal"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrYellow
#property indicator_width4  1

// Marks (arrows, Spt cross)
#property indicator_label5  "Marks"
#property indicator_type5   DRAW_ARROW
#property indicator_color5  clrWhite
#property indicator_width5  1

//---- inputs
input string           InpID       = "A";           // ID
input ENUM_TIMEFRAMES  InpTF       = PERIOD_H1;      // TimeFrame
input int              InpLength   = 10;             // ATR length
input double           InpMult     = 2.0;            // ATR multiplier
input int              InpSmooth   = 72;             // Smooth for hist EMA
input bool             InpShowLn   = true;
input bool             InpShowLb   = true;
input bool             InpShowPer  = false;

// vertical lines on osc color change
input bool             InpShowColorChangeLines  = true;
input color            InpVLineColorUp          = clrLime;
input ENUM_LINE_STYLE  InpVLineStyleUp          = STYLE_DOT;
input int              InpVLineWidthUp          = 1;

input color            InpVLineColorDn          = clrRed;
input ENUM_LINE_STYLE  InpVLineStyleDn          = STYLE_DOT;
input int              InpVLineWidthDn          = 1;

// arrows on main chart on osc color change
input bool             InpShowColorChangeArrows = false;
input color            InpChartArrowColorUp     = clrLime;
input color            InpChartArrowColorDn     = clrRed;
input int              InpChartArrowCodeUp      = 233;   // UP
input int              InpChartArrowCodeDn      = 234;   // DOWN
input int              InpChartArrowShiftPoints = 50;    // offset in points

//---- plot buffers (drawn on current chart's timeframe, values sourced from InpTF)
double MainUpBuffer[];
double MainDnBuffer[];
double HistBuffer[];
double AmaBuffer[];
double MarkBuffer[];

//---- internal (non-plotted) buffers on the selected higher timeframe
double OscTf[];
double SptTf[];
double UpperTf[];
double LowerTf[];
int    TrendTf[];
double amaNorm_tf[];
double histNorm_tf[];
double atrTf[];          // ATR values copied from the indicator handle (MQL5-specific)

//---- MQL5 requires an indicator handle for iATR, values are fetched via CopyBuffer
int    atrHandle = INVALID_HANDLE;

//---- global object-name prefix for this instance
string gPrefix;

//+------------------------------------------------------------------+
//| Delete indicator objects (only this instance)                    |
//+------------------------------------------------------------------+
void DeleteMyObjects()
{
   int total = ObjectsTotal(0, -1, -1);
   for(int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i, -1, -1);
      if(StringFind(name, gPrefix, 0) == 0)
         ObjectDelete(0, name);
   }
}

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   gPrefix = "SO_" + InpID + "_";

   DeleteMyObjects();

   SetIndexBuffer(0, MainUpBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, MainDnBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, HistBuffer,   INDICATOR_DATA);
   SetIndexBuffer(3, AmaBuffer,    INDICATOR_DATA);
   SetIndexBuffer(4, MarkBuffer,   INDICATOR_DATA);

   ArraySetAsSeries(MainUpBuffer, true);
   ArraySetAsSeries(MainDnBuffer, true);
   ArraySetAsSeries(HistBuffer,   true);
   ArraySetAsSeries(AmaBuffer,    true);
   ArraySetAsSeries(MarkBuffer,   true);

   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   // Original code never set an explicit arrow symbol, so the platform default is kept.
   // Uncomment to use a more visible marker (e.g. triangles like the chart arrows below):
   // PlotIndexSetInteger(4, PLOT_ARROW, 217);

   // MQL5: iATR no longer accepts a shift parameter directly - must create a handle
   // and read its values with CopyBuffer().
   atrHandle = iATR(NULL, InpTF, InpLength);
   if(atrHandle == INVALID_HANDLE)
   {
      Print("SuperOsc MTF: failed to create ATR handle, error ", GetLastError());
      return(INIT_FAILED);
   }

   IndicatorSetString(INDICATOR_SHORTNAME, "SuperOsc [LuxAlgo MTF]");

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   DeleteMyObjects();
   if(atrHandle != INVALID_HANDLE)
      IndicatorRelease(atrHandle);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Calculation                                                      |
//+------------------------------------------------------------------+
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 < InpLength + 5)
      return(0);

   // MQL5: unlike MQL4, the arrays passed into OnCalculate are NOT series-indexed
   // by default (index 0 = oldest bar). The original algorithm relies on index 0
   // being the most recent bar and index i+1 being the previous (older) bar, so
   // this must be set explicitly every call.
   ArraySetAsSeries(time,         true);
   ArraySetAsSeries(open,         true);
   ArraySetAsSeries(high,         true);
   ArraySetAsSeries(low,          true);
   ArraySetAsSeries(close,        true);
   ArraySetAsSeries(tick_volume,  true);
   ArraySetAsSeries(volume,       true);
   ArraySetAsSeries(spread,       true);

   // 1) Bars on the selected timeframe
   int tf_bars = iBars(NULL, InpTF);
   if(tf_bars <= InpLength + 5)
      return(prev_calculated);

   // 2) Resize internal arrays to the selected timeframe
   ArrayResize(OscTf,      tf_bars);
   ArrayResize(SptTf,      tf_bars);
   ArrayResize(UpperTf,    tf_bars);
   ArrayResize(LowerTf,    tf_bars);
   ArrayResize(TrendTf,    tf_bars);
   ArrayResize(amaNorm_tf, tf_bars);
   ArrayResize(histNorm_tf,tf_bars);
   ArrayResize(atrTf,      tf_bars);

   ArraySetAsSeries(OscTf,      true);
   ArraySetAsSeries(SptTf,      true);
   ArraySetAsSeries(UpperTf,    true);
   ArraySetAsSeries(LowerTf,    true);
   ArraySetAsSeries(TrendTf,    true);
   ArraySetAsSeries(amaNorm_tf, true);
   ArraySetAsSeries(histNorm_tf,true);
   ArraySetAsSeries(atrTf,      true);

   // MQL5: fetch ATR values from the indicator handle (series order: 0 = current bar)
   int copied = CopyBuffer(atrHandle, 0, 0, tf_bars, atrTf);
   if(copied <= 0)
      return(prev_calculated);   // ATR history not ready yet - retry on next tick

   // 3) Initialization on first call
   int start_tf = tf_bars - 2;
   if(prev_calculated == 0)
   {
      for(int j = 0; j < tf_bars; j++)
      {
         OscTf[j]       = 0.0;
         SptTf[j]       = 0.0;
         UpperTf[j]     = 0.0;
         LowerTf[j]     = 0.0;
         TrendTf[j]     = 0;
         amaNorm_tf[j]  = 0.0;
         histNorm_tf[j] = 0.0;
      }

      for(int i = 0; i < rates_total; i++)
      {
         MainUpBuffer[i] = EMPTY_VALUE;
         MainDnBuffer[i] = EMPTY_VALUE;
         HistBuffer[i]   = 0.0;
         AmaBuffer[i]    = 0.0;
         MarkBuffer[i]   = EMPTY_VALUE;
      }
   }

   // 4) SuperOsc calculation on the selected timeframe
   for(int j = start_tf; j >= 0; j--)
   {
      double atrv = atrTf[j] * InpMult;

      double hi = iHigh(NULL,  InpTF, j);
      double lo = iLow(NULL,   InpTF, j);
      double cl = iClose(NULL, InpTF, j);

      double hl2 = (hi + lo) * 0.5;
      double up  = hl2 + atrv;
      double dn  = hl2 - atrv;

      if(j == tf_bars - 2)
      {
         UpperTf[j] = up;
         LowerTf[j] = dn;
         TrendTf[j] = 0;
         OscTf[j]   = 0.0;
      }
      else
      {
         double cl_next = iClose(NULL, InpTF, j + 1);

         if(cl_next < UpperTf[j + 1])
            UpperTf[j] = MathMin(up, UpperTf[j + 1]);
         else
            UpperTf[j] = up;

         if(cl_next > LowerTf[j + 1])
            LowerTf[j] = MathMax(dn, LowerTf[j + 1]);
         else
            LowerTf[j] = dn;

         if(cl > UpperTf[j + 1])
            TrendTf[j] = 1;
         else if(cl < LowerTf[j + 1])
            TrendTf[j] = 0;
         else
            TrendTf[j] = TrendTf[j + 1];
      }

      SptTf[j] = TrendTf[j] * LowerTf[j] + (1 - TrendTf[j]) * UpperTf[j];

      double denom = UpperTf[j] - LowerTf[j];
      double osc   = 0.0;
      if(denom != 0.0)
         osc = (cl - SptTf[j]) / denom;

      if(osc > 1.0)  osc = 1.0;
      if(osc < -1.0) osc = -1.0;
      OscTf[j] = osc;
   }

   // 5) AMA + Hist on the selected timeframe
   for(int j = tf_bars - 2; j >= 0; j--)
   {
      double osc = OscTf[j];

      double alpha = (osc * osc) / InpLength;

      double amaNorm;
      if(j == tf_bars - 2)
         amaNorm = osc;
      else
      {
         double prevAmaNorm = amaNorm_tf[j + 1];
         amaNorm = prevAmaNorm + alpha * (osc - prevAmaNorm);
      }
      amaNorm_tf[j] = amaNorm;

      double diffNorm = osc - amaNorm;
      double k = 2.0 / (InpSmooth + 1.0);
      double histNorm;
      if(j == tf_bars - 2)
         histNorm = diffNorm;
      else
      {
         double prevHistNorm = histNorm_tf[j + 1];
         histNorm = prevHistNorm + k * (diffNorm - prevHistNorm);
      }
      histNorm_tf[j] = histNorm;
   }

   // 6) Projection onto the current chart
   int limit = rates_total - 1;

   for(int i = limit; i >= 0; i--)
   {
      int j = iBarShift(NULL, InpTF, time[i], true);
      if(j < 0 || j >= tf_bars)
      {
         MainUpBuffer[i] = EMPTY_VALUE;
         MainDnBuffer[i] = EMPTY_VALUE;
         HistBuffer[i]   = 0.0;
         AmaBuffer[i]    = 0.0;
         MarkBuffer[i]   = EMPTY_VALUE;
         continue;
      }

      double osc      = OscTf[j];
      double amaNorm  = amaNorm_tf[j];
      double histNorm = histNorm_tf[j];

      if(osc >= 0.0)
      {
         MainUpBuffer[i] = osc * 100.0;
         MainDnBuffer[i] = EMPTY_VALUE;
      }
      else
      {
         MainDnBuffer[i] = osc * 100.0;
         MainUpBuffer[i] = EMPTY_VALUE;
      }

      AmaBuffer[i]  = amaNorm  * 100.0;
      HistBuffer[i] = histNorm * 100.0;

      // Spt-cross marks (uses higher timeframe's trend / Spt)
      MarkBuffer[i] = EMPTY_VALUE;

      if(InpShowLb && i < rates_total - 2)
      {
         int j_next = iBarShift(NULL, InpTF, time[i + 1], true);
         if(j_next >= 0 && j_next < tf_bars)
         {
            double spt_cur  = SptTf[j];
            double spt_next = SptTf[j_next];

            bool crossUp = (close[i] > spt_cur  && close[i + 1] <= spt_next);
            bool crossDn = (close[i] < spt_cur  && close[i + 1] >= spt_next);

            if(crossUp)
               MarkBuffer[i] = -90.0;   // buy mark below
            else if(crossDn)
               MarkBuffer[i] = 90.0;    // sell mark above
         }
      }

      // 7) Osc color change: vertical lines and arrows
      if(i < rates_total - 2)
      {
         int j_next = iBarShift(NULL, InpTF, time[i + 1], true);
         if(j_next >= 0 && j_next < tf_bars)
         {
            double prevOsc = OscTf[j_next];
            int signPrev = (prevOsc > 0.0) ? 1 : ((prevOsc < 0.0) ? -1 : 0);
            int signCur  = (osc     > 0.0) ? 1 : ((osc     < 0.0) ? -1 : 0);

            if(signPrev != 0 && signCur != 0 && signPrev != signCur)
            {
               datetime t = time[i];

               // vertical line
               if(InpShowColorChangeLines)
               {
                  string vname = gPrefix + "ColorLine_" + IntegerToString(i) + "_" + IntegerToString((long)t);
                  if(ObjectFind(0, vname) < 0)
                  {
                     ObjectCreate(0, vname, OBJ_VLINE, 0, t, 0);
                     if(signCur > 0)
                     {
                        ObjectSetInteger(0, vname, OBJPROP_COLOR, InpVLineColorUp);
                        ObjectSetInteger(0, vname, OBJPROP_STYLE, InpVLineStyleUp);
                        ObjectSetInteger(0, vname, OBJPROP_WIDTH, InpVLineWidthUp);
                     }
                     else
                     {
                        ObjectSetInteger(0, vname, OBJPROP_COLOR, InpVLineColorDn);
                        ObjectSetInteger(0, vname, OBJPROP_STYLE, InpVLineStyleDn);
                        ObjectSetInteger(0, vname, OBJPROP_WIDTH, InpVLineWidthDn);
                     }
                  }
               }

               // arrow on the main chart
               if(InpShowColorChangeArrows)
               {
                  double arrowPrice;
                  if(signCur > 0)
                     arrowPrice = close[i] - InpChartArrowShiftPoints * _Point;
                  else
                     arrowPrice = close[i] + InpChartArrowShiftPoints * _Point;

                  string aname = gPrefix + "ColorArrow_" + IntegerToString(i) + "_" + IntegerToString((long)t);
                  if(ObjectFind(0, aname) < 0)
                  {
                     ObjectCreate(0, aname, OBJ_ARROW, 0, t, arrowPrice);
                     if(signCur > 0)
                     {
                        ObjectSetInteger(0, aname, OBJPROP_COLOR,     InpChartArrowColorUp);
                        ObjectSetInteger(0, aname, OBJPROP_ARROWCODE, InpChartArrowCodeUp);
                     }
                     else
                     {
                        ObjectSetInteger(0, aname, OBJPROP_COLOR,     InpChartArrowColorDn);
                        ObjectSetInteger(0, aname, OBJPROP_ARROWCODE, InpChartArrowCodeDn);
                     }
                  }
               }
            }
         }
      }
   }

   return(rates_total);
}
