//+------------------------------------------------------------------+
//|                                              Gold Fix v1.2.7.mq4 |
//|                                            Copyright © 2009, alx |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2010, alx"
#property link      ""

extern bool MasterMode = false;
extern int BrokerToGMT = 2;
extern string BrokerGoesOnDST	= "2010.03.14 02:00";
extern string BrokerGoesOffDST	= "2010.11.07 02:00";
extern int DaysBack = 10;
extern bool EnableAlarm = false;
extern int FireAlarmMinutesBefore = 5;
extern bool ShowDescription	= false;
extern bool	ShowAsVLine	= true; // if true it will draw Vertical Line, when false it will draw Trendline vertically. VLines intersect with subwindows while Trendlines not.
extern color GoldFix 		= Gold;

extern string V_3.0.14;

#define phaseCount 1
#define nameCount 1

datetime phaseTime[phaseCount][2];
color colorTable[phaseCount];
string nameTable[nameCount][2];

int stayAwaySecondsBefore, stayAwaySecondsAfter;

#define startTime 0
#define endTime 1

int dayIndex;
datetime alarmFired;
datetime previousBar = 0, currentBar = 0;

string PhaseName = "Gold_Fix_";

#import "kernel32.dll"
void GetSystemTime(int& TimeArray[]);
#import

#define dayInSeconds 86400

#define LondonDefaultTZ		0
#define TokyoDefaultTZ		9
#define NewYorkDefaultTZ	-5
#define SydneyDefaultTZ    10
int BrokerDefaultTZ = 0;
int brokerDSToffset = 0;
int BrokerOffset;

string GvTzName = "Session Phases v1.1 Last known Broker TimeZone";
string GvDstOnName = "Session Phases Broker DST On";
string GvDstOffName = "Session Phases Broker DST Off";
string GvMasterIDName = "Session Phases Master Flag";
string GvMasterName = "Session Phases Master Flag";
string masterID;
bool standalone = false;

#define startupPeriod 4 // for the first 3 minutes I will be updating objects every minute as history data may be incomplete
int startupCount = -1;
bool forceUpdate = false;
datetime previousBrokerTime = 0, currentBrokerTime = 0;
int currentBarCount, previousBarCount;
bool newM1bar = false;

