/**
 * eGroupWare egw_action framework - egw action framework
 *
 * @link http://www.egroupware.org
 * @author Andreas Stöckel <as@stylite.de>
 * @copyright 2011 by Andreas Stöckel
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
 * @package egw_action
 * @version $Id$
 */

/*egw:uses
	egw_action_common;
*/

/**
 * Contains logic for the column class. The column class represents the unique set
 * of columns a grid view owns. The parameters of the columns (except for visibility)
 * di normaly not change.
 */

var EGW_COL_TYPE_DEFAULT = 0;
var EGW_COL_TYPE_NAME_ICON_FIXED = 1;
var EGW_COL_TYPE_CHECKBOX = 2;

var EGW_COL_VISIBILITY_ALWAYS = 0;
var EGW_COL_VISIBILITY_VISIBLE = 1;
var EGW_COL_VISIBILITY_INVISIBLE = 2;
var EGW_COL_VISIBILITY_ALWAYS_NOSELECT = 3;

var EGW_COL_SORTABLE_NONE = 0;
var EGW_COL_SORTABLE_ALPHABETIC = 1;
var EGW_COL_SORTABLE_NUMERICAL = 2;
var EGW_COL_SORTABLE_NATURAL = 3;
var EGW_COL_SORTABLE_EXTERNAL = 4;


var EGW_COL_SORTMODE_NONE = 0;
var EGW_COL_SORTMODE_ASC = 1;
var EGW_COL_SORTMODE_DESC = 2;

var EGW_COL_DEFAULT_FETCH = -10000;


/**
 * Class representing a single grid column.
 */
function egwGridColumn(_context, _visiblityChangeCallback, _sortmodeChangeCallback)
{
	if (typeof _context == "undefined")
	{
		_context = null;
	}

	if (typeof _visiblityChangeCallback == "undefined")
	{
		_visiblityChangeCallback == null;
	}

	this.id = "";
	this.fixedWidth = false;
	this.relativeWidth = false;
	this.maxWidth = false;
	this.caption = "";
	this.type = EGW_COL_TYPE_DEFAULT;
	this.visibility = EGW_COL_VISIBILITY_VISIBLE;
	this.sortable = EGW_COL_SORTABLE_NONE;
	this.sortmode = EGW_COL_SORTMODE_NONE;
	this["default"] = EGW_COL_DEFAULT_FETCH;

	this.context = _context;
	this.visibilityChangeCallback = _visiblityChangeCallback;
	this.sortmodeChangeCallback = _sortmodeChangeCallback;
}

egwGridColumn.prototype.loadData = function(_data)
{
	egwActionStoreJSON(_data, this, true);
}

egwGridColumn.prototype.set_width = function(_value)
{
	// Parse the width parameter. Posible values are:
	// 	1. "100" => fixedWidth 100px
	// 	2. "100px" => fixedWidth 100px
	// 	3. "50%" => relativeWidth 50%
	if (_value)
	{
		if (typeof _value == "string")
		{
			var w = _value;
			if (w.charAt(w.length - 1) == "%" && !isNaN(w.substr(0, w.length - 1)))
			{
				this.relativeWidth = parseInt(w.substr(0, w.length - 1)) / 100;

				// Relative widths with more than 100% are not allowed!
				if (this.relativeWidth > 1)
				{
					this.relativeWidth = false;
				}
			}
			else if (w.substr(w.length - 2, 2) == "px" && !isNaN(w.substr(0, w.length - 2)))
			{
				this.fixedWidth = parseInt(w.substr(0, w.length - 2));
			}
			else if (!isNaN(w))
			{
				this.fixedWidth = parseInt(w);
			}
		}
	}
}

egwGridColumn.prototype.set_maxWidth = function(_value)
{
	if (!isNaN(_value) && _value > 0)
	{
		this.maxWidth = _value;
	}
}

