//+------------------------------------------------------------------+
//|                                                     strMeter.mq4 |
//|                                                          flotsom |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_color1	Lime
#property indicator_color2	Yellow
#property indicator_color3	DarkOrange
#property indicator_color4	Blue
#property indicator_color5	FireBrick
#property indicator_color6	White
#property indicator_color7	White
#property indicator_color8	Red

#define MAX_LEVELS			10
#define X_BARSPACING		22
#define Y_BARSPACING		6
#define BAR_CODE			175
#define X_BASELINE0			0
#define X_BASELINE1			200
#define X_BASELINE2			400
#define X_CUROFF			12
#define X_DIVOFF			7
#define Y_BASELINE0			87
#define Y_BASELINE1			60
#define Y_BASELINE2			8
#define FTSIZE_BAR			62
#define FTSIZE_CUR			9
#define FTSIZE_SYM			7
#define SCALE				10000

#define MAX					100
#define MIN					0

#property indicator_maximum	MAX
#property indicator_minimum	MIN

extern string	Currencies		= "EUR,GBP,USD,JPY,CHF,CAD,AUD,NZD";
extern string	SymbolFixes		= "";									//for irregular Symbols
extern int		TimeFrame		= 0;
extern int		StrSmoothing	= 3;
extern int		ChgSmoothing	= 2;
extern int		ChangePeriod	= 5;
extern int		BaseStr			= 120;
extern int		BaseChg			= 30;
extern int		ValueOfBar		= 1;
extern string	StrLevels		= "-45,-35,-25,-15,-5,5,15,25,35,45";
extern string	ChgLevels		= "1,2,3,5,8,13,21,34,55";
extern int		MinDivChgLevel	= 5;
extern bool		AllowAlert		= false;
extern bool		AllowSound		= false;
extern bool		RefreshOnTick	= true;
extern int		RefreshInterval	= 5;
extern string	MeterPosition	= "0,0";
extern color	StrClr			= Blue;
//extern color	BearStrClr		= DeepPink;
extern color	BullChgClr		= Green;
extern color	BearChgClr		= Red;
extern color	NullClr			= Gray;
extern color	TextClr			= White;


int		gCurs,gSyms,gStrLevels,gChgLevels,gDivs;
int		gBC,gTF,gWindow,gXoff,gYoff;
string	gCur[],gSym[];
int		gSymCurLft[],gSymCurRgt[];
double	gPrBuf[],gMaBuf[],gSymWgt[],gCurWgt[];
double	gStrLevel[MAX_LEVELS],gChgLevel[MAX_LEVELS];
string	gObjPrefix,gPrfx,gSufx;

int init()
{
	gBC=0;
	setPairs();
	setMeterLevels();
	int id;
	id=newID();
	gObjPrefix=StringConcatenate(id,"_strM_");
	gTF=TimeFrame;
	IndicatorShortName("");
	return(0);
}

//deinitialization
int deinit()
{
	gWindow=WindowOnDropped();
	ObjectsDeleteAll(gWindow);
	return(0);
}

int start()
{
	static datetime last_refresh_time=0;
	
	if (gBC==iBars(Symbol(),gTF)) {
		if (!RefreshOnTick && last_refresh_time+RefreshInterval>TimeCurrent()) return(0);
	}
	last_refresh_time=TimeCurrent();
	gBC=iBars(Symbol(),gTF);
	gWindow=WindowOnDropped();
	ObjectsDeleteAll(gWindow);
	if (gWindow==-1) {
		Print("window error");
		return(0);
	}
	drawMeter();
	drawTexts();
	
	return(0);
}

//Draw Begin
void drawLabel(string name, int x, int y, 
				string text, color clr, 
				int fontsize=FTSIZE_BAR, string font="consolas")
{
//	ObjectDelete(name);
	ObjectCreate(name,OBJ_LABEL,gWindow,0,0);
	ObjectSetText(name,text,fontsize,font,clr);
	ObjectSet(name,OBJPROP_XDISTANCE,x+gXoff);
	ObjectSet(name,OBJPROP_YDISTANCE,y+gYoff);
}

void drawTexts()
{
	int i;
	string name;

	for (i=0;i<gCurs;i++) {
		name=StringConcatenate(gObjPrefix,0,"_",i,"_SYM");
		drawLabel(name,i*X_BARSPACING+X_BASELINE0+X_CUROFF,Y_BASELINE0,gCur[i],TextClr,FTSIZE_CUR);
		name=StringConcatenate(gObjPrefix,1,"_",i,"_SYM");
		drawLabel(name,i*X_BARSPACING+X_BASELINE1+X_CUROFF,Y_BASELINE0,gCur[i],TextClr,FTSIZE_CUR);
	}
}

