/*###################################################################

Revision history:

Date      | By | Description
----------+----+------------------------------------------
14-Mar-07   RP   Created.
17-Oct-07   RP   Upgraded debug window to allow it to output into separate window.
                 This is done by setting DebugPanelInSeparateWindow to true.

####################################################################*/

//####################################################################
// These variables can safely be modified by client scripts as required
//####################################################################

// if true, helpful debug alerts will be be displayed.
var DebugAlert = false; 

// notifies every time a web service is executed. DebugAlert must be true.
var DebugAlertOnWebServiceExec = false; 

// notifies what parameters are used for a web service. Both above variables must be true.
var DebugAlertShowParamsOnExec = false;

var DebugPanelInSeparateWindow = false;

// if true, alerts will be sent to a DIV element rather than the alert box.
var UseDebugPanel = true;

// If true, then no style data will be allocated to the DebugPanel. UseDebugPanel must be set to true.
// This means the client should define CSS class definitions for div.DebugPanel, div.DebugEntry, div.DebugHeading
var DebugPanelUseCustomStyle = false; 

//####################################################################
// ChartingWebService definition and methods
//####################################################################
function ChartingWebService()
{

	var host = window.location.hostname.toLowerCase();
	if (host.indexOf('.co.uk') == -1 && host.indexOf('.com') == -1 && host.indexOf('.net') == -1 && host != 'localhost')
	{
		host += ':8057';
	}
	
	// This fix is for clients who might have an internal redirect server
	// when requesting our services. The url might be different to what we expect
	// This check ensures that the url is maintained (Example client ScottishProvident)
	// ie there might be directory structure between the hostname and clientsv21
	var url = window.location.href.toLowerCase();
	var pos1 = url.indexOf("/clients");
	if (pos1 != -1)
	{
	   var pos2 = url.indexOf(window.location.hostname.toLowerCase()) + window.location.hostname.length
	   if(pos2 != pos1)
	   {
	      host +=  window.location.href.substring(pos2,pos1);
	   }   
	}
	this.WebServiceUrl = window.location.protocol + '//' + host  + '/clientsv21/core/webservices/charting.asmx';
}


ChartingWebService.prototype.LoadPerformance = function(delegateFunc, typeCodes, periodType, priceType, methodType, sort, filter)
{
	var p = new WSParameters();

	if (periodType == null) periodType = '';
	if (priceType == null) priceType = 'TR';
	if (methodType == null) methodType = 1;

	p.Add('TypeCodes', typeCodes);
	p.Add('PeriodType', periodType);
	p.Add('PriceType', priceType);
	p.Add('MethodType', methodType);
	
	this.AddStdParams(p, sort, filter);
	this.Invoke('Performance', p, delegateFunc);
}

// Parameters are as follows:
//   calcType .......... one from [MinMax, MinMean]
//   statisticalType ... e.g. Alpha, Beta, etc.
//   period ............ period over which to make calculations, in months.
//   offset ............ how many months ago should the latest data point be?
//   step .............. in months (interval between data points).
//   number ............ number of data points to return.
//
// Example:
//   LoadRollingDiscreteRange(null, 'MinMax', 'FFISS', 'NASX', 'Alpha', 36, 0, 1, 12)
ChartingWebService.prototype.LoadRollingDiscreteRange = function(delegateFunc, calcType, typeCodes, benchmarkCode, statisticalType, period, offset, step, number)
{
	var p = new WSParameters();

	p.Add('CalcType', typeCodes);
	p.Add('TypeCodes', typeCodes);
	p.Add('BenchmarkCode', benchmarkCode);
	p.Add('StatisticalType', statisticalType);
	p.Add('Period', period);
	p.Add('Offset', offset);
	p.Add('Step', step);
	p.Add('Number', number);

	this.Invoke('GetRollingDiscrete', p, delegateFunc);
}

