//+------------------------------------------------------------------+
//|                  Bollinger Bands Xtra Smooth (Non-Repainting)   |
//+------------------------------------------------------------------+
#property copyright "mladen - remixed by Ptr777 "
#property version   "1.0"

#property indicator_chart_window
#property indicator_buffers    12
#property indicator_color1     clrAqua     // Upper line bullish
#property indicator_color2     clrRed      // Upper line bearish
#property indicator_color3     clrAqua     // Center line bullish
#property indicator_color4     clrRed      // Center line bearish
#property indicator_color5     clrAqua     // Lower line bullish
#property indicator_color6     clrRed      // Lower line bearish
#property indicator_color7     clrNONE
#property indicator_color8     clrNONE
#property indicator_color9     clrNONE
#property indicator_color10    clrNONE
#property indicator_color11    clrNONE
#property indicator_color12    clrNONE
#property indicator_style3     STYLE_DOT
#property indicator_style4     STYLE_DOT

// Input parameters
string TimeFrame         = "current time frame";
extern ENUM_APPLIED_PRICE Price = PRICE_CLOSE;
extern int    BB_Period         = 20;           // Bollinger Bands period
extern int    BB_Shift          = 0;            // Bollinger Bands shift
extern double BB_Deviation      = 2.0;          // Bollinger Bands standard deviations
extern int    BB_Method         = 0;            // 0=SMA, 1=EMA, 2=SMMA, 3=LWMA

// Smoothing parameters
extern bool   Enable_Smoothing  = true;
extern int    Smoothing_Type    = 1;            // 1=SMA, 2=EMA, 3=LSMA, 4=DEMA
extern int    Smoothing_Period  = 20;
extern bool   Interpolate       = true;

// Alert settings
extern bool   Alerts_Enabled    = false;
extern bool   Alerts_On_Current = false;
extern bool   Alerts_On_HighLow = false;
extern bool   Alert_Message     = false;
extern bool   Alert_Sound       = false;
extern bool   Alert_Email       = false;
extern bool   Alert_Notification = false;

// Indicator buffers
double Bullish_Upper_Buffer[], Bearish_Upper_Buffer[];
double Bullish_Center_Buffer[], Bearish_Center_Buffer[];
double Bullish_Lower_Buffer[], Bearish_Lower_Buffer[];
double Calculated_Upper_Buffer[], Calculated_Lower_Buffer[], Calculated_Center_Buffer[];
double Trend_Direction_Buffer[];
double Smoothing_Buffer1[], Smoothing_Buffer2[], Smoothing_Buffer3[];
double BaseUpper_Buffer[], BaseLower_Buffer[];