#property indicator_chart_window
//------------------------------------------------------
bool isEuropeOnDST(datetime now)
{
	// Europe DST rule:
	// On from 2:00 AM the last Sunday in March
	// Off to  3:00 AM the last Sunday in October
	
	int StartMonth	=	3;
	int EndMonth	=	10;
	int StartWeekDay=	0;
	int EndWeekDay	=	0;
	int StartHour	=	2;
	int EndHour		=	3;
	
	// the above transition times are local time. That means DST starts at 2AM local London standard time and ends at 3AM local London DST time...
	// I need to readjust it to gmt reference. London is GMT so I only need to adjust the end hour.
	EndHour -= 1;

	datetime pointer;
	int month = TimeMonth(now);
	if (month > StartMonth && month < EndMonth) return (true); // easy filter
	if (month < StartMonth || month > EndMonth) return (false); // same deal
	
	int i;
	int lookedDayNum;
	int currentDayNum = TimeDay(now);
	if (month == StartMonth)
		{
			//now wee need to know the starting day number. This is the last Sunday so lets do a walkthrough backward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(StartMonth+1),".01")); // 1st April
			pointer -= dayInSeconds;
			
			for (i=6;i>=0;i--)
				{
					if (TimeDayOfWeek(pointer) == StartWeekDay)
						{
							lookedDayNum = TimeDay(pointer);
							if (lookedDayNum < currentDayNum) return (true);
							if (lookedDayNum > currentDayNum) return (false);
							if (lookedDayNum == currentDayNum)
								{
									if (TimeHour(now) >= StartHour) return (true); else return (false);
								}
						}	
					pointer -= dayInSeconds; // day in seconds
				}
			return(false);
		}
	
	if (month == EndMonth)
		{
			//now wee need to know the starting day number. This is the last Sunday so lets do a walkthrough backward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(EndMonth+1),".01")); // 1st November
			pointer -= dayInSeconds;
			
			for (i=6;i>=0;i--)
				{
					if (TimeDayOfWeek(pointer) == EndWeekDay)
						{
							lookedDayNum = TimeDay(pointer);
							if (lookedDayNum < currentDayNum) return (false);
							if (lookedDayNum > currentDayNum) return (true);
							if (lookedDayNum == currentDayNum)
								{
									if (TimeHour(now) >= StartHour) return (false); else return (true);
								}
						}
					pointer -= dayInSeconds; // day in seconds
				}
			return(true);
		}	
	return (false);
}
//------------------------------------------------------ 
bool isUsOnDST(datetime now)
{
	// US DST rule:
	// On from 2:00 AM the second Sunday in March
	// Off to  2:00 AM the first Sunday in November
	
	// the code is almost the same as above. The only difference is I'm looking for the first and second Sunday.
	// and transition times adjustment..
	// this should be put in some kind of universal function, but i have no time, so copy&paste&edit:
	
	int StartMonth	=	3;
	int EndMonth	=	11;
	int StartWeekDay=	0;
	int EndWeekDay	=	0;
	int StartHour	=	2;
	int EndHour		=	2;
	
	// the above transition times are local time. That means DST starts at 2AM local London standard time and ends at 3AM local London DST time...
	// I need to readjust the gmt reference
	// this time a bit complex as NY is GMT-5...
	// I'm convertig the gmt to EST
	now += NewYorkDefaultTZ * 3600;
	
	datetime pointer;
	int month = TimeMonth(now);
	if (month > StartMonth && month < EndMonth) return (true); // easy filter
	if (month < StartMonth || month > EndMonth) return (false); // same deal

	int i;
	int lookedDayNum;
	int currentDayNum = TimeDay(now);
	if (month == StartMonth)
		{
			//now wee need to know the starting day number. This is the second Sunday so lets go forward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(StartMonth),".01")); // 1st March
			//pointer -= dayInSeconds;
			bool firstSundaySpot = false;
			for (i=0;i<14;i++)
				{
					if (TimeDayOfWeek(pointer) == StartWeekDay)
						if (!firstSundaySpot) firstSundaySpot = true;
						else
							{
								lookedDayNum = TimeDay(pointer);
								if (lookedDayNum < currentDayNum) return (true);
								if (lookedDayNum > currentDayNum) return (false);
								if (lookedDayNum == currentDayNum)
									{
										if (TimeHour(now) >= StartHour) return (true); else return (false);
									}
							}	
					pointer += dayInSeconds; // day in seconds
				}
			return(false);
		}
	
	if (month == EndMonth)
		{
			//now wee need to know the starting day number. This is the first Sunday so lets go forward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(EndMonth),".01")); // 1st November
			//pointer -= dayInSeconds;
			
			for (i=0;i<7;i++)
				{
					if (TimeDayOfWeek(pointer) == EndWeekDay)
						{
							lookedDayNum = TimeDay(pointer);
							if (lookedDayNum < currentDayNum) return (false);
							if (lookedDayNum > currentDayNum) return (true);
							if (lookedDayNum == currentDayNum)
								{
									if (TimeHour(now) >= StartHour) return (false); else return (true);
								}
						}
					pointer += dayInSeconds; // day in seconds
				}
			return(true);
		}	
	return (false);
}
//------------------------------------------------------ 
bool isJapanOnDST(datetime now)
{
	// DST rule
	// No rule. No DST support. But maybe one day you can put the rule here...
	return (false);
}
//------------------------------------------------------ 
bool isBrokerOnDST(datetime now)
{
	// It is impossible to automatically detect broker feed DST offset.
	// all I can do is to detect absolute offset and provide additional info about transition times.
	// Not very sophisticated but no other way.
	
	datetime startDate	= StrToTime(BrokerGoesOnDST);
	datetime endDate	= StrToTime(BrokerGoesOffDST);
	
	// some data check
	bool startDateFlag = false, endDateFlag = false;
	
	if (TimeYear(startDate) < Year()-1) startDateFlag = false; // wrong or outdated settings
	else startDateFlag = true;
	if (TimeYear(endDate) < Year()-1) endDateFlag = false; // wrong or outdated settings
	else endDateFlag = true;
	
	if (startDateFlag && endDateFlag) // have both dates entered
		{
			if (startDate < endDate)
				{
					if (now >= startDate && now < endDate) return (true); else return (false);
				}
			else if (startDate > endDate) // possibly the start date is future dst on date or the endDate is old one
				{
					if (now < endDate || now > startDate) return (true);
					else return (false);
				}
			else return (false); 
		}
	
	if (startDateFlag && !endDateFlag) // let's use the start date only but warn the user about the settings
		{
			if (now >= startDate)
				{
					Print("DST end date incorrect. Timing may be affected. Check BrokerGoesOffDST. Expected data format: YYYY.MM.DD HH:MM e.g. 2009.11.01 02:00 Note space between day and hour numbers");
					return (true);
				}
			else return (false);
		}

	if (!startDateFlag && endDateFlag) // let's use the end date only but warn the user about the settings
		{
			if (now < endDate)
				{
					Print("DST end date incorrect. Timing may be affected. Check BrokerGoesOnDST. Expected data format: YYYY.MM.DD HH:MM e.g. 2009.03.08 02:00 Note space between day and hour numbers");
					return (true);
				}
			else return (false);
		}

	return (false);
}
//------------------------------------------------------ 
// the code comes from http://forum.mql4.com/21619
string FormatDateTime(int nYear,int nMonth,int nDay,int nHour,int nMin,int nSec)
{
	string sMonth,sDay,sHour,sMin,sSec = "";
	sMonth=100+nMonth;
	sMonth=StringSubstr(sMonth,1);
	sDay=100+nDay;
	sDay=StringSubstr(sDay,1);
	sHour=100+nHour;
	sHour=StringSubstr(sHour,1);
	sMin=100+nMin;
	sMin=StringSubstr(sMin,1);
	sSec=100+nSec;
	sSec=StringSubstr(sSec,1);
	return(StringConcatenate(nYear,".",sMonth,".",sDay," ",sHour,":",sMin,":",sSec));
}
//------------------------------------------------------ 
// the code comes from http://forum.mql4.com/21619
datetime getGMTime()
{
	/*MS stores time in plain format:	
	typedef struct _SYSTEMTIME {
	  WORD wYear;
	  WORD wMonth;
	  WORD wDayOfWeek;
	  WORD wDay;
	  WORD wHour;
	  WORD wMinute;
	  WORD wSecond;
	  WORD wMilliseconds;
	}SYSTEMTIME, *PSYSTEMTIME;
	*/
	int TimeArray[4];
	int nYear,nMonth,nDay,nHour,nMin,nSec;
	
	GetSystemTime(TimeArray);
	// some bit mask and shif magic to get word walues from dwords..
	nYear=TimeArray[0]&0x0000FFFF;
	nMonth=TimeArray[0]>>16;
	nDay=TimeArray[1]>>16;
	nHour=TimeArray[2]&0x0000FFFF;
	nMin=TimeArray[2]>>16;
	nSec=TimeArray[3]&0x0000FFFF;
	
	// Now, MT4 stores time in second elapsed from the 1st Jan 1970.
	// there are two ways to convert:
	// recalculate the MS time into seconds starting from 1970 or
	// convert it into string MT4 can convert into it's time format.
	// The first method is risky - needs to go around number of February days changing every 4 years...
	// At the moment using method 2.
	
	datetime localTime = TimeLocal();
	int localGMTOffset = nHour-TimeHour(localTime);
	if (localGMTOffset < -11) localGMTOffset += 24;
	else if (localGMTOffset > 13) localGMTOffset -= 24;
	
	//This needs to be cleaned  - no need to convert time to string then string to time only because it may be stored in different numeric format
	string utc = FormatDateTime(nYear, nMonth, nDay, nHour, nMin, nSec);
	//Print (utc);
	return (StrToTime(utc));
}
//------------------------------------------------------ 
int getBrokerTZ(datetime gmt)
{
	int gmtHour		= TimeHour(gmt);
	datetime timeCurrent = TimeCurrent(); // Hmm...
	int brokerHour	= TimeHour(timeCurrent);
	int dif = brokerHour - gmtHour;
	if (dif < -11) dif += 24;
	if (dif > 13) dif -= 24;
	// now let's check if it's on DST
	brokerDSToffset = isBrokerOnDST(timeCurrent);
	dif -= brokerDSToffset;
	return(dif);
}
//------------------------------------------------------
void updateTimeSettings()
{
	datetime gmt 	= getGMTime();
	BrokerDefaultTZ	= getBrokerTZ(gmt); // It returns standard offset. DST offset is stored in brokerDSToffset
	BrokerOffset = BrokerDefaultTZ + brokerDSToffset; // For compatibility reasons...

	int weekDay = TimeDayOfWeek(TimeLocal()); // if weekend, broker TZ will be read from saved Global Variable if any.
	// however it should be checked once the broker feed is active again on Sunday... especially on Sunday!
	if ((Hour() >= 22 && weekDay == 5) || weekDay == 0 || weekDay == 6)
		{
			// Read saved Broker Offset..
			int newOffset = readGV(GvTzName);
			if (newOffset != EMPTY_VALUE) BrokerOffset = newOffset;
			else
				{
					Print ("No data available over weekend. Please wait for the first tick on Monday to update the Broker Time Zone. Using BrokerToGMT to calculate timetables.");
					BrokerOffset = BrokerToGMT;
				}
			BrokerDefaultTZ = BrokerOffset - brokerDSToffset;
		}
	else
		{	// Write settings if allowed...
			if (MasterMode || standalone)
				{
					if (startupCount < startupPeriod || forceUpdate)
						{
							if(newM1bar || forceUpdate)
								{
									// I need to make sure the history is updated so I set it a bit later...
									Print ("Saving new BrokerOffset ", BrokerOffset);
									GlobalVariableSet(GvTzName, BrokerOffset);
								}
						}
				}
		}
}
//------------------------------------------------------
void updateMasterFlags()
{	
	//Print ("updateMasterFlags...");
	if (MasterMode)
		{
			int test;
			test = GlobalVariableGet(GvDstOnName);
			if (test != StrToTime(BrokerGoesOnDST)) GlobalVariableSet(GvDstOnName, StrToTime(BrokerGoesOnDST));
			test = GlobalVariableGet(GvDstOffName);
			if (test != StrToTime(BrokerGoesOffDST)) GlobalVariableSet(GvDstOffName, StrToTime(BrokerGoesOffDST));
			test = GlobalVariableGet(GvMasterName);
			if (test == 0) GlobalVariableSet(GvMasterName, getGMTime());
			test = GlobalVariableGet(GvMasterIDName);
			if (test != 1) GlobalVariableSet(GvMasterIDName, 1);
		}
}
//------------------------------------------------------
void getColors()
{
	colorTable[0] = GoldFix;
}
// --------------------------------
void newTimeTable()
{
	int i, j;
	datetime today = StrToTime(TimeToStr(TimeLocal(), TIME_DATE));
	
	// All times London time
	phaseTime[0][startTime] = StrToTime("10:30"); phaseTime[0][endTime] = StrToTime("15:00");

	int localOffset = 0 - today;
	for (i=0; i<phaseCount; i++)
		{
			phaseTime[i][startTime] = phaseTime[i][startTime]	+ localOffset;
			phaseTime[i][endTime]	= phaseTime[i][endTime]		+ localOffset;	
		}
}
// --------------------------------
int init()
{
	masterID = StringConcatenate("Gold Fix is Master on ",Symbol(),Period());
	GvMasterIDName = masterID;
	standalone = false;
	currentBarCount = Bars;
	previousBarCount = currentBarCount;
	
	// This is only to keep with the common code... I don't use stayAway offset when displaying as lines
	/*
	if(Period() > PERIOD_M15)
		{
			stayAwaySecondsBefore = 0;
			stayAwaySecondsAfter = 0;
		}
	else
		{
			stayAwaySecondsBefore = StayAwayMinutesBefore*60;
			stayAwaySecondsAfter = StayAwayMinutesAfter*60;
		}
	*/
	stayAwaySecondsBefore = 0;
	stayAwaySecondsAfter = 0;
	
	updateTimeSettings();
	newTimeTable();
	
	nameTable[0][0] = PhaseName + "_1"; nameTable[0][1] = "London Gold Fix";
	
	getColors();
	
	currentBar = Time[0];
	previousBar = currentBar;
	
	deleteObjects();
	createObjects();
	return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
	deleteObjects();
	return(0);
}
// --------------------------------
void createObjects()
{
	string name;
	int i;
	for (dayIndex = 0; dayIndex < DaysBack+1; dayIndex++)
		{
			int dayOffset = iTime(Symbol(), PERIOD_D1, dayIndex);
			// strip the dayOffset from hours, minutes and seconds
			dayOffset -= TimeHour(dayOffset)*3600 + TimeMinute(dayOffset)*60 + TimeSeconds(dayOffset);
			dayOffset += (BrokerDefaultTZ + isBrokerOnDST(dayOffset))*3600;
			int sessionOffset = dayOffset - isEuropeOnDST(dayOffset)*3600;
			
			for (i=0; i< phaseCount; i++)
				{
					name = nameTable[i][0] + dayIndex;

					datetime startDateTime 	= phaseTime[i][startTime] + sessionOffset;
					datetime endDateTime 	= phaseTime[i][endTime] + sessionOffset;

					// some issue with lines appearing on Sudnay...
					if (TimeDayOfWeek(startDateTime) > 0 && TimeDayOfWeek(startDateTime) <6)
						{
							drawVLine(name+0, startDateTime, colorTable[i], nameTable[i][1]);
							if (i==phaseCount-1)
								{
									drawVLine(name+1, endDateTime, colorTable[i], nameTable[i][1]);
								}
						}
				}
		}

}
// --------------------------------
void updateObjects()
{
	currentBar = iTime(Symbol(), PERIOD_H1,0); // once an hour should be ok. iTime(Symbol(), PERIOD_D1,0);

	bool updateEveryMinute = false;
	if (startupCount < startupPeriod)
		{
			if (newM1bar) updateEveryMinute = true;	else updateEveryMinute = false;
		}
	else updateEveryMinute = false;
	
	if (currentBar >previousBar || updateEveryMinute || forceUpdate)
		{
			previousBar = currentBar;
			deleteObjects();
			createObjects();
		}
}
// --------------------------------
void deleteObjects()
{
	string name;
	int i;
	for (dayIndex = 0; dayIndex < DaysBack+1; dayIndex++)
		{
			for (i=0; i< phaseCount; i++)
				{
					name = nameTable[i][0] + dayIndex;
					ObjectDelete(name+0);
					ObjectDelete(name+1);
				}
		}
}
// ---------------------------------
void drawVLine(string name, datetime time1, color lineColor, string description)
{		
	if (ShowAsVLine) ObjectCreate(name, OBJ_VLINE, 0, time1, NULL, NULL, NULL, NULL, NULL);
	else
		{
			ObjectCreate(name, OBJ_TREND, 0, time1, 0, time1, 0.5, NULL, NULL);
			ObjectSet(name, OBJPROP_RAY, true);
		}	
	ObjectSet(name, OBJPROP_COLOR, lineColor);
	if (ShowDescription) ObjectSetText(name, description);
	ObjectSet(name, OBJPROP_STYLE, STYLE_DOT);
	ObjectSet(name, OBJPROP_BACK, true);
}
// --------------------------------
void Alarm(string alarmDescription)
{
	string altertText = alarmDescription;
	Alert(altertText);		
	Print("-----------------------------------");
	altertText = ">>>>>>>  " + altertText;
	Print(altertText);
	Print("-----------------------------------");
}
// --------------------------------
int readGV(string GvName)
{
	int result = EMPTY_VALUE;
	if (GlobalVariableCheck(GvName))
		{
			result = GlobalVariableGet(GvName);
		}
	return (result);
}
// --------------------------------
bool checkUpdateSchedule()
{
	// check if any master exists, if not, save the BrokerOffset as in Master mode.
	int isThereAnyMaster = readGV(GvMasterName);
	if (isThereAnyMaster > 0 && isThereAnyMaster != EMPTY_VALUE) standalone = false; else standalone = true;

	bool update = false;
	int newTZ = readGV(GvTzName);
	if (newTZ != BrokerOffset)
		{
			if (newTZ > -11 && newTZ < 13)
				{
					Print ("Found new BrokerOffset value: ", newTZ, " while BrokerOffset ", BrokerOffset);
					if (!MasterMode && !standalone) BrokerOffset = newTZ;
					update = true;
				}
		}
	// read DST settings, Broker TZ is updated automatically when feed active
	int newStart = readGV(GvDstOnName);
	int newEnd = readGV(GvDstOffName);
	if (newStart != EMPTY_VALUE && newEnd != EMPTY_VALUE && newStart*newEnd != NULL)
		{
			datetime currentStart = StrToTime(BrokerGoesOnDST);
			datetime currentEnd = StrToTime(BrokerGoesOffDST);
			
			if (currentStart != newStart)
				{
					if (!MasterMode && !standalone) BrokerGoesOnDST = TimeToStr(newStart, TIME_DATE|TIME_MINUTES);
					Print ("Found new BrokerGoesOnDST date: ", TimeToStr(newStart, TIME_DATE|TIME_MINUTES));//BrokerGoesOnDST);
					update = true;
				}
				
			if (currentEnd != newEnd)
				{
					if (!MasterMode && !standalone) BrokerGoesOffDST = TimeToStr(newEnd, TIME_DATE|TIME_MINUTES);
					Print ("Found new BrokerGoesOffDST date: ", TimeToStr(newEnd, TIME_DATE|TIME_MINUTES));//BrokerGoesOffDST);
					update = true;
				}
		}
		
	currentBarCount = Bars;
	if (currentBarCount != previousBarCount)
		{
			if (currentBarCount > previousBarCount +1 || currentBarCount < previousBarCount)
				{
					Print ("Discontinued history detected");
					startupCount = -1; // I will have to follow it for a while
					update = true;
				}
			previousBarCount = currentBarCount;
		}

	if (startupCount < startupPeriod) if (newM1bar) startupCount ++;
	
	return(update);
}
// --------------------------------
int start()
  {
	currentBrokerTime = iTime(NULL, PERIOD_M1, 0);
	if (currentBrokerTime >  previousBrokerTime) newM1bar = true; else newM1bar = false;
	updateMasterFlags();
	forceUpdate = checkUpdateSchedule();
	updateTimeSettings();
	updateObjects();
	forceUpdate = false; // clear flag
	previousBrokerTime = currentBrokerTime;
	
	int i;
	updateObjects();
	datetime currentBarTime = iTime(NULL, PERIOD_M1, 0);
   	if(EnableAlarm)
   		{
   			for (i=0; i< phaseCount; i++)
   				{
   					if(currentBarTime == phaseTime[i][startTime] - FireAlarmMinutesBefore * 60)
   						{
   							if(alarmFired != currentBarTime) 
   								{
   									Alarm(nameTable[i][1]);
   									alarmFired = currentBarTime;
   								}
   							
   						}
   				}
   		}
   return(0);
  }
//+------------------------------------------------------------------+