// Parameters are as follows:
//   statisticalType ... e.g. Alpha, Beta, etc.
//   period ............ period over which to make calculations, in months.
//   offset ............ how many months ago should the latest data point be?
//   step .............. in months (interval between data points).
//   number ............ number of data points to return.
//
// Example:
//   LoadRollingDiscrete(null, 'FFISS', 'NASX', 'Alpha', 36, 0, 1, 12)
ChartingWebService.prototype.LoadRollingDiscrete = function(delegateFunc, typeCodes, benchmarkCode, statisticalType, period, offset, step, number)
{
	var p = new WSParameters();

	p.Add('TypeCodes', typeCodes);
	p.Add('BenchmarkCode', benchmarkCode);
	p.Add('StatisticalType', statisticalType);
	p.Add('Period', period);
	p.Add('Offset', offset);
	p.Add('Step', step);
	p.Add('Number', number);

	this.Invoke('GetRollingDiscrete', p, delegateFunc);
}


ChartingWebService.prototype.LoadEquities = function(delegateFunc, sort, filter)
{
	var p = new WSParameters();
	if (sort == null) sort = '';
	this.AddStdParams(p, sort, filter);
	this.Invoke('Equities', p, delegateFunc);
}

ChartingWebService.prototype.LoadInstruments = function(delegateFunc, typeCodes, sort, filter)
{
	var p = new WSParameters();
	p.Add('TypeCodes', typeCodes);
	if (sort == null) sort = '';
	this.AddStdParams(p, sort, filter);
	this.Invoke('Instruments', p, delegateFunc);
}

ChartingWebService.prototype.LoadUnitsByManagerCode = function(delegateFunc, categories, managerCodes, sort, filter, client, customSelect, customSelectOptions)
{
	var p = new WSParameters();
	p.Add('Categories', categories);
	p.Add('ManagerCodes', managerCodes);
	if (sort == null) sort = '';
	this.AddStdParams(p, sort, filter);
	if (validCustomListOptions(client, customSelect, customSelectOptions)){
		this.AddCustomListParams(p, client, customSelect, customSelectOptions);
		this.Invoke('Units_ByManagerCode_Custom', p, delegateFunc);
	}
	else{
		this.Invoke('Units_ByManagerCode', p, delegateFunc);
	}
}

ChartingWebService.prototype.LoadUnitsBySectorClassCode = function(delegateFunc, categories, sectorClassCodes, sort, filter)
{
	var p = new WSParameters();
	p.Add('Categories', categories);
	p.Add('SectorClassCodes', sectorClassCodes);
	if (sort == null) sort = '';
	this.AddStdParams(p, sort, filter);
	this.Invoke('Units_BySectorClassCode', p, delegateFunc);
}

ChartingWebService.prototype.LoadUnitsByFundCode = function(delegateFunc, fundCodes, sort, filter)
{
	var p = new WSParameters();
	p.Add('FundCodes', fundCodes);
	if (sort == null) sort = '';
	this.AddStdParams(p, sort, filter);
	this.Invoke('Units_ByFundCode', p, delegateFunc);
}

ChartingWebService.prototype.LoadFundsByManagerCode = function(delegateFunc, categories, managerCodes, sort, filter)
{
	var p = new WSParameters();
	p.Add('Categories', categories);
	p.Add('ManagerCodes', managerCodes);
	if (sort == null) sort = '';
	this.AddStdParams(p, sort, filter);
	this.Invoke('Funds_ByManagerCode', p, delegateFunc);
}

ChartingWebService.prototype.LoadFundsBySectorClassCode = function(delegateFunc, categories, sectorClassCodes, sort, filter)
{
	var p = new WSParameters();
	p.Add('Categories', categories);
	p.Add('SectorClassCodes', sectorClassCodes);
	if (sort == null) sort = '';
	this.AddStdParams(p, sort, filter);
	this.Invoke('Funds_BySectorClassCode', p, delegateFunc);
}

ChartingWebService.prototype.LoadManagers = function(delegateFunc, categories, sort, filter, client, customSelect, customSelectOptions)
{
	var p = new WSParameters();	
	if (filter == null) filter = '';
	if (sort == null) sort = '';
	// Due to case-sensitivity, and the fact that the Managers method has different case 
	// parameters from the standard, it has to be done the long way.
	p.Add('categories', categories);
	p.Add('pageNo', 1);
	p.Add('pageSize', 0);
	p.Add('filter', filter);
	p.Add('sort', sort);
	if (validCustomListOptions(client, customSelect, customSelectOptions)){
		this.AddCustomListParams(p, client, customSelect, customSelectOptions);
		this.Invoke('Managers_Custom', p, delegateFunc);
	}
	else{
		this.Invoke('Managers', p, delegateFunc);
	}
}

