//===================================================================================================================================================//
#property copyright   "Copyright 2014-2019, Nikolaos Pantzos"
#property link        "https://www.mql5.com/en/users/pannik"
#property version     "1.5"
#property description "Follow Line Indicator"
#property strict
//===================================================================================================================================================//
enum Arrows{Hide_Arrows,Simple_Arrows,Open_Cose_Median,High_Low_Open_Close};
//===================================================================================================================================================//
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_color1 clrDeepSkyBlue
#property indicator_color2 clrCrimson
#property indicator_color3 clrWhite
#property indicator_color4 clrWhite
#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 2
#property indicator_width4 2
//===================================================================================================================================================//
input int    BarsCount     = 10000;
input int    BBperiod      = 21;
input double BBdeviations  = 1;
input int    MAperiod      = 21;
input int    ATRperiod     = 5;
input bool   UseATRfilter  = true;
input bool   AlertON       = false;
input bool               alertsMessage              = true;             // Alerts should display message?
input bool               alertsSound                = false;            // Alerts should play a sound?
input bool               alertsNotify               = true;            // Alerts should send a notification?
input bool               alertsEmail                = false;     // Alerts should send an email?
input string             soundfile                  = "alert2.wav";     // Sound file
input Arrows TypeOfArrows  = Simple_Arrows;
input int ArrowGap           = 100;
extern string             button_note1          = "------------------------------";
extern int                btn_Subwindow = 0;
extern ENUM_BASE_CORNER   btn_corner            = CORNER_LEFT_UPPER;
extern string             btn_text              = "FollowLine";
extern string             btn_Font              = "Arial";
extern int                btn_FontSize          = 10;
extern color              btn_text_ON_color     = clrLime;
extern color              btn_text_OFF_color    = clrRed;
extern color              btn_background_color  = clrDimGray;
extern color              btn_border_color      = clrBlack;
extern int                button_x              = 100;
extern int                button_y              = 0;
extern int                btn_Width             = 90;
extern int                btn_Height            = 20;
extern string             button_note2          = "------------------------------";

bool show_data = true;
bool recalc    = true;

string IndicatorName, IndicatorObjPrefix,buttonId;
//===================================================================================================================================================//
double TrendLine[];
double iTrend[];
double TrendUp[];
double TrendDown[];
double UpArrows[];
double DownArrows[];
double BB_Upper=0;
double BB_Lower=0;
double MA_High=0;
double MA_Open=0;
double MA_Close=0;
double MA_Low=0;
double MA_Median=0;
double Diff_LOC=0;
double Diff_HOC=0;
bool AlertBuy=TRUE;
bool AlertSell=TRUE;
bool ShowUpArrow=TRUE;
bool ShowDnArrow=TRUE;
int AlertSignal=0;

string GenerateIndicatorName(const string target)
  {
   string name = target;
   int try
         = 2;
   while(WindowFind(name) != -1)
     {
      name = target + " #" + IntegerToString(try
                                                ++);
     }
   return name;
  }
  
  
//===================================================================================================================================================//
int OnInit(void)
  {
   IndicatorName = GenerateIndicatorName(btn_text);
   IndicatorObjPrefix = "__" + IndicatorName + "__";
   IndicatorShortName(IndicatorName);
   IndicatorDigits(Digits);

   double val;
   if(GlobalVariableGet(IndicatorName + "_visibility", val))
      show_data = val != 0;
      
      
//-----------------------------------------------------------------------------------
   IndicatorShortName(WindowExpertName());
//-----------------------------------------------------------------------------------
   IndicatorDigits(Digits);
   IndicatorBuffers(7);
//---
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,TrendUp);
   SetIndexLabel(0,"Trend Up");
//---
   SetIndexStyle(1,DRAW_LINE);
   SetIndexBuffer(1,TrendDown);
   SetIndexLabel(1,"Trend Down");
//---
   SetIndexStyle(2,DRAW_ARROW);
   SetIndexArrow(2,233);
   SetIndexBuffer(2,UpArrows);
   SetIndexLabel(2,"Arrow Up");
//---
   SetIndexStyle(3,DRAW_ARROW);
   SetIndexArrow(3,234);
   SetIndexBuffer(3,DownArrows);
   SetIndexLabel(3,"Arrow Down");
//---
   SetIndexBuffer(4,TrendLine);
   SetIndexBuffer(5,iTrend);
   
    ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, 1);
   buttonId = IndicatorObjPrefix + (btn_text);
   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);
   
   
//-----------------------------------------------------------------------------------
   return(INIT_SUCCEEDED);
//-----------------------------------------------------------------------------------
  }