egwGridColumn.prototype.set_default = function(_value)
{
	if (typeof _value == "string")
	{
		this["default"] = _value;
	}
	else if (typeof _value == "number" && (_value == EGW_COL_DEFAULT_FETCH))
	{
		this["default"] = _value;
	}
}

egwGridColumn.prototype.set_id = function(_value)
{
	this.id = _value;
}

/**
 * Setter for the column type.
 */
egwGridColumn.prototype.set_type = function(_value)
{
	if (typeof _value == "number" && (_value == EGW_COL_TYPE_DEFAULT ||
	    _value == EGW_COL_TYPE_NAME_ICON_FIXED || _value == EGW_COL_TYPE_CHECKBOX))
	{
		this.type = _value;

		if (this.type == EGW_COL_TYPE_CHECKBOX)
		{
			this.set_width("23px");
		}
	}
}

/**
 * Setter for the visibility of the column. Checks whether the given value is in
 * the allowed range and calls the visibilityChangeCallback.
 */ 
egwGridColumn.prototype.set_visibility = function(_value)
{
	if (typeof _value == "number" && (_value == EGW_COL_VISIBILITY_ALWAYS ||
	    _value == EGW_COL_VISIBILITY_INVISIBLE || _value == EGW_COL_VISIBILITY_VISIBLE ||
	    _value == EGW_COL_VISIBILITY_ALWAYS_NOSELECT))
	{
		if (_value != this.visibility)
		{
			this.visibility = _value;

			if (this.visibilityChangeCallback)
			{
				this.visibilityChangeCallback.call(this.context, this);
			}
		}
	}
}

/**
 * Sets the sortmode of the column and informs the parent about it.
 */
egwGridColumn.prototype.set_sortmode = function(_value)
{
	if (typeof _value == "number" && (_value == EGW_COL_SORTMODE_NONE ||
	    _value == EGW_COL_SORTMODE_ASC || _value == EGW_COL_SORTMODE_DESC))
	{
		if (this.sortable == EGW_COL_SORTABLE_NONE)
		{
			this.sortmode = EGW_COL_SORTMODE_NONE;
		}
		else
		{
			if (_value != this.sortmode)
			{
				if (this.sortmodeChangeCallback)
				{
					this.sortmodeChangeCallback.call(this.context, this);
				}
				this.sortmode = _value;
			}
		}
	}
}

egwGridColumn.prototype.set_sortable = function(_value)
{
	if (typeof _value == "number" && (_value == EGW_COL_SORTABLE_ALPHABETIC ||
	    _value == EGW_COL_SORTABLE_NONE || _value == EGW_COL_SORTABLE_NATURAL ||
	    _value == EGW_COL_SORTABLE_NUMERICAL || _value == EGW_COL_SORTABLE_EXTERNAL))
	{
		if (_value == EGW_COL_SORTABLE_NONE)
		{
			this.sortmode = EGW_COL_SORTABLE_NONE;
		}

		this.sortable = _value;
	}
}


egwGridColumn.prototype.set_caption = function(_value)
{
	this.caption = _value;
}


/**
 * Object which is used inside egwGrid to manage the grid columns.
 */
function egwGridColumns(_columns, _updateCallback, _context, _columnSpace)
{
	// Default the coulumn padding value to two
	if (typeof _columnSpace == "undefined")
	{
		this.columnSpace = 2;
	}
	else
	{
		this.columnSpace = _columnSpace;
	}

	this.totalWidth = false;
	this.inUpdate = false;
	this.sortChanged = null;
	this.visibilityChanged = false;
	this.columnWidths = [];

	this.context = _context;
	this.updateCallback = _updateCallback;

	this._beginUpdate();

	this.columns = [];
	for (var i = 0; i < _columns.length; i++)
	{
		var column = new egwGridColumn(this, this._visibilityCallback, this._sortCallback);
		column.loadData(_columns[i]);

		this.columns.push(column);
	}

	this._endUpdate();
}

egwGridColumns.prototype._beginUpdate = function()
{
	this.inUpdate = true;
	this.sortChanged = null;
	this.visibilityChanged = false;
}