ChartingWebService.prototype.LoadSectors = function(delegateFunc, categories, officialSectors, sort, filter)
{
	var p = new WSParameters();
	var webMethod = officialSectors ? 'OfficialSectors' : 'Sectors';
	if (sort == null) sort = '';
	if (officialSectors != true)
	{
		officialSectors = false;
	}
	p.Add('Categories', categories);
	this.AddStdParams(p, sort, filter);
	this.Invoke(webMethod, p, delegateFunc);
}

ChartingWebService.prototype.LoadIndices = function(delegateFunc, sort, filter)
{
	var p = new WSParameters();
	if (sort == null) sort = '';
	this.AddStdParams(p, sort, filter);
	this.Invoke('Indices', p, delegateFunc);
}

// This method has not been tested.
ChartingWebService.prototype.LoadStochasticFanByAssetCodes = function(delegateFunc, assetCodes, percentiles, timePeriods, allowForInflation)
{
	var p = new WSParameters();
	p.Add('assetCodes', assetCodes);
	p.Add('percentiles', percentiles);
	p.Add('timePeriods', timePeriods);
	p.Add('allowForInflation', allowForInflation);
	this.Invoke('GetStochasticFan_ByAssetCodes', p, delegateFunc);
}

// This method has not been tested.
ChartingWebService.prototype.LoadStochasticFanByTypeCodes = function(delegateFunc, typeCodes, percentiles, timePeriods, allowForInflation)
{
	var p = new WSParameters();
	p.Add('typeCodes', typeCodes);
	p.Add('percentiles', percentiles);
	p.Add('timePeriods', timePeriods);
	p.Add('allowForInflation', allowForInflation);
	this.Invoke('GetStochasticFan_ByTypeCodes', p, delegateFunc);
}

ChartingWebService.prototype.AddStdParams = function(p, sort, filter)
{
	if (filter == null) filter = '';
	if (sort == null) sort = '';
	p.Add('PageNo', 1);
	p.Add('PageSize', 0);
	p.Add('Filter', filter);
	p.Add('Sort', sort);
}

ChartingWebService.prototype.AddCustomListParams = function(p, client, customSelect, customSelectOptions)
{
	p.Add('client', client);
	p.Add('customSelect', customSelect);
	p.Add('customSelectOptions', customSelectOptions);
}


ChartingWebService.prototype.Invoke = function(webMethod, params, delegateFunc)
{
	if (delegateFunc == null) 
	{
		delegateFunc = LoadServiceTest;
		if (webMethod.indexOf('GetRollingDiscrete') == 0)
		{
			delegateFunc = LoadServiceTestString;
		}
	}
	Debug(params.Length == 0, 'Invoke method: params is empty for method "' + webMethod + '"');
	if (DebugAlertOnWebServiceExec)
	{
		var msg = 'Executing ' + webMethod + ' method';
		if (DebugAlertShowParamsOnExec)
		{
			msg += ' with parameters:\n' + params.ToString();
		}
		Debug(true, msg);
	}

	try
	{
		SOAPClient.invoke(this.WebServiceUrl, webMethod, params.ToSoap(), true, delegateFunc);
	}
	catch (ex)
	{
		Debug(true, 'Error invoking web service.\nUrl: ' + this.WebServiceUrl + '\nMethod: ' + webMethod + '\nParameters: ' + params.ToString() + '\nException: ' + ex);
	}
}

//####################################################################
// Parameters collection
//####################################################################
function WSParameters()
{
	this.names = new Array();
	this.values = new Array();
}

WSParameters.prototype.Add = function(name, value)
{
	this.names[this.names.length] = name;
	this.values[this.values.length] = value;
}

WSParameters.prototype.Length = function()
{
	return this.names.length;
}

WSParameters.prototype.ToString = function(separator)
{
	var res = '', sep = '';
	if (separator == null) separator = '\n';
	for (var i = 0; i < this.names.length; i++)
	{
		res += sep + this.names[i] + '="' + this.values[i] + '"';
		sep = separator;
	}
	return res;
}

WSParameters.prototype.ToSoap = function()
{
	var p = new SOAPClientParameters();
	for (var i = 0; i < this.names.length; i++)
	{
		p.add(this.names[i], this.values[i]);
	}
	return p;
}