void drawBars(int section,int isym,int level,color clr)
{
	int i,x;
	static int offsetX[]={X_BASELINE0,X_BASELINE1,X_BASELINE2};

	string name;
	x=offsetX[section]+isym*X_BARSPACING;
	for (i=0;i<level;i++) {
		name=StringConcatenate(gObjPrefix,section,"_",isym,"_",i);
		drawLabel(name,x,Y_BASELINE1-i*Y_BARSPACING,CharToStr(BAR_CODE),clr);
	}
	while (i<MAX_LEVELS) {
		name=StringConcatenate(gObjPrefix,section,"_",isym,"_",i);
		drawLabel(name,x,Y_BASELINE1-i*Y_BARSPACING,CharToStr(BAR_CODE),NullClr);
		i++;
	}
	return;
}

void drawMeter()
{
	int i,j,level,lvl[];
	double value,val[];
	int smoothing,prbuflen,mabuflen;
	
	if (StrSmoothing<1) smoothing=1;
	else smoothing=StrSmoothing;
	mabuflen=ValueOfBar+1;
	prbuflen=mabuflen+smoothing;
	calc(ValueOfBar,ValueOfBar,BaseStr,prbuflen,mabuflen,smoothing);

	for (i=0;i<gCurs;i++) {
		value=gMaBuf[i*mabuflen+ValueOfBar];
		level=calcStrLevel(value);
		drawBars(0,i,level,StrClr);
		//else drawBars(0,i,level,BearStrClr);
	}

	if (ChgSmoothing<1) smoothing=1;
	else smoothing=ChgSmoothing;
	mabuflen=ValueOfBar+ChangePeriod+1;
	prbuflen=mabuflen+smoothing;
	calc(ValueOfBar,ValueOfBar+ChangePeriod,BaseChg,prbuflen,mabuflen,smoothing);
	ArrayResize(val,gCurs);
	ArrayResize(lvl,gCurs);
	for (i=0;i<gCurs;i++) {
		value=gMaBuf[i*mabuflen+ValueOfBar]-gMaBuf[i*mabuflen+ValueOfBar+ChangePeriod];
		val[i]=value;
		level=calcChgLevel(MathAbs(value));//
		lvl[i]=level;
		if (value>0) drawBars(1,i,level,BullChgClr);
		else drawBars(1,i,level,BearChgClr);
	}
	
	string sym,name;
	color clr;
	int k,symidx;
	k=0;
	for (i=0;i<gCurs;i++) {
		if (lvl[i]<MinDivChgLevel) continue;
		for (j=i+1;j<gCurs;j++) {
			if (lvl[j]<MinDivChgLevel || val[i]*val[j]>0) continue;
			value=MathSqrt((SCALE+val[i])/(SCALE+val[j]))*SCALE-SCALE;
			level=calcChgLevel(MathAbs(value));
			if (level>=MinDivChgLevel) {
				sym=makeSym(i,j);
				symidx=lookupSym(sym);
				if (symidx<0) {
					sym=makeSym(j,i);
					symidx=lookupSym(sym);
					if (symidx<0) continue;
					if (value>0) clr=BearChgClr;
					else clr=BullChgClr;
				} else if (value>0) clr=BullChgClr;
				else clr=BearChgClr;
				drawBars(2,k*2,level,clr);
				name=StringConcatenate(gObjPrefix,"_",k,"_DIV");
				drawLabel(name,k*2*X_BARSPACING+X_BASELINE2+X_DIVOFF,Y_BASELINE0,sym,TextClr,FTSIZE_SYM);
				doNotify(sym,level);
				k++;
			}
		}
	}
}

void doNotify(string sym, int level)
{
	if (AllowAlert) {
		Alert("[",sym,"] is diverging at level ", level);
	}
	if (AllowSound) {
		PlaySound("alert.wav");
	}
}
//Draw End