egwGridColumns.prototype._endUpdate = function()
{
	this.inUpdate = false;

	// Call the sort update again in order to update the other columns
	if (this.sortChanged)
	{
		this._sortCallback(this.sortCallback);
	}

	if (this.visibilityChanged || this.sortChanged)
	{
		this.updateCallback.call(this.context, this);
	}

	this.sortChanged = null;
	this.visibilityChanged = false;
}

egwGridColumns.prototype._visibilityCallback = function(_elem)
{
	if (this.inUpdate)
	{
		this.visibilityChanged = true;
	}
	else
	{
		this.updateCallback.call(this.context, this);
	}
}

egwGridColumns.prototype._sortCallback = function(_elem)
{
	if (this.inUpdate)
	{
		this.sortChanged = _elem;
	}
	else
	{
		// Reset the sortmode of all other elements.
		for (var i = 0; i < this.columns.length; i++)
		{
			if (this.columns[i] != _elem)
			{
				this.columns[i].sortmode = EGW_COL_SORTMODE_NONE;
			}
		}
	}
}

egwGridColumns.prototype._calculateWidths = function()
{
	// Reset some values which are used during the calculation
	for (var i = 0; i < this.columns.length; i++)
	{
		this.columns[i].larger = false;
		this.columns[i].newWidth = false;
	}

	// Remove the spacing between the columns from the total width
	var tw = this.totalWidth;// - (Math.max(this.getVisibleCount() - 1, 0)) * this.columnSpace;

	// Calculate how many space is - relatively - not occupied with columns with
	// relative or fixed width
	var remRelWidth = 1;
	var noWidthCount = 0

	for (var i = 0; i < this.columns.length; i++)
	{
		var col = this.columns[i];
		if (col.visibility != EGW_COL_VISIBILITY_INVISIBLE)
		{
			if (col.relativeWidth)
			{
				remRelWidth -= col.relativeWidth;
			}
			else if (col.fixedWidth)
			{
				remRelWidth -= col.fixedWidth / tw;
			}
			else
			{
				noWidthCount++;
			}
		}
	}

	// Check whether the width of columns with relative width is larger than their
	// maxWidth
	var done = true;
	do
	{
		done = true;

		var noWidth = remRelWidth / noWidthCount;

		for (var i = 0; i < this.columns.length; i++)
		{
			var col = this.columns[i];

			if (col.visibility != EGW_COL_VISIBILITY_INVISIBLE)
			{
				if (col.maxWidth && !col.larger)
				{
					if (col.relativeWidth)
					{
						var w = col.relativeWidth * tw;
						col.larger = w > col.maxWidth;
						if (col.larger)
						{
							// Recalculate the remaining relative width:
							// col.maxWidth / w is the relative amount of space p which
							// is remaining for the element. E.g. an element with
							// w = 150px and maxWidth = 100px => p = 2/3
							// The space which got removed is 1 - p => 1/3
							// ==> we have to add 1/3 * oldRelWidth to the remRelWidth
							// variable.
							remRelWidth += col.relativeWidth * (1 - col.maxWidth / w);
							done = false;
							break;
						}
					}
					else
					{
						col.larger = noWidth * tw > col.maxWidth;
						if (col.larger)
						{
							remRelWidth -= col.maxWidth / tw;
							noWidthCount--;
							done = false;
							break;
						}
					}
				}
			}
		}
	// As some columns take their max width, new space might come available, which
	// requires other columns to take their maximum width.
	} while (!done);


	// Check whether the columns where a relative width is specified have more
	// space than the remaining columns - if yes, make the relative ones larger
	for (var i = 0; i < this.columns.length; i++)
	{
		var col = this.columns[i];

		if (col.visibility != EGW_COL_VISIBILITY_INVISIBLE)
		{
			if (col.relativeWidth && !col.larger)
			{
				if (col.relativeWidth < noWidth)
				{
					noWidthCount++;
					remRelWidth += col.relativeWidth;
					col.newWidth = true;
				}
				else
				{
					col.newWidth = false;
				}
			}
		}
	}

	// Now calculate the absolute width of the columns in pixels
	this.columnWidths = [];
	for (var i = 0; i < this.columns.length; i++)
	{
		var w = 0;
		var col = this.columns[i];
		if (col.visibility != EGW_COL_VISIBILITY_INVISIBLE)
		{
			if (col.larger)
			{
				w = col.maxWidth;
			}
			else if (col.fixedWidth)
			{
				w = col.fixedWidth;
			}
			else if (col.relativeWidth && !col.newWidth)
			{
				w = Math.round(tw * col.relativeWidth);
			}
			else
			{
				w = Math.round(tw * (remRelWidth / noWidthCount));
			}

			if (w < 0)
			{
				w = 0;
			}
		}
		this.columnWidths.push(w);
	}
}