//====================================================================================================================================================//
void OnDeinit(const int reason)
  {
//--------------------------------------------------------------------------------
   Comment("");
   ObjectsDeleteAll(ChartID(), IndicatorObjPrefix);
   
//--------------------------------------------------------------------------------
  }
  
  void createButton(string buttonID,string buttonText,int width,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,width);
   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);
  }
//+------------------------------------------------------------------+
void handleButtonClicks()
  {
   if(ObjectGetInteger(0, buttonId, OBJPROP_STATE))
     {
      ObjectSetInteger(0, buttonId, OBJPROP_STATE, false);
      show_data = !show_data;
      GlobalVariableSet(IndicatorName + "_visibility", show_data ? 1.0 : 0.0);
      recalc = true;
      // start();
     }
  }
  
//
//
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   handleButtonClicks();
   if(id==CHARTEVENT_OBJECT_CLICK && ObjectGet(sparam,OBJPROP_TYPE)==OBJ_BUTTON)

      for(int banzai=0; banzai<indicator_buffers; banzai++);
    
   
        SetIndexStyle(0,DRAW_LINE);
        SetIndexStyle(1,DRAW_LINE);
        SetIndexStyle(2,DRAW_ARROW);
        SetIndexStyle(3,DRAW_ARROW);
       
   if(show_data)
     {
      ObjectSetInteger(ChartID(),buttonId,OBJPROP_COLOR,btn_text_ON_color);

     }
   else
     {
      ObjectSetInteger(ChartID(),buttonId,OBJPROP_COLOR,btn_text_OFF_color);
      for(int banzai=0; banzai<indicator_buffers; banzai++)
         SetIndexStyle(banzai,DRAW_NONE);
     }

  }
//===================================================================================================================================================//
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 i=0;
   int Limit=0;
   int BB_Signal=0;
   int MA_Signal=0;
   int CountedBars=0;
//-----------------------------------------------------------------------------------
   CountedBars=BarsCount;//IndicatorCounted();
   if(CountedBars>Bars-1) CountedBars=Bars-1;
   if(CountedBars<0) return(-1);
   if(CountedBars>0) CountedBars--;
   Limit=CountedBars;
//-----------------------------------------------------------------------------------
   for(i=Limit-1; i>=0; i--)
     {
      BB_Upper=iBands(NULL,0,BBperiod,BBdeviations,0,PRICE_CLOSE,MODE_UPPER,i+1);
      BB_Lower=iBands(NULL,0,BBperiod,BBdeviations,0,PRICE_CLOSE,MODE_LOWER,i+1);
      //-----------------------------------------------------------------------------------
      if(close[i]>BB_Upper) BB_Signal=1;
      if(close[i]<BB_Lower) BB_Signal=-1;
      //-----------------------------------------------------------------------------------
      if(BB_Signal>0)
        {
         if(UseATRfilter==true) TrendLine[i]=NormalizeDouble(low[i]-iATR(NULL,0,ATRperiod,i),Digits);
         if(UseATRfilter==false) TrendLine[i]=NormalizeDouble(low[i],Digits);
         if(TrendLine[i]<TrendLine[i+1]) TrendLine[i]=TrendLine[i+1];
        }
      //---
      if(BB_Signal<0)
        {
         if(UseATRfilter==true) TrendLine[i]=NormalizeDouble(high[i]+iATR(NULL,0,ATRperiod,i),Digits);
         if(UseATRfilter==false) TrendLine[i]=NormalizeDouble(high[i],Digits);
         if(TrendLine[i]>TrendLine[i+1]) TrendLine[i]=TrendLine[i+1];
        }
      //---
     }
