"use strict";

function ValueToDOMRenderers()
{
	this.arrRendererObjects=[];
}


ValueToDOMRenderers.prototype={
	/**
	 * @type {Array}
	 */
	arrRendererObjects: null,


	/**
	 * @param {RendererBase} renderer
	 * @param {Boolean} bReversePriority
	 *
	 * @return {undefined}
	 */
	addRenderer: function(renderer, bReversePriority)
	{
		bReversePriority=!!bReversePriority;

		for(var key in this.arrRendererObjects)
		{
			if(this.arrRendererObjects[key].constructor == renderer.constructor)
			{
				return;
			}
		}

		if(bReversePriority)
		{
			this.arrRendererObjects.push(renderer);
		}
		else
		{
			this.arrRendererObjects.unshift(renderer);
		}
	},


	/**
	 * @param {RendererBase} renderer
	 */
	removeRenderer: function(renderer)
	{
		for(var key in this.arrRendererObjects)
		{
			if(this.arrRendererObjects[key] instanceof renderer)
			{
				this.arrRendererObjects.splice(key, 1);
				return;
			}
		}

		throw new Error("Failed to remove renderer object, maybe renderer is not registered.");
	},


	/**
	 * Returns CSS class names as a string which can be used as a value for the .className property of an HTMLElement.
	 *
	 * @param {String} strFieldName
	 * @param {String} strTag
	 * @param {*} mxValue
	 * @returns {string}
	 */
	fieldToClassName: function(strFieldName, strTag, mxValue)
	{
		var arrClassNames=[];

		for(var key in this.arrRendererObjects)
		{
			arrClassNames = arrClassNames.concat(this.arrRendererObjects[key].fieldToCSSClassNames(strFieldName, strTag, mxValue));
		}

		// Remove duplicates.
		return arrClassNames.filter(
			function(value, position, self){
				return self.indexOf(value) == position;
			}
		).join(" ");
	},


	/**
	 * @param {String} strFieldName,
	 * @param {*} mxValue,
	 * @param {Object} objRow
	 * @param {Object} objOptions
	 * @param {*} controller
	 */
	runRenderers: function(strFieldName, mxValue, objRow, objOptions, controller)
	{
		var nMultiRowIndex = objOptions["nMultiRowIndex"];

		if(mxValue instanceof Array)
		{
			mxValue=(mxValue.hasOwnProperty(nMultiRowIndex)) ? mxValue[nMultiRowIndex] : null;
		}

		for(var mxKey in objRow)
		{
			if(objRow[mxKey] instanceof Array)
			{
				objRow[mxKey] = (objRow[mxKey].hasOwnProperty(nMultiRowIndex)) ? objRow[mxKey][nMultiRowIndex] : null;
			}
		}

		var strLabel = null;
		for(var key=this.arrRendererObjects.length-1; key>=0; key--)
		{
			var mxRendered = this.arrRendererObjects[key].render(strFieldName, mxValue, objRow, objOptions, controller);
			
			if(strLabel === null)
			{
				strLabel = this.arrRendererObjects[key].renderLabel(strFieldName);
			}

			if (mxRendered !== null)
			{
				return {
					value: mxRendered,
					label: strLabel // strLabel could still be null here
				};
			}
		}

		// This shouldn't be reached. Normally there have to be renderers added.
		return {
			value: mxRendered,
			label: strLabel // strLabel could still be null here
		};
	},


	/**
	 * @param {Array} arrFields
	 * @param {Object} objMergedColumns
	 */
	appendColumnDependencies: function(arrFields, objMergedColumns)
	{
		objMergedColumns = objMergedColumns === undefined ? [] : objMergedColumns;

		arrFields = (typeof arrFields === "object") ? arrFields : new Array(arrFields);
		var arrFieldsNew = arrFields.slice();

		ValueToDOMRenderers.arrayWalkRecursive(
			objMergedColumns,
			function(mxValue)
			{
				if (typeof mxValue === "string" && !arrFieldsNew.hasOwnProperty(mxValue))
				{
					arrFieldsNew.push(mxValue);
				}
			}
		);

		for(var key in this.arrRendererObjects)
		{
			this.arrRendererObjects[key].appendColumnDependencies(arrFieldsNew);
		}

		return arrFieldsNew;
	},


	/**
	 * @param {Object} objContext
	 * @param {Array} arrAllColumns
	 * @param {Array} arrExcludePropertyNames
	 * @returns {Array}
	 */
	extraUnrenderedProperties: function(objContext, arrAllColumns, arrExcludePropertyNames){
		var arrExtraPropertyNames=Object.keys(objContext);

		ValueToDOMRenderers.arrayWalkRecursive(
			arrAllColumns,
			function(mxValue)
			{
				if (typeof mxValue === "string")
				{
					if(arrExtraPropertyNames.indexOf(mxValue)>-1)
					{
						arrExtraPropertyNames.splice(arrExtraPropertyNames.indexOf(mxValue), 1);
					}
				}
			}
		);

		for(var nIndex in arrExcludePropertyNames)
		{
			if(arrExtraPropertyNames.indexOf(arrExcludePropertyNames[nIndex])>-1)
			{
				arrExtraPropertyNames.splice(arrExtraPropertyNames.indexOf(arrExcludePropertyNames[nIndex]), 1);
			}
		}

		return arrExtraPropertyNames;
	}
};


