//+------------------------------------------------------------------+
//|                                      Colored moving averages.mq5 |
//+------------------------------------------------------------------+
#property copyright "Mladen"
#property link      "http://www.forex-tsd.com"
#property version   "1.20"   // Added background coloring

#property indicator_chart_window
#property indicator_buffers 10
#property indicator_plots   3    // MA1 + MA2 + Candles

#property indicator_label1  "Fast MA"
#property indicator_type1   DRAW_COLOR_LINE
#property indicator_color1  Red
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

#property indicator_label2  "Slow MA"
#property indicator_type2   DRAW_COLOR_LINE
#property indicator_color2  Blue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

#property indicator_label3  "MA Candles"
#property indicator_type3   DRAW_COLOR_CANDLES
#property indicator_color3  Red
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//--- Inputs
input int                inpLength      = 14;              // Fast MA Period
input int                inpSlowLength  = 34;              // Slow MA Period
input ENUM_APPLIED_PRICE Price          = PRICE_CLOSE;     // Applied price
input ENUM_MA_METHOD     Method         = MODE_EMA;        // Moving average method
input ENUM_TIMEFRAMES    CandleColorTF  = PERIOD_CURRENT;  // Candle coloring timeframe
input color              ColorFrom      = Lime;            // MA up color
input color              ColorTo        = DeepPink;        // MA down color
input int                MaxAngle       = 20;              // Angle threshold for color steps
input bool               ColorCandles   = true;            // Color price candles?
// --- Background coloring ---
input bool               ColorBackground = false;          // Color chart background?
input color              BgColorUp       = C'20,30,20';    // Background: up (green-ish)
input color              BgColorDown     = C'30,20,20';    // Background: down (red-ish)
input int                BgMaxBars       = 500;            // Max bars for background coloring

//--- Buffers
double maBuffer[];
double maColors[];
double maSlowBuffer[];
double maSlowColors[];
double candleOpen[];
double candleHigh[];
double candleLow[];
double candleClose[];
double candleColors[];
double atrBuffer[];

int maHandle;
int maSlowHandle;
int atrHandle;
int candleMaHandle;
int candleAtrHandle;

#define angleBars   6
#define atrBars     100
#define Pi          3.141592653589793238462643

//+------------------------------------------------------------------+
//| Delete all background rectangle objects created by this indicator|
//+------------------------------------------------------------------+
void DeleteBackgroundObjects()
{
   int total = ObjectsTotal(0, 0, OBJ_RECTANGLE);
   for(int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i, 0, OBJ_RECTANGLE);
      if(StringFind(name, "MABg_") == 0)
         ObjectDelete(0, name);
   }
}

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   DeleteBackgroundObjects();

   SetIndexBuffer(0, maBuffer,      INDICATOR_DATA);         ArraySetAsSeries(maBuffer,     true);
   SetIndexBuffer(1, maColors,      INDICATOR_COLOR_INDEX);  ArraySetAsSeries(maColors,     true);
   SetIndexBuffer(2, maSlowBuffer,  INDICATOR_DATA);         ArraySetAsSeries(maSlowBuffer, true);
   SetIndexBuffer(3, maSlowColors,  INDICATOR_COLOR_INDEX);  ArraySetAsSeries(maSlowColors, true);
   SetIndexBuffer(4, candleOpen,    INDICATOR_DATA);         ArraySetAsSeries(candleOpen,   true);
   SetIndexBuffer(5, candleHigh,    INDICATOR_DATA);         ArraySetAsSeries(candleHigh,   true);
   SetIndexBuffer(6, candleLow,     INDICATOR_DATA);         ArraySetAsSeries(candleLow,    true);
   SetIndexBuffer(7, candleClose,   INDICATOR_DATA);         ArraySetAsSeries(candleClose,  true);
   SetIndexBuffer(8, candleColors,  INDICATOR_COLOR_INDEX);  ArraySetAsSeries(candleColors, true);
   SetIndexBuffer(9, atrBuffer,     INDICATOR_CALCULATIONS); ArraySetAsSeries(atrBuffer,    true);

   int iLength     = (inpLength     > 0) ? inpLength     : 1;
   int iSlowLength = (inpSlowLength > 0) ? inpSlowLength : 34;

   maHandle        = iMA(NULL, 0,            iLength,     0, Method, Price);
   maSlowHandle    = iMA(NULL, 0,            iSlowLength, 0, Method, Price);
   atrHandle       = iATR(NULL, 0,           atrBars);
   candleMaHandle  = iMA(NULL, CandleColorTF,iLength,     0, Method, Price);
   candleAtrHandle = iATR(NULL, CandleColorTF,atrBars);

   if(maHandle == INVALID_HANDLE || maSlowHandle == INVALID_HANDLE ||
      atrHandle == INVALID_HANDLE || candleMaHandle == INVALID_HANDLE ||
      candleAtrHandle == INVALID_HANDLE)
   {
      Print("Error creating indicator handles");
      return(INIT_FAILED);
   }

   PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, 20);
   PlotIndexSetInteger(1, PLOT_COLOR_INDEXES, 20);
   PlotIndexSetInteger(2, PLOT_COLOR_INDEXES, 20);

   for(int i = 0; i < 20; i++)
   {
      color c = gradientColor(i, 20, ColorTo, ColorFrom);
      PlotIndexSetInteger(0, PLOT_LINE_COLOR, i, c);
      PlotIndexSetInteger(1, PLOT_LINE_COLOR, i, c);
      PlotIndexSetInteger(2, PLOT_LINE_COLOR, i, c);
   }

   if(!ColorCandles)
      PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE);
   else
      PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   DeleteBackgroundObjects();
   IndicatorRelease(maHandle);
   IndicatorRelease(maSlowHandle);
   IndicatorRelease(atrHandle);
   IndicatorRelease(candleMaHandle);
   IndicatorRelease(candleAtrHandle);
}

