//+------------------------------------------------------------------+
//|                                              BK's FFCalendar.mq4 |
//|                                                       2008.08.11 |
//|                                                       BurgerKing |
//+------------------------------------------------------------------+
#property copyright "BurgerKing"
#property link      ""

#property show_inputs
#property indicator_chart_window

#property indicator_buffers 0


extern int	MaxNews = 3;
extern int	SecOldNews = 3600;
extern int	SecUpdateNews = 30;
extern string	NewsFilter = "Low;Medium;High";

extern color 	TxtColorTitle 		= LightGray;
extern color 	TxtColorNews 		= DeepSkyBlue;
extern color 	TxtColorImpact 		= Red;
extern color 	TxtColorPrevious 	= LimeGreen;
extern color 	TxtColorForecast 	= RoyalBlue;

extern int	DebugLevel		= 0;

int	TimeShift;

//=================================================================================================
//====================================   TimeGMT Functions   ======================================
//=================================================================================================
#import "kernel32.dll"
int	GetTimeZoneInformation(int& TZInfoArray[]);
int	TZInfoArray[43];
#import
int TimeGMT() {
	int DST = GetTimeZoneInformation(TZInfoArray);
	if (DST == 1) {DST = 3600;}
	else {DST = 0;}
	return( TimeLocal()+DST+TZInfoArray[0]*60 );
}
//=================================================================================================
//====================================   TimeFetchCalendar Functions   ======================================
//=================================================================================================

int hSession_IEType;
int hSession_Direct;
int Internet_Open_Type_Preconfig = 0;
int Internet_Open_Type_Direct = 1;
int Internet_Open_Type_Proxy = 3;

#import "wininet.dll"

#define INTERNET_FLAG_PRAGMA_NOCACHE    0x00000100 // Forces the request to be resolved by the origin server, even if a cached copy exists on the proxy.
#define INTERNET_FLAG_NO_CACHE_WRITE    0x04000000 // Does not add the returned entity to the cache. 
#define INTERNET_FLAG_RELOAD            0x80000000 // Forces a download of the requested file, object, or directory listing from the origin server, not from the cache.

int InternetOpenA(
	string	sAgent,
	int	lAccessType,
	string	sProxyName="",
	string	sProxyBypass="",
	int	lFlags=0
);

int InternetOpenUrlA(
	int	hInternetSession,
	string	sUrl, 
	string	sHeaders="",
	int	lHeadersLength=0,
	int	lFlags=0,
	int	lContext=0 
);

int InternetReadFile(
	int	hFile,
	string	sBuffer,
	int	lNumBytesToRead,
	int&	lNumberOfBytesRead[]
);

int InternetCloseHandle(
	int	hInet
);
#import

int hSession(bool Direct) {
	string InternetAgent;
	if (hSession_IEType == 0) {
		InternetAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;)";
		hSession_IEType = InternetOpenA(InternetAgent, Internet_Open_Type_Preconfig, "0", "0", 0);
		hSession_Direct = InternetOpenA(InternetAgent, Internet_Open_Type_Direct, "0", "0", 0);
	}
	if (Direct) { 
		return(hSession_Direct); 
	} else {
		return(hSession_IEType); 
	}
}

bool GrabWeb(string URL, string& WebPage) {
	int 	lReturn[1];
	string	sBuffer = "....:....A....:....B....:....C....:....D....:....E....:....F....:....G....:....H....:....I....:....J....:....K....:....L....:....M....:....N....:....O"; // not more than 255 spaces
	int	BufferLen = StringLen(sBuffer);

	if (DebugLevel > 0) Print("GrabWeb: ", URL);
	int hInternet = InternetOpenUrlA(hSession(FALSE), URL, "0", 0, INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD, 0);
	WebPage = "";
	if (hInternet != 0) {
		if (DebugLevel > 0) Print("Loading URL.. Please wait.");
		while (InternetReadFile(hInternet, sBuffer, BufferLen, lReturn) != 0) {
			if (lReturn[0] == 0) break;
			WebPage = WebPage + StringSubstr(sBuffer, 0, lReturn[0]);
		}
		InternetCloseHandle(hInternet);
		if (DebugLevel > 0) Print("Done Loading.");
		return(true);
	} else {
		return(false);
	}
}

//================================================================================================
//======================================== XML Calendar ==========================================
//================================================================================================