//####################################################################
// Helper functions below this point 
//####################################################################

//custom client list options validity check
function validCustomListOptions(client, customSelect, customSelectOptions){
	if (client == null || client == '') return false;
	if (customSelect == null || customSelect == '') return false;
	if (customSelectOptions == null || customSelectOptions == '') return false;
	return true;
}

function GetElementById(id, msgOnFail)
{
	if (msgOnFail == null) msgOnFail = true;
	var element = document.getElementById(id);
	Debug(element == null && msgOnFail, "Unable to locate element with id='" + id + "'");
	return element;
}


// This method can be safely run in client scripts. 
// Use instead of alert(message).
var debugQueue = [];
function Debug(condition, message)
{
	var href = document.location.href.toLowerCase();
	var urlDebug = href.indexOf('debugalert=1') > -1 || href.indexOf('debugalert=y') > -1;

	if (!(condition && (DebugAlert || urlDebug))) return;

	var div = null;	
	var isLoaded = false;

	if (message != null)
	{
		debugQueue[debugQueue.length] = message;
	}

	if (UseDebugPanel)
	{
		try
		{
			div = GetDebugPanel(); // this may fail if the document hasn't loaded fully.
			isLoaded = true;
		}
		catch (e) { }

		if (!isLoaded && debugQueue.length < 20)
		{
			setTimeout('Debug(true, null)', 200);
			return;
		}
	}

	for (var i = 0; i < debugQueue.length; i++)
	{	
		if (isLoaded)
		{
			div.Write(debugQueue[i]);
		}
		else
		{
			alert(debugQueue[i]);
		}
	}
	
	debugQueue = [];
}

// This function is used to populate drop-down boxes.
// Parameters:
//   xmlResult ....... the parameter result from the web service.
//   selectControl ... either the id of an element in the document or the element itselft. Must be a <SELECT>.
//   itemPlural ...... a string denoting the type of item, e.g. 'funds', 'indices', etc.
function LoadOptionList(xmlResult, selectControl, itemPlural)
{
	var data = null;
	var sc = selectControl;
	
	if (sc != null && sc.options == null)
	{
		sc = GetElementById(sc);
	}
	
	Debug(sc == null || sc.options == null, 'LoadOptionList parameter selectControl "' + selectControl + '" could not be resolved.');
	
	sc.options.length = 0;

	if (xmlResult == null || xmlResult.Items == null)
	{
		return;
	}
	
	data = xmlResult.Items;

	InfoFirstItem(sc, data.length, itemPlural);

	for (var i = 0; i < data.length; i++)
	{
		sc.options.add(new Option(data[i].Name, data[i].Code), sc.options.length);
	}
}

// This function can be over-ridden with a custom function as required.
function InfoFirstItem(selectControl, itemCount, itemPlural)
{
	var text = null;
	if (itemCount > 1)
	{
		text = itemCount + " " + itemPlural + " found...";
	}
	else if (itemCount < 1)
	{
		text = "0 " + itemPlural + " found!";
	}
	if (text != null)
	{
		selectControl.options.add(new Option(text, ""), 0)
	}
}

//####################################################################
// Test functions below this point
//####################################################################

// Used to test web service result set when result is not a string.
// If the delegate provided by a service is set to null, then either this function or the one below
// will be called. Ensure that DebugAlert is set to true to get a result.
function LoadServiceTest(r)
{
	Debug(r == null, 'LoadServiceTest: parameter "r" was unexpectedly null');
	var data = r.Items;
	if (data == null) data = r.PerfItems;
	Debug(data == null, 'LoadServiceTest: parameter "r" does not contain required property. Must contain either "Items" or "PerfItems".');
	Debug(true, 'LoadServiceTest: data.length is ' + data.length);
}

// Used to test web service result set when result is a string.
function LoadServiceTestString(r)
{
	Debug(r == null, 'LoadServiceTest: parameter "r" was unexpectedly null');
	Debug(true, 'LoadServiceTestString:\n' + r);
}

