//+------------------------------------------------------------------+
//|                                                         alma.mq4 |
//|                                              converted by mladen |
//|                                               mladenfx@gmail.com |
//+------------------------------------------------------------------+
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
#property indicator_chart_window
#property indicator_buffers 10
#property indicator_color1 clrMediumSeaGreen //C'0,70,0' //filled ribbon uptrend
#property indicator_color2 clrCrimson //C'70,0,0' //filled ribbon downtrend
#property indicator_width1  3
#property indicator_width2  3

#property indicator_color3  clrSilver //clrLimeGreen //first ALMA
#property indicator_color4  clrSilver //clrMediumVioletRed
#property indicator_color5  clrOrange
#property indicator_color6  clrNONE

#property indicator_width3  2
#property indicator_width4  2
#property indicator_width5  1

#property indicator_color7  clrSilver //clrDarkGreen //second ALMA
#property indicator_color8  clrSilver //clrCrimson
#property indicator_color9  clrOrange
#property indicator_color10  clrNONE

#property indicator_width7  2
#property indicator_width8  2
#property indicator_width9  1

extern string             ALMAnumber1           = "------------------------------";
extern int                AlmaPeriod1           = 13;
extern double             AlmaSigma1            = 6.0;
extern double             AlmaSample1           = 0.25;
extern ENUM_APPLIED_PRICE AlmaPrice1            = PRICE_CLOSE;
extern string             ALMAnumber2           = "------------------------------";
extern int                AlmaPeriod2           = 21;
extern double             AlmaSigma2            = 6.0;
extern double             AlmaSample2           = 0.25;
extern ENUM_APPLIED_PRICE AlmaPrice2            = PRICE_CLOSE;

extern string             button_note1          = "------------------------------";
extern int                btn_Subwindow         = 0;                               // What window to put the button on.  If <0, the button will use the same sub-window as the indicator.
extern ENUM_BASE_CORNER   btn_corner            = CORNER_LEFT_UPPER;               // button corner on chart for anchoring
extern string             btn_text              = "ALMA1";                         // a button name
extern string             btn_Font              = "Arial";                         // button font name
extern int                btn_FontSize          = 9;                               // button font size               
extern color              btn_text_ON_color     = clrLime;                         // ON color when the button is turned on
extern color              btn_text_OFF_color    = clrRed;                          // OFF color when the button is turned off
extern color              btn_background_color  = clrDimGray;                      // background color of the button
extern color              btn_border_color      = clrBlack;                        // border color the button
extern int                button_x              = 20;                              // x coordinate of the button     
extern int                button_y              = 25;                              // y coordinate of the button     
extern int                btn_Width             = 80;                              // button width
extern int                btn_Height            = 20;                              // button height
extern string             button_note2          = "------------------------------";
bool show_data, recalc=false;
string IndicatorName, IndicatorObjPrefix, buttonId;

