//+------------------------------------------------------------------+
//|               Advanced_Color_MA_Angle_v4.mq5                     |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   1

// Rysowanie kolorowej linii
#property indicator_label1  "Color Angle MA"
#property indicator_type1   DRAW_COLOR_LINE
#property indicator_color1  clrGreen, clrRed, clrYellow
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

enum ENUM_EXTENDED_MA_TYPE
{
   MA_SMA,        // Simple Moving Average (SMA)
   MA_EMA,        // Exponential Moving Average (EMA)
   MA_SMMA,       // Smoothed Moving Average (SMMA)
   MA_LWMA,       // Linear Weighted Moving Average (LWMA)
   MA_DEMA,       // Double Exponential Moving Average (DEMA)
   MA_TEMA,       // Triple Exponential Moving Average (TEMA)
   MA_ZLEMA,      // Zero-Lag EMA
   MA_HULL,       // Hull Moving Average (HMA)
   MA_KAMA,       // Kaufman Adaptive Moving Average (KAMA)
   MA_ALMA,       // Arnaud Legoux Moving Average (ALMA)
   MA_JMA,        // Jurik Moving Average (JMA)
   MA_MCGINLEY    // McGinley Dynamic (MGD)
};

//--- Inputy
input group "=== Ustawienia Średniej ==="
input ENUM_EXTENDED_MA_TYPE InpMAType         = MA_JMA;      // Typ średniej kroczącej
input int                   InpMAPeriod       = 21;          // Okres głównej średniej
input ENUM_APPLIED_PRICE    InpAppliedPrice   = PRICE_CLOSE; // Ceny do obliczeń
input double                InpThresholdAngle = 10.0;        // Próg kąta w stopniach (+/-)
input double                InpSensitivity    = 1.0;         // Czułość nachylenia (skalowanie)

input group "=== Wygładzanie (Smoothing) ==="
input bool                  InpUseSmoothing   = false;       // Włącz dodatkowe wygładzanie
input int                   InpSmoothPeriod   = 3;           // Okres wygładzania (Smoothing)

input group "=== Parametry Specyficzne ==="
input double                InpJmaPhase       = 0.0;         // JMA Phase (-100 do +100)
input double                InpMcGinleyK      = 0.6;         // McGinley Constant K

input group "=== Alerty (Aktywna Świeca) ==="
input bool                  InpEnableAlerts   = true;        // Włącz alerty dla aktywnej świecy
input bool                  InpPopUpAlert     = true;        // Okno Pop-Up
input bool                  InpPushAlert      = false;       // Powiadomienie Push (telefon)
input bool                  InpEmailAlert     = false;       // Powiadomienie E-mail

//--- Bufory główne
double MABuffer[];
double ColorBuffer[];

//--- Bufory pomocnicze
double RawMABuffer[];
double RawHullBuffer[];

//--- Bufory dla bezbłędnego JMA
double JmaE0[], JmaE1[], JmaE2[];

//--- Handles
int maHandleMain = INVALID_HANDLE;

//--- Zmienne śledzące dla alertów na aktywnej świecy
datetime lastAlertCandleTime = 0;
int      lastAlertColor      = -1;

//+------------------------------------------------------------------+
//| WMA Helper                                                       |
//+------------------------------------------------------------------+
double CalculateWMA(const double &array[], int index, int period)
{
   if(index < period - 1) return array[index];
   
   double sum = 0.0;
   int weightSum = 0;
   
   for(int i = 0; i < period; i++)
   {
      int weight = period - i;
      sum += array[index - i] * weight;
      weightSum += weight;
   }
   
   return (weightSum > 0) ? sum / weightSum : array[index];
}

//+------------------------------------------------------------------+
//| ALMA Helper                                                      |
//+------------------------------------------------------------------+
double CalculateALMA(const double &price[], int index, int period, double offset = 0.85, double sigma = 6.0)
{
   if(index < period - 1) return price[index];
   
   double m = floor(offset * (period - 1));
   double s = period / sigma;
   double norm = 0.0;
   double sum = 0.0;

   for(int i = 0; i < period; i++)
   {
      double weight = MathExp(-((i - m) * (i - m)) / (2 * s * s));
      sum += price[index - (period - 1 - i)] * weight;
      norm += weight;
   }
   return (norm > 0) ? sum / norm : price[index];
}

