//+------------------------------------------------------------------+
//|                                           Chaos semafor 3mod.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 6
#property indicator_plots   6
//--- plot Highest1_
#property indicator_label1  "Highest1_"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrWhite
#property indicator_style1  STYLE_SOLID
#property indicator_width1  5
//--- plot Lowest1_
#property indicator_label2  "Lowest1_"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrWhite
#property indicator_style2  STYLE_SOLID
#property indicator_width2  5
//--- plot Highest2_
#property indicator_label3  "Highest2_"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrDodgerBlue
#property indicator_style3  STYLE_SOLID
#property indicator_width3  3
//--- plot lowest2_
#property indicator_label4  "lowest2_"
#property indicator_type4   DRAW_ARROW
#property indicator_color4  clrDodgerBlue
#property indicator_style4  STYLE_SOLID
#property indicator_width4  3
//--- plot Highest3_
#property indicator_label5  "Highest3_"
#property indicator_type5   DRAW_ARROW
#property indicator_color5  clrRed
#property indicator_style5  STYLE_SOLID
#property indicator_width5  2
//--- plot Lowest3_
#property indicator_label6  "Lowest3_"
#property indicator_type6   DRAW_ARROW
#property indicator_color6  clrLime
#property indicator_style6  STYLE_SOLID
#property indicator_width6  2
//--- input parameters
input string	dummy1;							//---Large ZigZag---
input uint 		ExtDepth1 		= 77;			//Depth
input uint 		ExtDeviation1 	= 5;			//Deviation
input uint 		ExtBackstep1 	= 3;			//Backstep
input string	dummy2;							//---Middle ZigZag---
input uint 		ExtDepth2 		= 31;			//Depth
input uint 		ExtDeviation2 	= 5;			//Deviation
input uint 		ExtBackstep2 	= 3;			//Backstep
input string	dummy3;							//---Small ZigZag---
input uint 		ExtDepth3 		= 9;			//Depth
input uint 		ExtDeviation3 	= 5;			//Deviation
input uint 		ExtBackstep3 	= 3;			//Backstep
input string	dummy4;							//---   Alert   ---
input bool   	Box_Alerts     = true ;		//PopUp Alert
input bool   	Sound_Alerts   = false ;	//Sound Alert
input bool   	Email_Alerts   = false ;	//E-mail
//--- indicator buffers
double         Highest1_Buffer[];
double         Lowest1_Buffer[];
double         Highest2_Buffer[];
double         Lowest2_Buffer[];
double         Highest3_Buffer[];
double         Lowest3_Buffer[];