string Indicator_Name;
bool   Calculate_Value_Only;
bool   Return_Bars_Only;
int    Selected_TimeFrame;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   IndicatorBuffers(15);
   BB_Period = MathMax(BB_Period, 2);
   Smoothing_Period = MathMax(Smoothing_Period, 1);

   // Set visible buffers - FIXED BUFFER INDICES
   SetIndexBuffer(0, Bullish_Upper_Buffer); SetIndexDrawBegin(0, BB_Period + Smoothing_Period * 3);
   SetIndexBuffer(1, Bearish_Upper_Buffer); SetIndexDrawBegin(1, BB_Period + Smoothing_Period * 3);
   SetIndexBuffer(2, Bullish_Center_Buffer); SetIndexDrawBegin(2, BB_Period + Smoothing_Period * 3);
   SetIndexBuffer(3, Bearish_Center_Buffer); SetIndexDrawBegin(3, BB_Period + Smoothing_Period * 3);
   SetIndexBuffer(4, Bullish_Lower_Buffer); SetIndexDrawBegin(4, BB_Period + Smoothing_Period * 3);
   SetIndexBuffer(5, Bearish_Lower_Buffer); SetIndexDrawBegin(5, BB_Period + Smoothing_Period * 3);

   // Hidden calculation buffers
   SetIndexBuffer(6, Calculated_Upper_Buffer);
   SetIndexBuffer(7, Calculated_Lower_Buffer);
   SetIndexBuffer(8, Calculated_Center_Buffer);
   SetIndexBuffer(9, Trend_Direction_Buffer);
   
   // Smoothing buffers
   SetIndexBuffer(10, Smoothing_Buffer1);
   SetIndexBuffer(11, Smoothing_Buffer2);
   SetIndexBuffer(12, Smoothing_Buffer3);
   
   // Base Bollinger Bands buffers
   SetIndexBuffer(13, BaseUpper_Buffer);
   SetIndexBuffer(14, BaseLower_Buffer);

   // Line styles and thickness
   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 3); // Bullish upper
   SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 3); // Bearish upper
   SetIndexStyle(2, DRAW_LINE, STYLE_DOT, 2);   // Bullish center
   SetIndexStyle(3, DRAW_LINE, STYLE_DOT, 2);   // Bearish center
   SetIndexStyle(4, DRAW_LINE, STYLE_SOLID, 3); // Bullish lower
   SetIndexStyle(5, DRAW_LINE, STYLE_SOLID, 3); // Bearish lower

   Indicator_Name = WindowExpertName();
   Return_Bars_Only = (TimeFrame == "returnBars");
   Calculate_Value_Only = (TimeFrame == "calculateValue");
   Selected_TimeFrame = StringToTimeFrame(TimeFrame);
   
   if (!Return_Bars_Only && !Calculate_Value_Only)
   {
      IndicatorShortName(" Bollinger Bands Xtra Smooth (" + BB_Period + ", " + DoubleToString(BB_Deviation, 1) + ")");
   }
   
   return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   int counted_bars = IndicatorCounted();
   int i, limit;
   
   // Check for data errors
   if(counted_bars < 0) return(-1);
   
   // Adjust limit for calculation
   if(counted_bars > 0) counted_bars--;
   limit = MathMin(Bars - 1, Bars - counted_bars + BB_Period + Smoothing_Period * 3);
   
   if (Return_Bars_Only)
   {
      Bullish_Center_Buffer[0] = limit + 1;
      return(0);
   }
   
   if (Calculate_Value_Only || Selected_TimeFrame == Period())
   {
      // Calculate standard Bollinger Bands
      for (i = limit; i >= 0; i--)
      {
         // Get the standard Bollinger Bands values
         double upper_band = iBands(NULL, 0, BB_Period, BB_Deviation, BB_Shift, Price, MODE_UPPER, i);
         double lower_band = iBands(NULL, 0, BB_Period, BB_Deviation, BB_Shift, Price, MODE_LOWER, i);
         double middle_band = iBands(NULL, 0, BB_Period, BB_Deviation, BB_Shift, Price, MODE_MAIN, i);
         
         // Store base calculations
         BaseUpper_Buffer[i] = upper_band;
         BaseLower_Buffer[i] = lower_band;
         Calculated_Center_Buffer[i] = middle_band;
         Calculated_Upper_Buffer[i] = upper_band;
         Calculated_Lower_Buffer[i] = lower_band;
      }
      
      // Apply smart cascading smoothing if enabled
      if (Enable_Smoothing)
      {
         ApplyCascadingSmoothing(limit);
      }
      
      // Apply synchronized colors based on smoothed trend
      for (i = limit; i >= 0; i--)
      {
         // For non-repainting: only show confirmed values
         // Current bar (index 0) is not confirmed until it closes
         if (i > 0)
         {
            // Determine trend direction based on comparison with previous bar
            bool is_bullish = Calculated_Center_Buffer[i] > Calculated_Center_Buffer[i + 1];
            Trend_Direction_Buffer[i] = (is_bullish ? 1 : -1);
            
            // Apply colors based on trend
            if (is_bullish)
            {
               // Bullish trend - show aqua lines
               Bullish_Upper_Buffer[i] = Calculated_Upper_Buffer[i];
               Bearish_Upper_Buffer[i] = EMPTY_VALUE;
               
               Bullish_Center_Buffer[i] = Calculated_Center_Buffer[i];
               Bearish_Center_Buffer[i] = EMPTY_VALUE;
               
               Bullish_Lower_Buffer[i] = Calculated_Lower_Buffer[i];
               Bearish_Lower_Buffer[i] = EMPTY_VALUE;
            }
            else // Bearish trend
            {
               // Bearish trend - show red lines
               Bearish_Upper_Buffer[i] = Calculated_Upper_Buffer[i];
               Bullish_Upper_Buffer[i] = EMPTY_VALUE;
               
               Bearish_Center_Buffer[i] = Calculated_Center_Buffer[i];
               Bullish_Center_Buffer[i] = EMPTY_VALUE;
               
               Bearish_Lower_Buffer[i] = Calculated_Lower_Buffer[i];
               Bullish_Lower_Buffer[i] = EMPTY_VALUE;
            }
         }
         else
         {
            // Clear buffers for current (unconfirmed) bar
            // This prevents repainting by not showing values for bar 0
            Bullish_Upper_Buffer[i] = EMPTY_VALUE;
            Bearish_Upper_Buffer[i] = EMPTY_VALUE;
            Bullish_Center_Buffer[i] = EMPTY_VALUE;
            Bearish_Center_Buffer[i] = EMPTY_VALUE;
            Bullish_Lower_Buffer[i] = EMPTY_VALUE;
            Bearish_Lower_Buffer[i] = EMPTY_VALUE;
            Trend_Direction_Buffer[i] = 0;
         }
      }
      
      if (!Calculate_Value_Only) ManageAlerts();
      return(0);
   }
   
   ManageAlerts();
   return(0);
}