//+------------------------------------------------------------------+
//| McGinley Dynamic Helper                                          |
//+------------------------------------------------------------------+
double CalculateMcGinley(const double &price[], int index, int period, double kFactor, const double &prevMGD[])
{
   if(index == 0) return price[0];
   
   double prevVal = prevMGD[index - 1];
   if(prevVal <= 0.0) prevVal = price[index - 1];

   double ratio = price[index] / prevVal;
   double ratio4 = MathPow(ratio, 4.0);
   
   double denominator = kFactor * period * ratio4;
   if(denominator == 0.0) return prevVal;

   return prevVal + (price[index] - prevVal) / denominator;
}

//+------------------------------------------------------------------+
//| Kąt nachylenia i kolor                                          |
//+------------------------------------------------------------------+
int GetAngleColorIndex(double currentMA, double prevMA, double threshold, double sensitivity)
{
   double diffInPoints = (currentMA - prevMA) / _Point;
   double slope = diffInPoints * sensitivity * 0.01;
   double angleDegrees = MathArctan(slope) * (180.0 / M_PI);

   if(angleDegrees > threshold)   return 0; // Zielony (Wzrostowy)
   if(angleDegrees < -threshold)  return 1; // Czerwony (Spadkowy)
   return 2;                                // Żółty (Brak trendu)
}