int 				min_rates_total[3];
int 				LAST_lowpos[3],  LAST_highpos[3];
double 			LAST_lowone[3],  LAST_lowtwo[3];
double			LAST_highone[3], LAST_hightwo[3];
bool 				Trigger;
int 				OldBars 	= -1;
string 			Alert_SoundFile = "buy_rob_pirate.wav"; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
	//--- indicator buffers mapping
	SetIndexBuffer(0, Highest1_Buffer, INDICATOR_DATA);
	SetIndexBuffer(1, Lowest1_Buffer,  INDICATOR_DATA);
	SetIndexBuffer(2, Highest2_Buffer, INDICATOR_DATA);
	SetIndexBuffer(3, Lowest2_Buffer,  INDICATOR_DATA);
	SetIndexBuffer(4, Highest3_Buffer, INDICATOR_DATA);
	SetIndexBuffer(5, Lowest3_Buffer,  INDICATOR_DATA);
	//--- setting a code from the Wingdings charset as the property of PLOT_ARROW
	PlotIndexSetInteger(0, PLOT_ARROW, 93);
	PlotIndexSetInteger(1, PLOT_ARROW, 93);
	PlotIndexSetInteger(2, PLOT_ARROW, 108);
	PlotIndexSetInteger(3, PLOT_ARROW, 108);
	PlotIndexSetInteger(4, PLOT_ARROW, 167);
	PlotIndexSetInteger(5, PLOT_ARROW, 167);
	
	PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
	PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
	PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0.0);
	PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0.0);
	PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, 0.0);
	PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, 0.0);
	
	ArraySetAsSeries(Lowest1_Buffer,  true);
	ArraySetAsSeries(Highest1_Buffer, true);
	ArraySetAsSeries(Lowest2_Buffer,  true);
	ArraySetAsSeries(Highest2_Buffer, true);
	ArraySetAsSeries(Lowest3_Buffer,  true);
	ArraySetAsSeries(Highest3_Buffer, true);
	
	min_rates_total[0] = int(ExtDepth1 + ExtBackstep1);
	min_rates_total[1] = int(ExtDepth2 + ExtBackstep2);
	min_rates_total[2] = int(ExtDepth3 + ExtBackstep3);
	
	PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, min_rates_total[0]);
	PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, min_rates_total[0]);
	PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, min_rates_total[1]);
	PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, min_rates_total[1]);
	PlotIndexSetInteger(4, PLOT_DRAW_BEGIN, min_rates_total[2]);
	PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, min_rates_total[2]);
	
	IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
	
	string shortname = "Chaos Semafor 3-mod(" + (string)ExtDepth1 + "," + (string)ExtDepth2 + "," + (string)ExtDepth3 + ")";
	IndicatorSetString(INDICATOR_SHORTNAME, shortname);
	//---
	return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 < min_rates_total[0]) 
		return(0);
		
	if (rates_total != OldBars)
		Trigger = true;
	
	double price_high[];
	double price_low[];
	
	ArrayCopy(price_high, high, 0, 0, WHOLE_ARRAY);
	ArrayCopy(price_low,  low,  0, 0, WHOLE_ARRAY);
	
	ZigZag_Calc(rates_total, prev_calculated, price_high, price_low, ExtDepth1, ExtDeviation1, ExtBackstep1, Highest1_Buffer, Lowest1_Buffer, 0);
	ZigZag_Calc(rates_total, prev_calculated, price_high, price_low, ExtDepth2, ExtDeviation2, ExtBackstep2, Highest2_Buffer, Lowest2_Buffer, 1);
	ZigZag_Calc(rates_total, prev_calculated, price_high, price_low, ExtDepth3, ExtDeviation3, ExtBackstep3, Highest3_Buffer, Lowest3_Buffer, 2);

	string alert_level;
	string Chart_period = TimeFrameToString(Period());
	string alert_message = Symbol() + "  " + Chart_period + " at " + DoubleToString(close[0], Digits());

	if (Trigger)
	{
		if (Lowest1_Buffer[0] != 0)
		{
			Trigger     = false;
			alert_level = " ZZ: Level Low;  ";

			if (Box_Alerts)    
				Alert(alert_level, alert_message);
				
			if (Sound_Alerts)  
				PlaySound(Alert_SoundFile);

			if (Email_Alerts)  
				SendMail(alert_level, alert_message);
		}

		if (Highest1_Buffer[0] != 0)
		{
			Trigger     = false;
			alert_level = " ZZ: Level High; ";

			if (Box_Alerts)    
				Alert(alert_level, alert_message);
			
			if (Sound_Alerts)  
				PlaySound(Alert_SoundFile);

			if (Email_Alerts)  
				SendMail(alert_level, alert_message);
		}
	}
	
	OldBars = rates_total;
	//--- return value of prev_calculated for next call
	return(rates_total);
}
//+------------------------------------------------------------------+
void ZigZag_Calc(int rates_total, 
					  int prev_calculated, 
					  double& high[], 
					  double& low[], 
					  uint Depth, 
					  uint Deviation, 
					  uint Backstep,
					  double& High_Buffer[],
					  double& Low_Buffer[],
					  int n)
{
	int i, j, limit;
	int last_highpos, last_lowpos;
	double cur_low, cur_high, val, res;
	double last_highone[] = {NULL, NULL, NULL}, last_lowone[] = {NULL, NULL, NULL};
	double last_hightwo[3], last_lowtwo[3];
	
	if (prev_calculated > rates_total || prev_calculated <= 0)
	{
		limit = rates_total - min_rates_total[n];
		
		last_lowtwo[n]  = -1;
		last_hightwo[n] = -1;
		last_lowpos     = -1;
		last_highpos    = -1;
	}
	else
	{
		limit = rates_total - prev_calculated; 
		
		last_lowone[n]  = LAST_lowone[n];
		last_highone[n] = LAST_highone[n];
		last_lowtwo[n]  = LAST_lowtwo[n];
		last_hightwo[n] = LAST_hightwo[n];
		last_lowpos     = LAST_lowpos[n]  + limit;
		last_highpos    = LAST_highpos[n] + limit;
	}

	ArraySetAsSeries(high, true);
	ArraySetAsSeries(low,  true);

	for (i = limit; i >= 0 && !IsStopped(); i--)
	{
		if (rates_total != prev_calculated && i == 0)
		{
			LAST_lowone[n]  = last_lowone[n];
			LAST_highone[n] = last_highone[n];
		}

		//--- low
		val = low[ArrayMinimum(low, i, Depth)];

		if (val == last_lowone[n]) 
			val = NULL;
		else
		{
			last_lowone[n] = val;

			if (low[i] - val > Deviation * _Point) 
				val = NULL;
			else
			{
				for (j = 1; j <= int(Backstep); j++)
				{
					res = Low_Buffer[i + j];

					if (res && res > val) 
						Low_Buffer[i + j] = NULL;
				}
			}
		}

		Low_Buffer[i] = val;
		//--- high
		val = high[ArrayMaximum(high, i, Depth)];

		if (val == last_highone[n]) 
			val = NULL;
		else
		{
			last_highone[n] = val;

			if (val - high[i] > Deviation * _Point) 
				val = NULL;
			else
			{
				for (j = 1; j <= int(Backstep); j++)
				{
					res = High_Buffer[i + j];

					if (res && res < val) 
						High_Buffer[i + j] = NULL;
				}
			}
		}

		High_Buffer[i] = val;
	}

	for (i = limit; i >= 0; i--)
	{
		if (rates_total != prev_calculated && !i)
		{
			LAST_lowtwo[n]  = last_lowtwo[n];
			LAST_hightwo[n] = last_hightwo[n];
			LAST_lowpos[n]  = last_lowpos;
			LAST_highpos[n] = last_highpos;
		}

		cur_low  = Low_Buffer[i];
		cur_high = High_Buffer[i];

		if (!cur_low && !cur_high) 
			continue;

		if (cur_high)
		{
			if (last_hightwo[n] > 0)
			{
				if (last_hightwo[n] < cur_high) 
					High_Buffer[last_highpos] = NULL;
				else 
					High_Buffer[i] = NULL;
			}

			if (last_hightwo[n] < cur_high || last_hightwo[n] < 0)
			{
				last_hightwo[n] = cur_high;
				last_highpos    = i;
			}

			last_lowtwo[n] = -1;
		}

		if (cur_low)
		{
			if (last_lowtwo[n] > 0)
			{
				if (last_lowtwo[n] > cur_low) 
					Low_Buffer[last_lowpos] = NULL;
				else 
					Low_Buffer[i] = NULL;
			}

			if (cur_low < last_lowtwo[n] || last_lowtwo[n] < 0)
			{
				last_lowtwo[n] = cur_low;
				last_lowpos    = i;
			}

			last_hightwo[n] = -1;
		}
	}
}
//+------------------------------------------------------------------+
string TimeFrameToString(int tf)
{
	string tfs;

	switch (tf)
	{
		case PERIOD_M1:
			tfs = "M1"  ;
			break;
		case PERIOD_M5:
			tfs = "M5"  ;
			break;
		case PERIOD_M15:
			tfs = "M15" ;
			break;
		case PERIOD_M30:
			tfs = "M30" ;
			break;
		case PERIOD_H1:
			tfs = "H1"  ;
			break;
		case PERIOD_H4:
			tfs = "H4"  ;
			break;
		case PERIOD_D1:
			tfs = "D1"  ;
			break;
		case PERIOD_W1:
			tfs = "W1"  ;
			break;
		case PERIOD_MN1:
			tfs = "MN";
	}

	return(tfs);
}
//+------------------------------------------------------------------+