//+------------------------------------------------------------------+
//| Main 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[])
{
   int limit = rates_total - prev_calculated;
   if(prev_calculated > 0) limit++;
   if(prev_calculated == 0) limit -= (angleBars + 1);

   if(!checkCalculated(maHandle,     rates_total, "Fast MA"))  return(prev_calculated);
   if(!checkCalculated(maSlowHandle, rates_total, "Slow MA"))  return(prev_calculated);
   if(!checkCalculated(atrHandle,    rates_total, "ATR"))      return(prev_calculated);

   if(!doCopy(maHandle,     maBuffer,     0, limit, "Fast MA"))  return(prev_calculated);
   if(!doCopy(maSlowHandle, maSlowBuffer, 0, limit, "Slow MA"))  return(prev_calculated);
   if(!doCopy(atrHandle,    atrBuffer,    0, limit, "ATR"))      return(prev_calculated);

   // Candle TF data
   double candleMaBuffer[], candleAtrBuffer[];
   bool useSelectedCandleTF = (CandleColorTF != PERIOD_CURRENT);

   if((ColorCandles || ColorBackground) && useSelectedCandleTF)
   {
      int oldestShift = iBarShift(_Symbol, CandleColorTF, time[0], false);
      if(oldestShift < 0) return(prev_calculated);

      int copyCount = oldestShift + angleBars + 2;
      int candleBars = Bars(_Symbol, CandleColorTF);
      if(copyCount > candleBars) copyCount = candleBars;

      ArraySetAsSeries(candleMaBuffer,  true);
      ArraySetAsSeries(candleAtrBuffer, true);

      if(!checkCalculated(candleMaHandle,  copyCount, "candle MA"))  return(prev_calculated);
      if(!checkCalculated(candleAtrHandle, copyCount, "candle ATR")) return(prev_calculated);
      if(!doCopy(candleMaHandle,  candleMaBuffer,  0, copyCount, "candle MA"))  return(prev_calculated);
      if(!doCopy(candleAtrHandle, candleAtrBuffer, 0, copyCount, "candle ATR")) return(prev_calculated);
   }

   //--- Main loop: MA colors + candle colors
   for(int i = limit; i >= 0; i--)
   {
      maColors[i]     = slopeColor(i, maBuffer);
      maSlowColors[i] = slopeColor(i, maSlowBuffer);

      if(ColorCandles || ColorBackground)
      {
         int barIndex = rates_total - 1 - i;
         if(barIndex >= 0 && barIndex < rates_total)
         {
            candleOpen[i]  = open[barIndex];
            candleHigh[i]  = high[barIndex];
            candleLow[i]   = low[barIndex];
            candleClose[i] = close[barIndex];

            if(useSelectedCandleTF)
            {
               int shift = iBarShift(_Symbol, CandleColorTF, time[barIndex], false);
               candleColors[i] = slopeColorFromBuffers(candleMaBuffer, candleAtrBuffer, shift);
            }
            else
               candleColors[i] = maColors[i];
         }
      }
   }

   //--- Background coloring
   if(ColorBackground)
   {
      int maxBars = (BgMaxBars > 0) ? BgMaxBars : 2000;
      int bgLimit = (limit > maxBars) ? maxBars : limit;
      if(bgLimit < 0) bgLimit = 0;

      for(int i = bgLimit; i >= 0; i--)
      {
         int barIndex = rates_total - 1 - i;
         if(barIndex < 0 || barIndex >= rates_total) continue;

         datetime time1 = time[barIndex];
         datetime time2 = time1 + PeriodSeconds(_Period);

         // candleColors[i] holds a 0-19 gradient index.
         // Index 0 = full "down" color (ColorTo), index 19 = full "up" color (ColorFrom).
         // Map the same 0-19 step to a background gradient between BgColorDown and BgColorUp.
         int colorIdx = (int)candleColors[i];
         color bgCol  = gradientColor(colorIdx, 20, BgColorDown, BgColorUp);

         string rectName = "MABg_" + TimeToString(time1);

         if(ObjectFind(0, rectName) < 0)
         {
            if(ObjectCreate(0, rectName, OBJ_RECTANGLE, 0,
                            time1,  2000000000.0,
                            time2, -2000000000.0))
            {
               ObjectSetInteger(0, rectName, OBJPROP_COLOR,      bgCol);
               ObjectSetInteger(0, rectName, OBJPROP_FILL,       true);
               ObjectSetInteger(0, rectName, OBJPROP_BACK,       true);
               ObjectSetInteger(0, rectName, OBJPROP_SELECTABLE, false);
               ObjectSetInteger(0, rectName, OBJPROP_SELECTED,   false);
               ObjectSetInteger(0, rectName, OBJPROP_HIDDEN,     true);
            }
         }
         else
         {
            // Update color if it changed (e.g. live bar)
            if((color)ObjectGetInteger(0, rectName, OBJPROP_COLOR) != bgCol)
               ObjectSetInteger(0, rectName, OBJPROP_COLOR, bgCol);

            // Keep time coordinates in sync
            if((datetime)ObjectGetInteger(0, rectName, OBJPROP_TIME, 0) != time1)
               ObjectSetInteger(0, rectName, OBJPROP_TIME, 0, time1);
            if((datetime)ObjectGetInteger(0, rectName, OBJPROP_TIME, 1) != time2)
               ObjectSetInteger(0, rectName, OBJPROP_TIME, 1, time2);
         }
      }

      // Remove background objects for bars older than BgMaxBars
      if(limit > maxBars)
      {
         int oldestBarIdx = rates_total - 1 - maxBars;
         if(oldestBarIdx >= 0 && oldestBarIdx < rates_total)
         {
            datetime oldestTime = time[oldestBarIdx];
            int total = ObjectsTotal(0, 0, OBJ_RECTANGLE);
            for(int idx = total - 1; idx >= 0; idx--)
            {
               string name = ObjectName(0, idx, 0, OBJ_RECTANGLE);
               if(StringFind(name, "MABg_") == 0)
               {
                  datetime objTime = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
                  if(objTime < oldestTime)
                     ObjectDelete(0, name);
               }
            }
         }
      }
   }
   else
   {
      // Background was turned off — clean up any existing objects
      DeleteBackgroundObjects();
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Slope Color Functions                                            |
//+------------------------------------------------------------------+
int slopeColor(int i, const double &maArray[])
{
   double range  = atrBuffer[i];
   double angle  = 0.0;
   double change = maArray[i] - maArray[i + angleBars];

   if(range != 0)
      angle = MathArctan(change / (range * angleBars)) * 180.0 / Pi;

   int theColor = (int)round((angle + MaxAngle) / (MaxAngle / 10.0));
   theColor = (theColor >= 0) ? ((theColor < 20) ? theColor : 19) : 0;
   return(theColor);
}

int slopeColorFromBuffers(const double &maValues[], const double &atrValues[], int shift)
{
   if(shift < 0 || shift + angleBars >= ArraySize(maValues) || shift >= ArraySize(atrValues))
      return(10);

   double range  = atrValues[shift];
   double angle  = 0.0;
   double change = maValues[shift] - maValues[shift + angleBars];

   if(range != 0)
      angle = MathArctan(change / (range * angleBars)) * 180.0 / Pi;

   int theColor = (int)round((angle + MaxAngle) / (MaxAngle / 10.0));
   theColor = (theColor >= 0) ? ((theColor < 20) ? theColor : 19) : 0;
   return(theColor);
}

//+------------------------------------------------------------------+
//| Gradient color helpers                                           |
//+------------------------------------------------------------------+
color gradientColor(int step, int totalSteps, color from, color to)
{
   color newBlue  = getColor(step,totalSteps,(from & 0XFF0000)>>16,(to & 0XFF0000)>>16)<<16;
   color newGreen = getColor(step,totalSteps,(from & 0X00FF00)>> 8,(to & 0X00FF00)>> 8) <<8;
   color newRed   = getColor(step,totalSteps,(from & 0X0000FF)    ,(to & 0X0000FF)    )    ;
   return(newBlue + newGreen + newRed);
}

color getColor(int stepNo, int totalSteps, color from, color to)
{
   double step = (from - to) / (totalSteps - 1.0);
   return((color)round(from - step * stepNo));
}

//+------------------------------------------------------------------+
//| Helper functions                                                 |
//+------------------------------------------------------------------+
bool checkCalculated(int bufferHandle, int total, string checkDescription)
{
   int calculated = BarsCalculated(bufferHandle);
   if(calculated < total)
   {
      Print("Not all data of " + checkDescription + " calculated (", (string)(total - calculated), " un-calculated bars )");
      return(false);
   }
   return(true);
}

bool doCopy(const int bufferHandle, double& buffer[], const int buffNum, const int copyCount, string copyDescription)
{
   if(CopyBuffer(bufferHandle, buffNum, 0, copyCount, buffer) <= 0)
   {
      Print("Getting " + copyDescription + " failed! Error", GetLastError());
      return(false);
   }
   return(true);
}