//| Strength calc Begin
void calc(int start,int end,int base_bar,
		int prbuflen, int mabuflen, int smoothing)
{
	int i,j,l,r;
	double ratio,base;
	double wgt[];
	
	ArrayResize(wgt,gCurs);
	for (i=0;i<gCurs;i++) wgt[i]=gCurWgt[i];
	
	ArrayInitialize(gPrBuf,0);
	for (i=0;i<gSyms;i++) {
		l=gSymCurLft[i];
		r=gSymCurRgt[i];
		base=iClose(gSym[i],gTF,base_bar);
		if (0==base) {
			wgt[l]-=gSymWgt[i];
			wgt[r]-=gSymWgt[i];
			continue;
		}
		l*=prbuflen;
		r*=prbuflen;
		for (j=0;j<prbuflen;j++) {
			ratio=iClose(gSym[i],gTF,j)/base;
			gPrBuf[l+j]+=gSymWgt[i]*ratio;
			gPrBuf[r+j]+=gSymWgt[i]/ratio;
		}
	}
	for (i=0;i<gCurs;i++) {
		l=i*prbuflen;
		for (j=0;j<prbuflen;j++) {
			if (0==wgt[i]) {
				gPrBuf[l+j]=0;
				continue;
			}
			gPrBuf[l+j]/=wgt[i];
			gPrBuf[l+j]-=SCALE;
		}
	}
	if (smoothing<2) {
		for (i=0;i<gCurs;i++) {
			gMaBuf[i*mabuflen+start]=gPrBuf[i*prbuflen+start];
			gMaBuf[i*mabuflen+end]=gPrBuf[i*prbuflen+end];
		}
		return;
	}
	double sum;
	for (i=0;i<gCurs;i++) {
		sum=0;l=i*prbuflen;
		for(j=0;j<=smoothing;j++) sum+=gPrBuf[l+j];
		for(j=0;j<=end;j++) {
			gMaBuf[i*mabuflen+j]=sum/smoothing;
			sum-=gPrBuf[l+j];
			sum+=gPrBuf[l+j+smoothing];
		}
	}
}

int calcStrLevel(double level)
{
	int i=0;
	while (i<gStrLevels && level>gStrLevel[i]) 
		i++;
	return (i);
}

int calcChgLevel(double level)
{
	int i=0;
	while (i<gChgLevels && level>gChgLevel[i]) 
		i++;
	return (i);
}
//| Strength calc End

//| Init settings Begin
bool testSym(string sym)
{
	GetLastError();
	if (iBars(sym,gTF)>0) return (true);
	int error=GetLastError();
	if (4066==error) {
		Print("Waiting for data of [",sym,"].");
		return (true);
	}
//	if (error!=0) Print("Error ",error," occured testing Symbol [",sym,"]");
	return (false);
}

int lookupSym(string sym)
{
	for (int i=0;i<gSyms;i++)
		if (gSym[i]==sym) return (i);
//	int error=GetLastError();
//	if (error!=0) Print("Error ",error," occured looking up Symbol [",sym,"], i=",i,"|gSyms=",gSyms);
	return (-1);
}

bool findNaddPair(int left,int right,int c)
{
	string sym=makeSym(left,right);
	int s=lookupSym(sym);
	if (s<0) {
		if (testSym(sym)) {
			gSym[gSyms]=sym;
			gSymCurLft[gSyms]=left;
			gSymCurRgt[gSyms]=right;
			gSyms++;
		} else return(false);
	}
//	Print("Symbol [",sym,"] found for currency [",gCur[c],"].");
	return (true);
}