//+------------------------------------------------------------------+
//| Apply cascading smoothing to bands                              |
//+------------------------------------------------------------------+
void ApplyCascadingSmoothing(int limit)
{
   int i;
   
   // First smoothing pass on center line
   for(i = limit; i >= 0; i--)
   {
      Smoothing_Buffer1[i] = ApplySmoothing(Calculated_Center_Buffer, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Second smoothing pass (smoothing the smoothed values)
   for(i = limit; i >= 0; i--)
   {
      Smoothing_Buffer2[i] = ApplySmoothing(Smoothing_Buffer1, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Third smoothing pass for ultra-smooth result
   for(i = limit; i >= 0; i--)
   {
      Smoothing_Buffer3[i] = ApplySmoothing(Smoothing_Buffer2, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Smooth upper band separately
   double smoothed_upper[];
   ArrayResize(smoothed_upper, Bars);
   ArrayInitialize(smoothed_upper, 0);
   
   for(i = limit; i >= 0; i--)
   {
      smoothed_upper[i] = ApplySmoothing(BaseUpper_Buffer, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Second pass smoothing for upper band
   for(i = limit; i >= 0; i--)
   {
      smoothed_upper[i] = ApplySmoothing(smoothed_upper, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Third pass smoothing for upper band
   for(i = limit; i >= 0; i--)
   {
      smoothed_upper[i] = ApplySmoothing(smoothed_upper, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Smooth lower band separately
   double smoothed_lower[];
   ArrayResize(smoothed_lower, Bars);
   ArrayInitialize(smoothed_lower, 0);
   
   for(i = limit; i >= 0; i--)
   {
      smoothed_lower[i] = ApplySmoothing(BaseLower_Buffer, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Second pass smoothing for lower band
   for(i = limit; i >= 0; i--)
   {
      smoothed_lower[i] = ApplySmoothing(smoothed_lower, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Third pass smoothing for lower band
   for(i = limit; i >= 0; i--)
   {
      smoothed_lower[i] = ApplySmoothing(smoothed_lower, i, Smoothing_Type, Smoothing_Period);
   }
   
   // Apply smoothed values to all buffers
   for(i = limit; i >= 0; i--)
   {
      // Use the ultra-smoothed center line
      double smoothed_center = Smoothing_Buffer3[i];
      
      // Update all buffers with smoothed values
      Calculated_Center_Buffer[i] = smoothed_center;
      Calculated_Upper_Buffer[i] = smoothed_upper[i];
      Calculated_Lower_Buffer[i] = smoothed_lower[i];
   }
}

//+------------------------------------------------------------------+
//| Apply specific smoothing type                                   |
//+------------------------------------------------------------------+
double ApplySmoothing(double &buffer[], int index, int type, int period)
{
   if (index >= Bars - period) return buffer[index];
   
   switch(type)
   {
      case 1: // Simple Moving Average
         return CalculateSMA(buffer, index, period);
         
      case 2: // Exponential Moving Average
         return CalculateEMA(buffer, index, period);
         
      case 3: // Least Squares Moving Average
         return CalculateLSMA(buffer, index, period);
         
      case 4: // Double Exponential Moving Average
         return CalculateDEMA(buffer, index, period);
         
      default:
         return buffer[index];
   }
}

//+------------------------------------------------------------------+
//| Calculate Simple Moving Average                                 |
//+------------------------------------------------------------------+
double CalculateSMA(double &buffer[], int index, int period)
{
   double sum = 0;
   int count = 0;
   
   for(int i = 0; i < period; i++)
   {
      if (index + i < ArraySize(buffer))
      {
         sum += buffer[index + i];
         count++;
      }
   }
   
   if (count > 0)
      return sum / count;
   else
      return buffer[index];
}

//+------------------------------------------------------------------+
//| Calculate Exponential Moving Average                            |
//+------------------------------------------------------------------+
double CalculateEMA(double &buffer[], int index, int period)
{
   if (period <= 0) return buffer[index];
   
   double k = 2.0 / (period + 1.0);
   
   // Initialize with SMA for the first value
   if (index >= Bars - 1 || index + period >= Bars)
   {
      return CalculateSMA(buffer, index, period);
   }
   
   // Calculate EMA recursively
   double ema_previous = 0;
   if (index + 1 < Bars)
   {
      // Calculate EMA for previous bar
      double sum_sma = 0;
      int count_sma = 0;
      for(int i = 1; i <= period; i++)
      {
         if (index + i < Bars)
         {
            sum_sma += buffer[index + i];
            count_sma++;
         }
      }
      if (count_sma > 0)
         ema_previous = sum_sma / count_sma;
      else
         ema_previous = buffer[index + 1];
   }
   else
   {
      ema_previous = buffer[index];
   }
   
   // Apply EMA formula
   return buffer[index] * k + ema_previous * (1.0 - k);
}

//+------------------------------------------------------------------+
//| Calculate Least Squares Moving Average                          |
//+------------------------------------------------------------------+
double CalculateLSMA(double &buffer[], int index, int period)
{
   if (index >= Bars - period || period <= 1) 
      return buffer[index];
   
   double sum_x = 0;
   double sum_y = 0;
   double sum_xy = 0;
   double sum_xx = 0;
   
   for(int i = 0; i < period; i++)
   {
      if (index + i >= Bars) break;
      
      sum_x += i;
      sum_y += buffer[index + i];
      sum_xy += i * buffer[index + i];
      sum_xx += i * i;
   }
   
   double denominator = period * sum_xx - sum_x * sum_x;
   if (MathAbs(denominator) < 0.00001) 
      return buffer[index];
   
   double slope = (period * sum_xy - sum_x * sum_y) / denominator;
   double intercept = (sum_y - slope * sum_x) / period;
   
   return intercept + slope * (period - 1);
}

//+------------------------------------------------------------------+
//| Calculate Double Exponential Moving Average                     |
//+------------------------------------------------------------------+
double CalculateDEMA(double &buffer[], int index, int period)
{
   if (index >= Bars - period * 2 || period <= 0) 
      return buffer[index];
   
   // Calculate first EMA
   double ema1 = CalculateEMA(buffer, index, period);
   
   // Create a temporary array for the EMA of EMA
   double ema1_buffer[];
   ArrayResize(ema1_buffer, Bars);
   ArrayInitialize(ema1_buffer, 0);
   
   // Fill the ema1_buffer with EMA values
   for(int i = index; i < MathMin(Bars, index + period * 2); i++)
   {
      ema1_buffer[i] = CalculateEMA(buffer, i, period);
   }
   
   // Calculate second EMA (EMA of EMA)
   double ema2 = CalculateEMA(ema1_buffer, index, period);
   
   // DEMA formula: 2*EMA - EMA(EMA)
   return 2 * ema1 - ema2;
}

//+------------------------------------------------------------------+
//| Manage alert triggers                                           |
//+------------------------------------------------------------------+
void ManageAlerts()
{
   if (!Alerts_Enabled) return;
   
   int alert_bar;
   if (Alerts_On_Current)
   {
      alert_bar = 0;
   }
   else
   {
      alert_bar = 1;
   }
   
   alert_bar = iBarShift(NULL, 0, iTime(NULL, Selected_TimeFrame, alert_bar));
   
   // For non-repainting alerts, only trigger on confirmed bars (index > 0)
   if (alert_bar > 0 && Trend_Direction_Buffer[alert_bar] != Trend_Direction_Buffer[alert_bar + 1])
   {
      if (Trend_Direction_Buffer[alert_bar] == 1)
         TriggerAlert(alert_bar, "bullish breakout");
      if (Trend_Direction_Buffer[alert_bar] == -1)
         TriggerAlert(alert_bar, "bearish breakout");
   }
}

//+------------------------------------------------------------------+
//| Trigger alert with anti-spam protection                         |
//+------------------------------------------------------------------+
void TriggerAlert(int bar_index, string alert_type)
{
   static string   previous_alert = "none";
   static datetime previous_alert_time;
   string message;
   
   if (previous_alert != alert_type || previous_alert_time != Time[bar_index])
   {
      previous_alert = alert_type;
      previous_alert_time = Time[bar_index];
      
      message = StringConcatenate(Symbol(), " at ", TimeToStr(TimeLocal(), TIME_SECONDS),
                                  " ", TimeFrameToString(Selected_TimeFrame),
                                  " Bollinger Bands price penetrated ", alert_type, " band");
      
      if (Alert_Message)      Alert(message);
      if (Alert_Email)        SendMail(StringConcatenate(Symbol(), " Bollinger Bands Xtra Smooth Alert"), message);
      if (Alert_Notification) SendNotification(message);
      if (Alert_Sound)        PlaySound("alert2.wav");
   }
}

//+------------------------------------------------------------------+
//| Convert timeframe string to integer                             |
//+------------------------------------------------------------------+
int StringToTimeFrame(string timeframe_string)
{
   string tf_upper = StringUpperCase(timeframe_string);
   string tf_table[] = {"M1", "M5", "M15", "M30", "H1", "H4", "D1", "W1", "MN"};
   int period_table[] = {1, 5, 15, 30, 60, 240, 1440, 10080, 43200};
   
   for (int i = ArraySize(period_table) - 1; i >= 0; i--)
   {
      if (tf_upper == tf_table[i] || tf_upper == "" + period_table[i])
      {
         return MathMax(period_table[i], Period());
      }
   }
   return Period();
}

//+------------------------------------------------------------------+
//| Convert integer timeframe to string                             |
//+------------------------------------------------------------------+
string TimeFrameToString(int timeframe)
{
   string tf_table[] = {"M1", "M5", "M15", "M30", "H1", "H4", "D1", "W1", "MN"};
   int period_table[] = {1, 5, 15, 30, 60, 240, 1440, 10080, 43200};
   
   for (int i = ArraySize(period_table) - 1; i >= 0; i--)
   {
      if (timeframe == period_table[i])
      {
         return tf_table[i];
      }
   }
   return "";
}

//+------------------------------------------------------------------+
//| Convert string to uppercase                                     |
//+------------------------------------------------------------------+
string StringUpperCase(string input_string)
{
   string output_string = input_string;
   int string_length = StringLen(input_string);
   
   for (int i = 0; i < string_length; i++)
   {
      int char_code = StringGetChar(output_string, i);
      
      // Convert lowercase letters to uppercase
      if (char_code >= 97 && char_code <= 122)
      {
         output_string = StringSetChar(output_string, i, char_code - 32);
      }
   }
   
   return output_string;
}