function TestSuite()
{
	var cws = new ChartingWebService();
	
	Debug(true, 'Tests are asynchronous and may not appear in order.');
	
	Debug(true, 'Testing LoadPerformance...');
	cws.LoadPerformance(null, 'FFISS,FTSENVA');
	
	Debug(true, 'Testing LoadInstruments...');
	cws.LoadInstruments(null, 'FFISS,FTSENVA');
	
	Debug(true, 'Testing LoadUnitsBySectorClassCode...');
	cws.LoadUnitsBySectorClassCode(null, 'OIC', 'U:UG');
	
	Debug(true, 'Testing LoadPerformance...');
	cws.LoadUnitsByManagerCode(null, 'OIC', 'FIDE');
	
	Debug(true, 'Testing LoadSectors...');
	cws.LoadSectors(null, 'OIC', false);

	Debug(true, 'Testing LoadSectors...');
	cws.LoadSectors(null, 'OIC', true);
	
	Debug(true, 'Testing LoadRollingDiscrete...');
	cws.LoadRollingDiscrete(null, 'FFISS', 'NASX', 'Alpha', 36, 0, 1, 12);
	
	Debug(true, 'Testing LoadRollingDiscreteRange...');
	cws.LoadRollingDiscreteRange(null, 'MinMax', 'FFISS', 'NASX', 'Alpha', 36, 0, 1, 12)
	
	Debug(true, 'Testing LoadEquities...');
	cws.LoadEquities(null, "Code LIKE 'EE%' OR Code LIKE 'F%'");
	
	Debug(true, 'Testing LoadIndices...');
	cws.LoadIndices(null, "Source='FTET' OR Source='FTVU' OR Source='MISC'");
}

//####################################################################
// Functions and methods below should not be called directly by client scripts.
// They are called via the Debug function as required.
//####################################################################
var winDebug = null; // global reference.

function GetDebugPanel()
{
	var doc = (winDebug == null) ? document : winDebug.document;
	var div = doc.getElementById('DebugPanel');
	
	if (div == null) 
	{
		div = new DebugPanel();
	}
	else
	{
		div = div.PanelRef;
	}
	
	return div;
}


function CloseDebugWindow()
{
	if (winDebug != null && !winDebug.closed)
	{
		winDebug.close();
	}
}


function DebugPanel()
{
	if (DebugPanelInSeparateWindow && winDebug == null)
	{
		var winName = 'DebugPanel' + Math.round(Math.random() * 10000, 0)
		winDebug = window.open('', winName, 'width=358,height=800');
		winDebug.moveTo(screen.width - 375, 35);
		if (document.body.onunload == null)
		{
			document.body.onunload = 'winDebug.close()';
		}
	}
	
	var isWindow = (winDebug != null);

	var doc = !isWindow ? document : winDebug.document;
	var body = doc.getElementsByTagName('BODY')[0];
	var div = doc.createElement('DIV');
	var hdgStyle = ''
	
	div.id = 'DebugPanel';
	div.className = 'DebugPanel';
	if (!DebugPanelUseCustomStyle)
	{
		div.style.position = 'absolute';
		div.style.width = '350px';
		div.style.left = isWindow ? '4px' : ((doc.body.clientWidth - 370) + 'px');
		div.style.top = isWindow ? '4px' : '20px';
		div.style.fontFamily = 'Arial, Verdana, Sans-Serif';
		div.style.fontSize = '12px';
		div.style.display = 'block';
		div.style.border = 'solid 1px black';
		div.style.backgroundColor = '#E0E0FF';
		div.style.padding = '2px';
		div.style.height = isWindow ? '99%' : (Math.round(doc.body.clientHeight * 2 / 3, 0) + 'px');
		div.style.overflow = 'auto';
		if (!isWindow)
		{
			div.style.filter = 'alpha(opacity=80)';
		}
		hdgStyle = ' style="font-weight:bold; border-bottom: solid 1px black;"'
	}
	div.innerHTML = '<div class="DebugHeading"' + hdgStyle + '>Debug Window</div>';
	
	body.insertBefore(div, null);	
	
	this.window = winDebug;
	this.document = doc;
	this.Div = div;
	div.PanelRef = this;
}


DebugPanel.prototype.Write = function(message)
{
	var div = this.document.createElement('DIV');
	this.Div.insertBefore(div, null);
	div.className = 'DebugEntry';
	
	if (!DebugPanelUseCustomStyle)
	{
		div.style.borderBottom = 'solid 1px black';
	}
	div.innerText = message;
	
	if (this.window != null)
	{
		this.Div.doScroll('down');
	}
}

//####################################################################