void setPairs()
{
	int i,j,k,s;
	string current,workstr;
	
	workstr = normalizeStr(SymbolFixes,",");
	s=0;i=StringFind(workstr,",",s);
	if (i>0) gPrfx = StringSubstr(workstr,s,i-s);
	else gPrfx="";
	s=i+1;i=StringFind(workstr,",",s);
	if (i>s) gSufx = StringSubstr(workstr,s,i-s);
	else gSufx="";

	workstr = normalizeStr(Currencies,",");
	s = 0;gCurs=0;
	i = StringFind(workstr,",",s);
	while (i > 0)
	{
		if (i-s>0) {
			current = stringUpperCase(StringSubstr(workstr,s,i-s));
			gCurs++;
			ArrayResize(gCur,gCurs);
			gCur[gCurs-1]=current;
		}
		s = i + 1;
		i = StringFind(workstr,",",s);
	}
	ArrayResize(gSym,(gCurs-1)*gCurs/2);
	ArrayResize(gSymCurLft,(gCurs-1)*gCurs/2);
	ArrayResize(gSymCurRgt,(gCurs-1)*gCurs/2);
	ArrayResize(gSymWgt,(gCurs-1)*gCurs/2);
	ArrayResize(gMaBuf,(ValueOfBar+ChangePeriod+1)*gCurs);
	if (StrSmoothing>ChgSmoothing+ChangePeriod) s=StrSmoothing+1;
	else s=ChgSmoothing+ChangePeriod+1;
	if (s<1) s=1;
	ArrayResize(gPrBuf,(ValueOfBar+s)*gCurs);
	ArrayResize(gCurWgt,gCurs);
//	ArraySetAsSeries(gPrBuf,true);

	gSyms=0;
	for (i=0;i<gCurs;i++) {
		k=0;current="";
		for (j=0;j<gCurs;j++) {
			if (i==j) continue;
			if (findNaddPair(i,j,i)) {k++;current=current+"["+makeSym(i,j)+"]";continue;}
			if (findNaddPair(j,i,i)) {k++;current=current+"["+makeSym(j,i)+"]";continue;}
		//	Print("No Symbol found for ",gCur[i]," vs ",gCur[j]," not found.");
		}
		if (k>0) {
			Print (k," Symbols found for currency [",gCur[i],"]:",current);
			continue;
		}
		Print ("No symbol found for currency [",gCur[i],"], removed from list.");
		gCurs--;
		for (j=i;j<gCurs;j++) gCur[j]=gCur[j+1];
		i--;
	}//*/
	ArrayInitialize(gCurWgt,0);
	ArrayInitialize(gSymWgt,100);
	for (i=0;i<gSyms;i++) {
		gCurWgt[gSymCurLft[i]]+=gSymWgt[i];
		gCurWgt[gSymCurRgt[i]]+=gSymWgt[i];
		gSymWgt[i]*=SCALE;
	}
	Print("Symbols setting finished.");
}//*/

void setMeterLevels()
{
	int i,j,s;
	double value;
	string current,workstr;

	gStrLevels=0;
	workstr = normalizeStr(StrLevels,",");
	s=0;i = StringFind(workstr,",",s);
	while (i>0 && MAX_LEVELS>gStrLevels)
	{
		value = StrToDouble(StringSubstr(workstr,s,i-s));
		if (true) {
			for (j=gStrLevels;j>0 && gStrLevel[j-1]>value;j--) 
				gStrLevel[j]=gStrLevel[j-1];
			gStrLevel[j]=value;
		//	Print("Add level [",DoubleToStr(value,2),"].");
			gStrLevels++;
		}
		s = i + 1;
		i = StringFind(workstr,",",s);
	}
	gChgLevels=0;
	workstr = normalizeStr(ChgLevels,",");
	s=0;i = StringFind(workstr,",",s);
	while (i>0 && MAX_LEVELS>gChgLevels)
	{
		value = StrToDouble(StringSubstr(workstr,s,i-s));
		if (value > 0) {
			for (j=gChgLevels;j>0 && gChgLevel[j-1]>value;j--) 
				gChgLevel[j]=gChgLevel[j-1];
			gChgLevel[j]=value;
		//	Print("Add level [",DoubleToStr(value,2),"].");
			gChgLevels++;
		}
		s = i + 1;
		i = StringFind(workstr,",",s);
	}

	workstr = normalizeStr(MeterPosition,",");
	s=0;i=StringFind(workstr,",",s);
	gXoff = StrToInteger(StringSubstr(workstr,s,i-s));
	s=i+1;i=StringFind(workstr,",",s);
	gYoff = StrToInteger(StringSubstr(workstr,s,i-s));

}//*/

int newID()
{
	int id;
	if(!GlobalVariableCheck("id")) {
		id=0;
	} else id=1+GlobalVariableGet("id");
	GlobalVariableSet("id",id);
	
	return(id);
}//*/

//| Init settings End
string makeSym(int left, int right)
{
	return (StringConcatenate(gPrfx,gCur[left],gCur[right],gSufx));
}

string normalizeStr(string s,string div)
{
	string workstr = StringTrimLeft(StringTrimRight(s));
	if (StringSubstr(workstr,StringLen(workstr),1) != div)
		workstr = StringConcatenate(workstr,div);
	return (workstr);
}

string stringUpperCase(string str)
{
	string s=str;
	int tchar,lenght=StringLen(str)-1;
   
	while(lenght>=0) {
		tchar=StringGetChar(s,lenght);
		if((tchar>96 && tchar<123) || (tchar>223 && tchar<256))
			s=StringSetChar(s,lenght,tchar-32);
		else if(tchar>-33 && tchar<0)
			s=StringSetChar(s,lenght,tchar+224);
		lenght--;
	}
	
	return(s);
}