string 	NewsData[150][10];
int	NewsIndex 	= 0;	//no news on record
#define DATE		0
#define TIME		1
#define CURRENCY	2
#define TITLE		3
#define IMPACT		4
#define PREVIOUS	5
#define FORECAST	6
#define ACTUAL		7
#define DATETIME	8
string	sTags[10] = {  "<date>",  "<time>",  "<country>",  "<title>",  "<impact>",  "<previous>",  "<forecast>",  "<actual>",  "<datetime>", "" };
string	eTags[10] = { "</date>", "</time>", "</country>", "</title>", "</impact>", "</previous>", "</forecast>", "</actual>", "</datetime>", "" };
string  Calendar = "";


void ParseXML(string Data) {
	int iX=0;
	NewsIndex = 0;	//reset global variable News Counter
	while (true) {
		int x,y;
		//XML Entry Format
		//	<event>
		//		<title>Manufacturing Sales q/q</title>
		//		<country>NZD</country>
		//		<date>06-15-2008</date>
		//		<time>10:49pm</time>
		//		<impact>Low</impact>
		//		<forecast></forecast>
		//		<previous>8.3%</previous>
		//		<actual>8.3%</actual>
		//	</event>

		x = StringFind(Data, "<event>", iX);	//start of an event entry
		if (x < 0) break;			//done
		y = StringFind(Data, "</event>", x);	//end of an event entry
		if (y < 0) break;			//done
		iX = y+8;				//move iX to next entry

		string thisEvent = StringSubstr(Data, x, y-x);	//copy this event to thisEvent

		for (int i = 0; sTags[i] != ""; i++) {
			NewsData[NewsIndex][i] = "";
			x = StringFind(thisEvent, sTags[i]);
			if (x < 0) continue;		//error: did not find this key
			y = StringFind(thisEvent, eTags[i], x);
			if (y <= x) continue;		//error: no end tag match
			x = x + StringLen(sTags[i]);
			NewsData[NewsIndex][i] = StringSubstr(thisEvent,x,y-x);
		}
		NewsData[NewsIndex][DATE] = StringSubstr(NewsData[NewsIndex][DATE],6,4) +  "-"  + StringSubstr(NewsData[NewsIndex][DATE],0,5);

		x = StringFind(NewsData[NewsIndex][TIME],":");
		if (x > 0) {
			int hh = StrToInteger(StringSubstr(NewsData[NewsIndex][TIME],0,x));
			if ( StringFind(NewsData[NewsIndex][TIME],"pm")>0 && hh<12 ) {
				hh = hh + 12;
			}
			NewsData[NewsIndex][TIME] = DoubleToStr(hh,0) + StringSubstr(NewsData[NewsIndex][TIME],x,3);
		}
		NewsData[NewsIndex][DATETIME] = NewsData[NewsIndex][DATE] + " " + NewsData[NewsIndex][TIME];

		if (DebugLevel > 1) Print(NewsIndex, ": ", NewsData[NewsIndex][DATE], " ", NewsData[NewsIndex][TIME], " ", NewsData[NewsIndex][CURRENCY], " ", NewsData[NewsIndex][TITLE]);
		NewsIndex++;
	}
}

int SaveXML(string& Data) {
	int FileHandle = FileOpen("BK\'s FFCalendar.xml", FILE_BIN|FILE_WRITE);
	if (FileHandle < 1) return(0);
	FileWriteString(FileHandle, Data, StringLen(Data));
	FileClose(FileHandle);
	return(1);
}