egwGridColumns.prototype.setTotalWidth = function(_value)
{
	if (_value < 100)
	{
		_value = 100;
	}

	this.totalWidth = _value;
	this._calculateWidths();
}

egwGridColumns.prototype.getColumnIndexById = function(_id)
{
	for (var i = 0; i < this.columns.length; i++)
	{
		if (this.columns[i].id == _id)
		{
			return i;
		}
	}
	return -1;
}

egwGridColumns.prototype.getColumnById = function(_id)
{
	var idx = this.getColumnIndexById(_id);
	return (idx == -1) ? null : this.columns[idx];
}

egwGridColumns.prototype.getVisibleCount = function()
{
	var cnt = 0;
	for (var i = 0; i < this.columns.length; i++)
	{
		if (this.columns[i].visibility != EGW_COL_VISIBILITY_INVISIBLE)
		{
			cnt++;
		}
	}
	return cnt;
}

egwGridColumns.prototype.getColumnVisibilitySet = function()
{
	var result = {};

	for (var i = 0; i < this.columns.length; i++)
	{
		if (this.columns[i].visibility != EGW_COL_VISIBILITY_ALWAYS_NOSELECT)
		{
			result[this.columns[i].id] = {
				"caption": this.columns[i].caption,
				"enabled": (this.columns[i].visibility != EGW_COL_VISIBILITY_ALWAYS) &&
					(this.columns[i].type != EGW_COL_TYPE_NAME_ICON_FIXED),
				"visible": this.columns[i].visibility != EGW_COL_VISIBILITY_INVISIBLE
			};
		}
	}

	return result;
}

egwGridColumns.prototype.setColumnVisibilitySet = function(_set)
{
	this._beginUpdate();

	for (var k in _set)
	{
		var col = this.getColumnById(k);
		if (col)
		{
			col.set_visibility(_set[k].visible ? EGW_COL_VISIBILITY_VISIBLE :
				EGW_COL_VISIBILITY_INVISIBLE);
		}
	}

	if (this.visibilityChanged)
	{
		this._calculateWidths();
	}

	this._endUpdate();
}

egwGridColumns.prototype.getColumnData = function()
{
	var result = [];

	for (var i = 0; i < this.columns.length; i++)
	{
		result.push(
			{
				"id": this.columns[i].id,
				"caption": this.columns[i].caption,
				"sortable": this.columns[i].sortable,
				"sortmode": this.columns[i].sortmode,
				"default": this.columns[i]["default"],
				"width": this.columnWidths[i],
				"type": this.columns[i].type,
				"visible": this.columns[i].visibility != EGW_COL_VISIBILITY_INVISIBLE,
				"element": this.columns[i]
			}
		);
	}

	return result;
}

egwGridColumns.prototype.sortBy = function(_id, _mode)
{
	// Fetch the column and set its sortmode. If the column supports sorting,
	// it will call the callback function.
	var col = this.getColumnById(_id);
	if (col)
	{
		col.set_sortmode(_mode);
	}
}