//+------------------------------------------------------------------+
//| OnInit                                                           |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, MABuffer, INDICATOR_DATA);
   SetIndexBuffer(1, ColorBuffer, INDICATOR_COLOR_INDEX);

   ENUM_MA_METHOD stdMethod = MODE_SMA;
   switch(InpMAType)
   {
      case MA_SMA:  stdMethod = MODE_SMA;  break;
      case MA_EMA:  stdMethod = MODE_EMA;  break;
      case MA_SMMA: stdMethod = MODE_SMMA; break;
      case MA_LWMA: stdMethod = MODE_LWMA; break;
      default: break;
   }

   if(InpMAType <= MA_LWMA)
   {
      maHandleMain = iMA(_Symbol, _Period, InpMAPeriod, 0, stdMethod, InpAppliedPrice);
   }
   else if(InpMAType == MA_DEMA)
   {
      maHandleMain = iDEMA(_Symbol, _Period, InpMAPeriod, 0, InpAppliedPrice);
   }
   else if(InpMAType == MA_TEMA)
   {
      maHandleMain = iTEMA(_Symbol, _Period, InpMAPeriod, 0, InpAppliedPrice);
   }
   else if(InpMAType == MA_KAMA)
   {
      maHandleMain = iAMA(_Symbol, _Period, InpMAPeriod, 2, 30, 0, InpAppliedPrice);
   }

   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpMAPeriod + InpSmoothPeriod);
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 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[])
{
   if(rates_total < InpMAPeriod + InpSmoothPeriod + 2) return(0);

   int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;
   ArrayResize(RawMABuffer, rates_total);

   //--- 1. Obliczanie bazowej MA
   if(InpMAType <= MA_TEMA || InpMAType == MA_KAMA)
   {
      if(CopyBuffer(maHandleMain, 0, 0, rates_total, RawMABuffer) <= 0) return(0);
   }
   else if(InpMAType == MA_HULL)
   {
      int halfPeriod = (int)MathMax(1, InpMAPeriod / 2);
      int sqrtPeriod = (int)MathMax(1, MathSqrt(InpMAPeriod));

      ArrayResize(RawHullBuffer, rates_total);

      for(int i = start; i < rates_total; i++)
      {
         double wmaHalf = CalculateWMA(close, i, halfPeriod);
         double wmaFull = CalculateWMA(close, i, InpMAPeriod);
         RawHullBuffer[i] = (2.0 * wmaHalf) - wmaFull;
      }

      for(int i = start; i < rates_total; i++)
      {
         RawMABuffer[i] = CalculateWMA(RawHullBuffer, i, sqrtPeriod);
      }
   }
   else if(InpMAType == MA_JMA)
   {
      ArrayResize(JmaE0, rates_total);
      ArrayResize(JmaE1, rates_total);
      ArrayResize(JmaE2, rates_total);

      double beta = 0.45 * (InpMAPeriod - 1) / (0.45 * (InpMAPeriod - 1) + 2.0);
      double alpha = MathPow(beta, 2.0);
      double pFactor = (InpJmaPhase < -100) ? -0.5 : (InpJmaPhase > 100) ? 0.5 : InpJmaPhase / 200.0;

      for(int i = start; i < rates_total; i++)
      {
         if(i == 0)
         {
            JmaE0[0] = close[0];
            JmaE1[0] = 0.0;
            JmaE2[0] = 0.0;
            RawMABuffer[0] = close[0];
            continue;
         }

         JmaE0[i] = (1.0 - beta) * close[i] + beta * JmaE0[i-1];
         JmaE1[i] = (close[i] - JmaE0[i]) * (1.0 - beta) + beta * JmaE1[i-1];
         JmaE2[i] = (JmaE0[i] + JmaE1[i] - RawMABuffer[i-1]) * MathPow(1.0 - alpha, 2.0) + alpha * alpha * JmaE2[i-1];

         RawMABuffer[i] = JmaE2[i] + RawMABuffer[i-1] + pFactor * (JmaE1[i] - JmaE2[i]);
      }
   }
   else
   {
      for(int i = start; i < rates_total; i++)
      {
         if(InpMAType == MA_ALMA)
         {
            RawMABuffer[i] = CalculateALMA(close, i, InpMAPeriod);
         }
         else if(InpMAType == MA_ZLEMA)
         {
            int lag = (InpMAPeriod - 1) / 2;
            double priceLag = (i >= lag) ? 2 * close[i] - close[i - lag] : close[i];
            double alpha = 2.0 / (InpMAPeriod + 1.0);
            RawMABuffer[i] = (i > 0) ? (priceLag * alpha) + (RawMABuffer[i-1] * (1.0 - alpha)) : close[i];
         }
         else if(InpMAType == MA_MCGINLEY)
         {
            RawMABuffer[i] = CalculateMcGinley(close, i, InpMAPeriod, InpMcGinleyK, RawMABuffer);
         }
      }
   }

   //--- 2. Wygładzanie (Smoothing)
   if(InpUseSmoothing && InpSmoothPeriod > 1)
   {
      double alphaSmooth = 2.0 / (InpSmoothPeriod + 1.0);
      for(int i = start; i < rates_total; i++)
      {
         if(i == 0) MABuffer[i] = RawMABuffer[i];
         else MABuffer[i] = (RawMABuffer[i] * alphaSmooth) + (MABuffer[i-1] * (1.0 - alphaSmooth));
      }
   }
   else
   {
      ArrayCopy(MABuffer, RawMABuffer, 0, 0, rates_total);
   }

   //--- 3. Przypisywanie kolorów na podstawie kąta
   int calcStart = (start < 1) ? 1 : start;
   for(int i = calcStart; i < rates_total; i++)
   {
      ColorBuffer[i] = GetAngleColorIndex(MABuffer[i], MABuffer[i-1], InpThresholdAngle, InpSensitivity);
   }

   //--- 4. Obliczanie alertów na aktywnej świecy
   if(InpEnableAlerts)
   {
      CheckActiveCandleAlerts(rates_total, time);
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Funkcja obsługująca alerty na żywej (aktywnej) świecy           |
//+------------------------------------------------------------------+
void CheckActiveCandleAlerts(int rates_total, const datetime &time[])
{
   int activeBar = rates_total - 1;
   if(activeBar < 1) return;

   int currentColor = (int)ColorBuffer[activeBar];

   // Ignorujemy kolor żółty (indeks 2)
   if(currentColor != 0 && currentColor != 1)
      return;

   // Sprawdzamy, czy alert dla tego koloru nie został już wysłany na obecnej świecy
   if(lastAlertCandleTime == time[activeBar] && lastAlertColor == currentColor)
      return;

   // Wytworzenie wiadomości
   string colorText = (currentColor == 0) ? "ZIELONY (Bullish)" : "CZERWONY (Bearish)";
   string msg = StringFormat("[%s | %s] Alert średniej: Kolor zmienił się na %s!", 
                             _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period), colorText);

   // Zapamiętanie opublikowanego alertu
   lastAlertCandleTime = time[activeBar];
   lastAlertColor      = currentColor;

   // Powiadomienia
   if(InpPopUpAlert) Alert(msg);
   if(InpPushAlert)  SendNotification(msg);
   if(InpEmailAlert) SendMail(_Symbol + " - Active Candle MA Alert", msg);
}