int ReadXML(string& Data) {
	Data = "";
	int FileHandle = FileOpen("BK\'s FFCalendar.xml", FILE_BIN|FILE_READ);
	if (FileHandle < 1) return(0);
	while (true) {
		string temp = FileReadString(FileHandle, 250);
		if (temp == "") break;
		Data = Data + temp;
	}
	FileClose(FileHandle);
	return(StringLen(Data));
}

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init() {
	MathSrand(TimeLocal() * Bid);
	SecUpdateNews = MathMin( MathMax(SecUpdateNews,60), 10800);	//15 minutes to 3 Hours only
	return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit() {
	for (int iX=0; iX < NewsIndex; iX++) {
		ObjectDelete("Title_"  +iX);
		ObjectDelete("Header_"  +iX);
		ObjectDelete("Impact_"  +iX);
		ObjectDelete("Previous_"+iX);
		ObjectDelete("Forecast_"+iX);
	}

	return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int TimeReadXML=0, TimeUpdateDisplay=0;
int CurY;
int Xcoor = 0, Ycoor = 0;
int start() {
	TimeShift = TimeLocal() - TimeGMT();

	if (TimeLocal() > NormalizeDouble(GlobalVariableGet("TimeFetchCalendar"),0)) {		//Fetch FF Calendar every 6 Hours
		GlobalVariableSet("TimeFetchCalendar", TimeLocal() + 3600*6);
		GrabWeb("http://www.forexfactory.com/ff_calendar_thisweek.xml", Calendar);	//Get FF's XML Calendar
		SaveXML(Calendar);
		TimeReadXML = 0;
		
	}
	if (TimeLocal() > TimeReadXML) {
		TimeReadXML = TimeLocal() + SecUpdateNews;
		ReadXML(Calendar);
		ParseXML(Calendar);
	}

	if (ObjectFind("Title_0") < 0) {	//Create Title Label if not available
		LABEL("Title_0", "BK\'S FOREX FACTORY CALENDAR", "Arial Bold", 0, 0, TxtColorTitle);
	}

	//Display News items below Title Label
	string	C1 = StringSubstr(Symbol(), 0, 3);
	string	C2 = StringSubstr(Symbol(), 3, 3);
	Xcoor = ObjectGet("Title_0",OBJPROP_XDISTANCE)-5;
	Ycoor = ObjectGet("Title_0",OBJPROP_YDISTANCE)-15;

	string	temp;
	int iX, iZ, x,y;
	for (iX=0; iX<50; iX++) {
		ObjectDelete("Header_"  +iX);
		ObjectDelete("Impact_"  +iX);
		ObjectDelete("Previous_"+iX);
		ObjectDelete("Forecast_"+iX);
		ObjectDelete("NewsLine_"+iX);
	}
	for (iX=0,iZ=0; iZ<MaxNews && iX<NewsIndex; iX++) {
		x = StrToTime(NewsData[iX][DATETIME]) + TimeShift;				//convert time to LocalTime
		if (TimeLocal() > x+SecOldNews) continue;					//this news is too old
		if (NewsData[iX][CURRENCY] != C1 && NewsData[iX][CURRENCY] != C2) continue;	//not related to this news
		if (StringFind(NewsFilter, NewsData[iX][IMPACT]) < 0) continue;			//insignificant news

		temp = TimeToStr(x) + " (" + DoubleToStr( (x-TimeLocal())/60,0) + "mins) " + NewsData[iX][CURRENCY] + " " + NewsData[iX][TITLE];
						  LABEL("Header_"  +iZ, temp,					"Arial Bold",	  0, iZ*40+20, TxtColorNews);
		if (NewsData[iX][IMPACT]   != "") LABEL("Impact_"  +iZ, "Impact: "  +NewsData[iX][IMPACT],	"Arial Bold", 	  0, iZ*40+35, TxtColorImpact);
		if (NewsData[iX][PREVIOUS] != "") LABEL("Previous_"+iZ, "Previous: "+NewsData[iX][PREVIOUS],	"Arial Bold",	110, iZ*40+35, TxtColorPrevious);
		if (NewsData[iX][FORECAST] != "") LABEL("Forecast_"+iZ, "Forecast: "+NewsData[iX][FORECAST],	"Arial Bold",	220, iZ*40+35, TxtColorForecast);

		VLine("NewsLine_"+iZ, x+(MathFloor(TimeCurrent()/60)-MathFloor(TimeLocal()/60))*60, TxtColorNews, 2);
		iZ++;
	}
	if (iZ == 0) {	//No News
			LABEL("Header_0", "No News on record till next week " + StringLen(Calendar), "Arial Bold", 0, iZ*40+20, TxtColorNews);
	}

	return(0);
}

void LABEL (string name, string text, string font, int x, int y, color hue) {
	ObjectDelete(name);
	ObjectCreate(name, OBJ_LABEL, 0, 0, 0);
	ObjectSetText(name, text, 10, font, hue);
	ObjectSet(name, OBJPROP_CORNER, 0);
	ObjectSet(name, OBJPROP_XDISTANCE,  5 + x + Xcoor);
	ObjectSet(name, OBJPROP_YDISTANCE, 15 + y + Ycoor); 
}
void VLine (string ObjName, int T, color ObjColor, int width) {
	ObjectDelete(ObjName);
	ObjectCreate(ObjName, OBJ_VLINE, 0, T, 0 );
	ObjectSet   (ObjName, OBJPROP_COLOR, ObjColor);
	ObjectSet   (ObjName, OBJPROP_WIDTH,  width);
}