/**
 * @static
 * @type {Object}
 */
ValueToDOMRenderers.modRewriteLookupTable = {};


ValueToDOMRenderers.fieldRenderFloat = function(fNumber, nDecimals, bRemoveTrailingZeroes)
{
	bRemoveTrailingZeroes = (typeof bRemoveTrailingZeroes === "undefined") ? false : bRemoveTrailingZeroes;
	nDecimals = (typeof nDecimals === "undefined") ? 3 : nDecimals;
	fNumber = (typeof fNumber === "number") ? fNumber : Number(fNumber);

	if(!nDecimals)
		return Math.floor(fNumber);

	if(bRemoveTrailingZeroes)
	{
		return Number(fNumber.toFixed(nDecimals)); // this will remove trailing zeros
	}
	else
	{
		return fNumber.toFixed(nDecimals);
	}
};

ValueToDOMRenderers.fieldRenderFloatSafeRounding = function(fNumber, nDecimals, bRemoveTrailingZeroes)
{
	var nDecimalsOriginal=nDecimals;

	--nDecimals;
	var strResult;
	do
	{
		strResult=ValueToDOMRenderers.fieldRenderFloat(fNumber, ++nDecimals, bRemoveTrailingZeroes);
	}
	while(strResult===ValueToDOMRenderers.fieldRenderFloat(0, nDecimals, bRemoveTrailingZeroes) && nDecimals < 8);

	if(strResult===ValueToDOMRenderers.fieldRenderFloat(0, nDecimals, bRemoveTrailingZeroes))
	{
		strResult=ValueToDOMRenderers.fieldRenderFloat(fNumber, nDecimalsOriginal, bRemoveTrailingZeroes);
	}

	return strResult;
};


/**
 * @static
 *
 * @param {Array} array
 * @param {Function} fnCallback
 */
ValueToDOMRenderers.arrayWalkRecursive = function(array, fnCallback)
{
	if (typeof(array) == "object")
	{
		for(var i in array)
		{
			ValueToDOMRenderers.arrayWalkRecursive(array[i], fnCallback);
		}
	}
	else
	{
		fnCallback(array);
	}
};


/**
 * @static
 *
 * @param {String} strPath
 *
 * @return {String}
 */
ValueToDOMRenderers.modRewriteURL = function(strPath)
{
	if(ValueToDOMRenderers.modRewriteLookupTable["url"][strPath]===undefined)
	{
		throw new Error("Unable to resolve strPath "+JSON.stringify(strPath)+" to an URL.");
	}
	var strShortLanguageName = ValueToDOMRenderers.strLanguage;

	return strShortLanguageName + "/" + ValueToDOMRenderers.modRewriteLookupTable["url"][strPath];
};


/**
 * Default ISO-639-1 two letter language code.
 *
 * @static
 *
 * @type {String}
 */
ValueToDOMRenderers.strLanguage="en";


/**
 * @type {ValueToDOMRenderers}
 */
ValueToDOMRenderers.singleton=new ValueToDOMRenderers();