double Alma1[], Almada1[], Almadb1[], slope1[], coeffs1[], FilledRibbonUptrend[], FilledRibbonDowntrend[];
double Alma2[], Almada2[], Almadb2[], slope2[], coeffs2[];
//+------------------------------------------------------------------------------------------------------------------+
int OnInit()
{  
   IndicatorDigits(Digits);
   IndicatorObjPrefix = "__" + btn_text + "__";
     
   // The leading "_" gives buttonId a *unique* prefix.  Furthermore, prepending the swin is usually unique unless >2+ of THIS indy are displayed in the SAME sub-window. (But, if >2 used, be sure to shift the buttonId position)
   buttonId = "_" + IndicatorObjPrefix + "_BT_";
   if (ObjectFind(buttonId)<0) 
      createButton(buttonId, btn_text, btn_Width, btn_Height, btn_Font, btn_FontSize, btn_background_color, btn_border_color, btn_text_ON_color);
   ObjectSetInteger(0, buttonId, OBJPROP_YDISTANCE, button_y);
   ObjectSetInteger(0, buttonId, OBJPROP_XDISTANCE, button_x);

   OnInit2();

   show_data = ObjectGetInteger(0, buttonId, OBJPROP_STATE);
   
   if (show_data) ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_ON_color); 
   else ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_OFF_color);
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------------------------------------------------------+
//+------------------------------------------------------------------------------------------------------------------+
void createButton(string buttonID,string buttonText,int width2,int height,string font,int fontSize,color bgColor,color borderColor,color txtColor)
{
      ObjectDelete    (0,buttonID);
      ObjectCreate    (0,buttonID,OBJ_BUTTON,btn_Subwindow,0,0);
      ObjectSetInteger(0,buttonID,OBJPROP_COLOR,txtColor);
      ObjectSetInteger(0,buttonID,OBJPROP_BGCOLOR,bgColor);
      ObjectSetInteger(0,buttonID,OBJPROP_BORDER_COLOR,borderColor);
      ObjectSetInteger(0,buttonID,OBJPROP_BORDER_TYPE,BORDER_RAISED);
      ObjectSetInteger(0,buttonID,OBJPROP_XSIZE,width2);
      ObjectSetInteger(0,buttonID,OBJPROP_YSIZE,height);
      ObjectSetString (0,buttonID,OBJPROP_FONT,font);
      ObjectSetString (0,buttonID,OBJPROP_TEXT,buttonText);
      ObjectSetInteger(0,buttonID,OBJPROP_FONTSIZE,fontSize);
      ObjectSetInteger(0,buttonID,OBJPROP_SELECTABLE,0);
      ObjectSetInteger(0,buttonID,OBJPROP_CORNER,btn_corner);
      ObjectSetInteger(0,buttonID,OBJPROP_HIDDEN,1);
      ObjectSetInteger(0,buttonID,OBJPROP_XDISTANCE,9999);
      ObjectSetInteger(0,buttonID,OBJPROP_YDISTANCE,9999);
      // Upon creation, set the initial state to "true" which is "on", so one will see the indicator by default
      ObjectSetInteger(0, buttonId, OBJPROP_STATE, true);
}
//+------------------------------------------------------------------------------------------------------------------+
void OnDeinit(const int reason) 
{
   // This 'ObjectsDeleteAll' is only needed when any objects *besides* the button are created, but this indicator does not, hence, not needed.
   //ObjectsDeleteAll(0, IndicatorObjPrefix);

   // If just changing a TF', the button need not be deleted, therefore the 'OBJPROP_STATE' is also preserved.
   if(reason != REASON_CHARTCHANGE) ObjectDelete(buttonId);
}
//+------------------------------------------------------------------------------------------------------------------+
void OnChartEvent(const int id, //don't change anything here
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   int ForexStation;
   // If another indy on the same chart has enabled events for create/delete/mouse-move, just skip this events up front because they aren't
   //    needed, AND in the worst case, this indy might cause MT4 to hang!!  Skipping the events seems to help, along with other (major) changes to the code below.
   if(id==CHARTEVENT_OBJECT_CREATE || id==CHARTEVENT_OBJECT_DELETE) return; // This appears to make this indy compatible with other programs that enabled CHART_EVENT_OBJECT_CREATE and/or CHART_EVENT_OBJECT_DELETE
   if(id==CHARTEVENT_MOUSE_MOVE    || id==CHARTEVENT_MOUSE_WHEEL)   return; // If this, or another program, enabled mouse-events, these are not needed below, so skip it unless actually needed. 

   if (id==CHARTEVENT_OBJECT_CLICK && sparam == buttonId)
   {
      show_data = ObjectGetInteger(0, buttonId, OBJPROP_STATE);
      
      if (show_data)
      {
         ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_ON_color); 
         OnInit2();
          // Is it a problem to call 'start()' ??  Possibly it makes no difference, but now calling "mystart()" instead of "start()"; and "start()" simply runs "mystart()", so should be same as before.
         recalc=true;
         mystart();
      }
      else
      {
         ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_OFF_color);
         for (ForexStation=0; ForexStation<indicator_buffers; ForexStation++)
              SetIndexStyle(ForexStation,DRAW_NONE);
      }
   }
}
//+------------------------------------------------------------------------------------------------------------------+
int OnInit2()
{
   IndicatorBuffers(10);
   SetIndexBuffer(0,FilledRibbonUptrend);   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer(1,FilledRibbonDowntrend); SetIndexStyle(1,DRAW_HISTOGRAM);

   SetIndexBuffer(2,Alma1); 
   SetIndexBuffer(3,Almada1);
   SetIndexBuffer(4,Almadb1);
   SetIndexBuffer(5,slope1);
   SetIndexBuffer(6,Alma2);
   SetIndexBuffer(7,Almada2);
   SetIndexBuffer(8,Almadb2);
   SetIndexBuffer(9,slope2);
   
   SetIndexLabel (2, "ALMA " + AlmaPeriod1);
   SetIndexLabel (6, "ALMA " + AlmaPeriod2);

      for (int ForexStation=2; ForexStation<indicator_buffers; ForexStation++)
           SetIndexStyle(ForexStation,DRAW_LINE);
   
      AlmaPeriod1 = MathMax(AlmaPeriod1,1);
      ArrayResize(coeffs1,AlmaPeriod1);
      AlmaPeriod2 = MathMax(AlmaPeriod2,1);
      ArrayResize(coeffs2,AlmaPeriod2);
      
         double m = MathFloor(AlmaSample1 * (AlmaPeriod1 - 1.0));
         double s = AlmaPeriod1/AlmaSigma1;
         for (int i=0; i<AlmaPeriod1; i++)
                  coeffs1[i] = MathExp(-((i-m)*(i-m))/(2.0*s*s));

         double m2 = MathFloor(AlmaSample2 * (AlmaPeriod2 - 1.0));
         double s2 = AlmaPeriod2/AlmaSigma2;
         for (int banzai=0; banzai<AlmaPeriod2; banzai++)
                  coeffs2[banzai] = MathExp(-((banzai-m2)*(banzai-m2))/(2.0*s2*s2));
         
   return(0);
}
//+------------------------------------------------------------------------------------------------------------------+
//+------------------------------------------------------------------------------------------------------------------+
int start() {return(mystart()); }
//+------------------------------------------------------------------------------------------------------------------+
int mystart()
{
   if (show_data)
      {
   int counted_bars=IndicatorCounted();
        if(recalc) 
        {
           // If a button goes from off-to-on, everything must be recalculated.  The 'recalc' variable is used as a trigger to do this.
           counted_bars = 0;
           recalc=false;
        }
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         int limit = MathMin(Bars-counted_bars,Bars-1);

   //
   //
   //
   //
   //
   
   if (slope1[limit]==-1) CleanPoint(limit,Almada1,Almadb1);
   if (slope2[limit]==-1) CleanPoint(limit,Almada2,Almadb2);
   
	for(int i = limit; i >= 0; i--)
   {
      double sum=0;
      double div=0;
      double sum2=0;
      double div2=0;
      for (int k=0; k<AlmaPeriod1 && (k+i)<Bars; k++)
      {
         sum += coeffs1[k]*iMA(NULL,0,1,0,MODE_SMA,AlmaPrice1,i+k);
         div += coeffs1[k];
      }
      for (int k2=0; k2<AlmaPeriod2 && (k2+i)<Bars; k2++)
      {
         sum2 += coeffs2[k2]*iMA(NULL,0,1,0,MODE_SMA,AlmaPrice2,i+k2);
         div2 += coeffs2[k2];
      }
      
      //
      //
      //
      //
      //
      
      Almada1[i] = EMPTY_VALUE;
      Almadb1[i] = EMPTY_VALUE;
      if (div!=0)
           {
            Alma1[i] = sum/div;
            FilledRibbonUptrend[i] = Alma1[i];
           }
      else  
           {
            Alma1[i] = 0;
            FilledRibbonUptrend[i] = Alma1[i];
           }       
      slope1[i] = slope1[i+1];
      if (Alma1[i]>Alma1[i+1]) slope1[i] =  1;
      if (Alma1[i]<Alma1[i+1]) slope1[i] = -1;
      if (slope1[i]==-1) PlotPoint(i,Almada1,Almadb1,Alma1);

      Almada2[i] = EMPTY_VALUE;
      Almadb2[i] = EMPTY_VALUE;
      if (div2!=0)
         {
            Alma2[i] = sum2/div2;
            FilledRibbonDowntrend[i] = Alma2[i];          
          }
      else  
          {
            Alma2[i] = 0;       
            FilledRibbonDowntrend[i] = Alma2[i];                
      }
      slope2[i] = slope2[i+1];
      if (Alma2[i]>Alma2[i+1]) slope2[i] =  1;
      if (Alma2[i]<Alma2[i+1]) slope2[i] = -1;
      if (slope2[i]==-1) PlotPoint(i,Almada2,Almadb2,Alma2);
   }
       } //if (show_data)  
   return(0);
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

void CleanPoint(int i,double& first[],double& second[])
{
   if (i>Bars-3) return;
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
          first[i+1] = EMPTY_VALUE;
}

//
//
//
//
//

void PlotPoint(int i,double& first[],double& second[],double& from[])
{
   if (i>Bars-3) return;
   if (first[i+1] == EMPTY_VALUE)
      {
         if (first[i+2] == EMPTY_VALUE) {
                first[i]   = from[i];
                first[i+1] = from[i+1];
                second[i]  = EMPTY_VALUE;
            }
         else {
                second[i]   =  from[i];
                second[i+1] =  from[i+1];
                first[i]    = EMPTY_VALUE;
            }
      }
   else
      {
         first[i]  = from[i];
         second[i] = EMPTY_VALUE;
      }
}