//-----------------------------------------------------------------------------------
   for(i=Limit-2; i>=0; i--)
     {
      iTrend[i]=iTrend[i+1];
      if(TrendLine[i]>TrendLine[i+1]) iTrend[i]=1;
      if(TrendLine[i]<TrendLine[i+1]) iTrend[i]=-1;
      //-------------------------------------------------
      if(TypeOfArrows>1)
        {
         MA_High=iMA(NULL,0,MAperiod,0,MODE_SMA,PRICE_HIGH,i);
         MA_Open=iMA(NULL,0,MAperiod,0,MODE_SMA,PRICE_OPEN,i);
         MA_Close=iMA(NULL,0,MAperiod,0,MODE_SMA,PRICE_CLOSE,i);
         MA_Low=iMA(NULL,0,MAperiod,0,MODE_SMA,PRICE_LOW,i);
         MA_Median=iMA(NULL,0,MAperiod,0,MODE_SMA,PRICE_MEDIAN,i);
         //-------------------------------------------------
         Diff_HOC=NormalizeDouble((MA_High-MA_Open)+(MA_High-MA_Close),Digits);
         Diff_LOC=NormalizeDouble((MA_Open-MA_Low)+(MA_Close-MA_Low),Digits);
         //-------------------------------------------------
         if(TypeOfArrows==2)
           {
            if((MA_Close>MA_Open)&&(MA_Median<MA_Close)&&(MA_Median>MA_Open)) MA_Signal=1;
            if((MA_Close<MA_Open)&&(MA_Median>MA_Close)&&(MA_Median<MA_Open)) MA_Signal=-1;
           }
         if(TypeOfArrows==3)
           {
            if((MA_Close>MA_Open)&&(Diff_HOC>Diff_LOC)) MA_Signal=1;
            if((MA_Close<MA_Open)&&(Diff_HOC<Diff_LOC)) MA_Signal=-1;
           }
         //-------------------------------------------------
         if(iTrend[i]<0) ShowUpArrow=true;
         if(iTrend[i]>0) ShowDnArrow=true;
        }
      //-------------------------------------------------
      if(iTrend[i]>0)
        {
         TrendUp[i]=TrendLine[i];
         if(((iTrend[i+1]<0) && (TypeOfArrows==1)) || (TypeOfArrows>1))
           {
            TrendUp[i+1]=TrendLine[i+1];
            //---Arrows UP
            if((ShowUpArrow==true) && (TypeOfArrows!=0) && ((MA_Signal>0) || (TypeOfArrows==1)))
              {
               UpArrows[i+1]=NormalizeDouble(TrendLine[i+1]-ArrowGap*Point,Digits);
               ShowUpArrow=false;
               ShowDnArrow=true;
              }
            //---
           }
         TrendDown[i]=EMPTY_VALUE;
         DownArrows[i]=EMPTY_VALUE;
        }
      //-------------------------------------------------
      if(iTrend[i]<0)
        {
         TrendDown[i]=TrendLine[i];
         if(((iTrend[i+1]>0) && (TypeOfArrows==1)) || (TypeOfArrows>1))
           {
            TrendDown[i+1]=TrendLine[i+1];
            //---Arrows DN
            if((ShowDnArrow==true) && (TypeOfArrows!=0) && ((MA_Signal<0) || (TypeOfArrows==1)))
              {
               DownArrows[i+1]=NormalizeDouble(TrendLine[i+1]+ArrowGap*Point,Digits);
               ShowUpArrow=true;
               ShowDnArrow=false;
              }
           }
         TrendUp[i]=EMPTY_VALUE;
         UpArrows[i]=EMPTY_VALUE;
        }
      //---
     }
//-----------------------------------------------------------------------------------
//Pop up alerts
   if(AlertON==TRUE)
     {
      if((AlertBuy==true) && ((iTrend[1]>0)) && ((iTrend[2]<0)))
        {
         AlertSignal=1;
         AlertBuy=FALSE;
         AlertSell=TRUE;
        }
      //---
      if((AlertSell==true) && ((iTrend[1]<0)) && ((iTrend[2]>0)))
        {
         AlertSignal=-1;
         AlertBuy=TRUE;
         AlertSell=FALSE;
        }
      //-----------------------------------------------------------------------------------
      if(AlertSignal>0)     
        {
        string buy_msg = WindowExpertName()+" => Buy signal @ "+DoubleToStr(close[0],Digits)+" "+Symbol()+", Time: "+TimeToString(TimeCurrent())+", "+DoubleToStr(Period(),0)+" Minutes Chart";
         if (alertsMessage)  Alert(buy_msg);
          if (alertsNotify)  SendNotification(buy_msg);
          if (alertsEmail)   SendMail(_Symbol+" FollowLine ",buy_msg);
          if (alertsSound)   PlaySound(soundfile);
         AlertSignal=0;
        }
      if(AlertSignal<0)
        {
        string sell_msg = WindowExpertName()+" => Sell signal @ "+DoubleToStr(close[0],Digits)+" "+Symbol()+", Time: "+TimeToString(TimeCurrent())+", "+DoubleToStr(Period(),0)+" Minutes Chart";
        
         if (alertsMessage) Alert(sell_msg);
          if (alertsNotify)  SendNotification(sell_msg);
          if (alertsEmail)   SendMail(_Symbol+" FollowLine ",sell_msg);
          if (alertsSound)   PlaySound(soundfile);
         AlertSignal=0;
        }
     }
//-----------------------------------------------------------------------------------
   WindowRedraw();
//-----------------------------------------------------------------------------------
   return(rates_total);
//-----------------------------------------------------------------------------------
  }
//===================================================================================================================================================//
