var $GridManager = new GridManager();

function GridManager () {
	this.Grids = Grids; // get all from global variable, in future replace all references to it with $GridManager.Grids
	this.AlternativeGrids = new Array();
}

GridManager.prototype.AddAlternativeGrid = function ($source_grid, $destination_grid, $reciprocal)
{
	if ($source_grid == $destination_grid) {
		return false;
	}

	if (typeof(this.AlternativeGrids[$source_grid]) == 'undefined') {
		// alternative grids not found, create empty list
		this.AlternativeGrids[$source_grid] = new Array();
	}

	if (!in_array($destination_grid, this.AlternativeGrids[$source_grid])) {
		// alternative grids found, check if not added already
		this.AlternativeGrids[$source_grid].push($destination_grid);
	}

	if ($reciprocal) {
		this.AddAlternativeGrid($destination_grid, $source_grid);
	}
}

GridManager.prototype.ClearAlternativeGridsSelection = function ($source_prefix)
{
	if (!this.AlternativeGrids[$source_prefix]) return false;

	var $i = 0;
	var $destination_prefix = '';
	while ($i < this.AlternativeGrids[$source_prefix].length) {
		$destination_prefix = this.AlternativeGrids[$source_prefix][$i];
		if (this.Grids[$destination_prefix]) {
			// alternative grid set, but not yet loaded by ajax
			this.Grids[$destination_prefix].ClearSelection();
		}
		$i++;
	}
}

GridManager.prototype.CheckDependencies = function ($prefix) {
	if (typeof(this.Grids[$prefix]) != 'undefined') {
		this.Grids[$prefix].CheckDependencies('GridManager.CheckDependencies');
	}
}

function GridItem(grid, an_element, cb, item_id, class_on, class_off)
{
	this.Grid = grid;
	this.selected = false;
	this.id = an_element.id;
	this.ItemId = item_id;
	this.sequence = parseInt(an_element.getAttribute('sequence'));
//	this.class_on = class_on;

	if (class_off == ':original') {
		this.class_off = an_element.className;
	}
	else
		this.class_off = class_off;

	if (class_on.match(/(.*):(.*)/)) {
		var even = RegExp.$2;
		var odd = RegExp.$1;
		this.class_on = this.class_off.match(/even/) ? even : odd
	}
	else {
		this.class_on = class_on
	}

	this.HTMLelement = an_element;

	if (document.getElementById('left_'+an_element.id)) {
		this.LeftElement = document.getElementById('left_'+an_element.id);
	}
	else {
		this.LeftElement = false;
	}

	this.CheckBox = cb;

	this.value = this.ItemId;
	this.ItemType = 11;
}

GridItem.prototype.Init = function ()
{
	this.HTMLelement.GridItem = this;
	this.HTMLelement.onclick = function(ev) {
			this.GridItem.Click(ev);
	};
	this.HTMLelement.ondblclick = function(ev) {
			this.GridItem.DblClick(ev);
	}
	if ( this.LeftElement ) {
		this.LeftElement.GridItem = this;
		this.LeftElement.onclick = function(ev) {
				this.GridItem.Click(ev);
		};
		this.LeftElement.ondblclick = function(ev) {
				this.GridItem.DblClick(ev);
		}
	}
	if ( isset(this.CheckBox) ) {
		this.CheckBox.GridItem = this;
		this.CheckBox.onclick = function(ev) {
				this.GridItem.cbClick(ev);
		};
		this.CheckBox.ondblclick = function(ev) {
			this.GridItem.DblClick(ev);
		}
	}

//	if ( this.Grid.MouseOverClass ) {
		addEvent(this.HTMLelement, 'mouseover', function(ev) { this.GridItem.MouseOver(ev)}, true); // true for traditional event model
		addEvent(this.HTMLelement, 'mouseout', function(ev) { this.GridItem.MouseOut(ev)}, true);
		if ( this.LeftElement ) {
			addEvent(this.LeftElement, 'mouseover', function(ev) { this.GridItem.MouseOver(ev)}, true);
			addEvent(this.LeftElement, 'mouseout', function(ev) { this.GridItem.MouseOut(ev)}, true);
		}
//	}

}

GridItem.prototype.AddClass = function(elem, class_name)
{
	var cur_classes = elem.className;
//	if (cur_classes.match(class_name)) return;
	elem.className = cur_classes+' '+class_name;
//	console.log('added %s to %s resulted %s', class_name, elem.id, elem.className)
}

GridItem.prototype.RemoveClass = function(elem, class_name)
{
	var cur_classes = elem.className;
//	console.log('looking for %s in %s', class_name, cur_classes)
	if (!cur_classes.match(class_name)) return;
	cur_classes = cur_classes.replace(class_name, '');
	cur_classes = cur_classes.replace(/[ ]+/, ' ');
	elem.className = cur_classes;
//	console.log('removed %s from %s resulted %s', class_name, elem.id, elem.className)
}

GridItem.prototype.DisableClicking = function ()
{
	this.HTMLelement.onclick = function(ev) {
			return false;
	};
	this.HTMLelement.ondblclick = function(ev) {
			return false;
	}
	if ( this.LeftElement ) {
		this.LeftElement.onclick = function(ev) {
			return false;
		};
		this.LeftElement.ondblclick = function(ev) {
				return false;
		}
	}
	if ( isset(this.CheckBox) ) {
		this.CheckBox.onclick = function(ev) {
				return false;
		};
	}
}

GridItem.prototype.Select = function ()
{
	if (this.selected) return;
	this.selected = true;
	this.HTMLelement.setAttribute('_row_selected', 1)
	this.AddClass(this.HTMLelement, this.class_on);
	if ( this.LeftElement ) {
		this.LeftElement.setAttribute('_row_selected', 1)
		this.AddClass(this.LeftElement, this.class_on);
	}
	if ( isset(this.CheckBox) ) {
		this.CheckBox.checked = true;
	}
	this.Grid.LastSelectedId = this.ItemId;
	this.Grid.SelectedCount++;

	// this is for in-portal only (used in relation select)
	LastCheckedItem = this;
	if (typeof (this.Grid.OnSelect) == 'function' ) {
		this.Grid.OnSelect(this.ItemId);
	}
}

GridItem.prototype.UnSelect = function ( force )
{
	if ( !this.selected && !force) return;
	this.selected = false;
	this.HTMLelement.removeAttribute('_row_selected')
	this.RemoveClass(this.HTMLelement, this.class_on);
	if ( this.LeftElement ) {
		this.LeftElement.removeAttribute('_row_selected')
		this.RemoveClass(this.LeftElement, this.class_on);
	}
	if ( isset(this.CheckBox) ) {
		this.CheckBox.checked = false;
	}
	this.Grid.SelectedCount--;
	if (typeof (this.Grid.OnUnSelect) == 'function' ) {
		this.Grid.OnUnSelect(this.ItemId);
	}
	this.Grid.LastSelectedId = null;
}

GridItem.prototype.ClearBrowserSelection = function() {
	ClearBrowserSelection();
}

GridItem.prototype.Click = function (ev)
{
	//this.ClearBrowserSelection();
	this.Grid.ClearAlternativeGridsSelection('GridItem.Click');

	var e = !is.ie ? ev : window.event;

	if (e.shiftKey && !this.Grid.RadioMode) {
		this.ClearBrowserSelection();
		this.Grid.SelectRangeUpTo(this.sequence);
	}
	else {
		if (e.ctrlKey && !this.Grid.RadioMode) {
			this.Toggle()
		}
		else {
			if (!(this.Grid.RadioMode && this.Grid.LastSelectedId == this.ItemId && this.selected)) {
				// don't clear selection if item same as current is selected
				if (!this.Grid.StickySelection) {
					this.Grid.ClearSelection(null,'GridItem.Click');
				}
				else {
					if (this.Grid.LastSelectedId == this.ItemId) {
						return;
					}
				}
				this.Toggle();
			}
		}
	}
	this.Grid.CheckDependencies('GridItem.Click');
	e.cancelBubble = true;
}

GridItem.prototype.cbClick = function (ev)
{
	var e = is.ie ? window.event : ev;
	if (this.Grid.RadioMode) this.Grid.ClearSelection(null,'GridItem.cbClick');
	this.Grid.ClearAlternativeGridsSelection('GridItem.cbClick');
	this.Toggle();
	this.Grid.CheckDependencies('GridItem.cbClick');
	e.cancelBubble = true;
}

GridItem.prototype.DblClick = function (ev)
{
	var e = is.ie ? window.event : ev;
	this.Grid.Edit();
}

GridItem.prototype.MouseOver = function (ev)
{
	this.HTMLelement.setAttribute('_row_highlighted', 1)

	this.AddClass(this.HTMLelement, this.Grid.MouseOverClass);

//	if (this.Grid.MouseOverClass) {this.HTMLelement.className = this.Grid.MouseOverClass;}
	if ( this.LeftElement ) {
		this.LeftElement.setAttribute('_row_highlighted', 1)
//		if (this.Grid.MouseOverClass) this.LeftElement.className = this.Grid.MouseOverClass;
		this.AddClass(this.LeftElement, this.Grid.MouseOverClass);
//		this.LeftElement.className = this.LeftElement.className; // this is to make IE re-render the element
	}
}
GridItem.prototype.MouseOut = function (ev)
{
	this.HTMLelement.removeAttribute('_row_highlighted')
	this.RemoveClass(this.HTMLelement, this.Grid.MouseOverClass);
//	if (this.Grid.MouseOverClass) {this.HTMLelement.className = this.selected ? this.class_on : this.class_off;}
	if ( this.LeftElement ) {
		this.LeftElement.removeAttribute('_row_highlighted')
		this.RemoveClass(this.LeftElement, this.Grid.MouseOverClass);
//		if (this.Grid.MouseOverClass) this.LeftElement.className = this.selected ? this.class_on : this.class_off;
//		this.LeftElement.className = this.LeftElement.className; // this is to make IE re-render the element
	}
}

GridItem.prototype.Toggle = function ()
{
	if (this.selected) this.UnSelect()
	else {
		this.Grid.LastSelectedSequence = this.sequence;
		this.Select();
	}
}

GridItem.prototype.FallsInRange = function (from, to)
{
	return (from <= to) ?
					(this.sequence >= from && this.sequence <= to) :
					(this.sequence >= to && this.sequence <= from);
}


function Grid(prefix, class_on, class_off, dbl_click, toolbar)
{
	this.prefix = prefix;
	this.class_on = class_on;
	this.class_off = class_off;
	this.Items = {};
	this.LastSelectedSequence = 1;
	this.LastSelectedId = null;
	this.DblClick = dbl_click;
	this.ToolBar = toolbar;
	this.SelectedCount = 0;
	this.AlternativeGrids = new Array();
	this.DependantButtons = new Array();
	this.RadioMode = false;
	this.MouseOverClass = false;

	// K3-style sticky selection, selection an item does not unselect currently selected
	// even w/o Ctrl key pressed
	this.StickySelection = false;
}

Grid.prototype.EnableRadioMode = function() {
	this.RadioMode = true;
	this.StickySelection = false;
}

Grid.prototype.AddItem = function( an_item ) {
	this.Items[an_item.id] = an_item;
}

Grid.prototype.AddItemsByIdMask = function ( tag, mask, cb_mask ) {
	var $item_id=0;
	elements = document.getElementsByTagName(tag.toUpperCase());
	for (var i=0; i < elements.length; i++) {
		if ( typeof(elements[i].id) == 'undefined') {
			continue;
		}
		if ( !elements[i].id.match(mask)) continue;
		$item_id=RegExp.$1;

		cb_name = cb_mask.replace('$$ID$$',$item_id);

		cb = document.getElementById(cb_name);
		if (typeof(cb) == 'undefined') alert ('No Checkbox defined for item '+elements[i].id);

		this.AddItem( new GridItem( this, elements[i], cb, $item_id, this.class_on, this.class_off ) );
	}
}

Grid.prototype.InitItems = function() {
	for (var i in this.Items) {
		this.Items[i].Init();
	}
	this.ClearSelection( true,'Grid.InitItems' );

	var a_Grid = this;
	addEvent(document, 'keyup', function(ev) {
		var e = !is.ie ? ev : window.event;
		switch (e.keyCode) {
			case 65:
				if (!e.ctrlKey) break;
				a_Grid.SelectAll();
				ClearBrowserSelection()
//				window.setTimeout(ClearBrowserSelection, 500);
				break;
			case 27:
				a_Grid.ClearSelection();
				break;
			case 33:
			case 37:
				//alert('<-') // go to prev page here
				break;
			case 34:
			case 39:
				// alert('->') // go to next page here
				break;
			case 88:
				ClearBrowserSelection();
				break;
		}
	});
}

Grid.prototype.DisableClicking = function() {
	for (var i in this.Items) {
		this.Items[i].DisableClicking();
	}
	this.ClearSelection( true, 'Grid.DisableClicking' );
}

Grid.prototype.ClearSelection = function( force, called_from ) {
//	alert('selection clear. force: '+force+'; called_from: '+called_from);
	if (typeof(force) == 'undefined') force = false;
	if (this.CountSelected() == 0 && !force) return;
	for (var i in this.Items) {
		this.Items[i].UnSelect(force);
	}
	this.SelectedCount = 0;
	this.CheckDependencies('Grid.ClearSelection');
}

Grid.prototype.GetSelected = function() {
	var $ret = new Array();
	for (var i in this.Items) {
		if (this.Items[i].selected) {
			$ret[$ret.length] = this.Items[i].ItemId;
		}

	}
	return $ret;
}

Grid.prototype.SetSelected = function($ids) {
	$ids = $ids.split(',');
	for (var i in this.Items) {
		if (in_array(this.Items[i].ItemId, $ids)) {
			this.Items[i].Select();
		}
	}
}

Grid.prototype.InvertSelection = function() {
	for (var i in this.Items)
	{
		if( this.Items[i].selected )
		{
			this.Items[i].UnSelect();
		}
		else
		{
			this.Items[i].Select();
		}
	}
	this.CheckDependencies('Grid.InvertSelection');
}

Grid.prototype.SelectAll = function() {
	for (var i in this.Items) {
		this.Items[i].Select();
	}
	this.CheckDependencies('Grid.SelectAll');
	this.ClearAlternativeGridsSelection('Grid.SelectAll');
}

Grid.prototype.SelectRangeUpTo = function( last_sequence ) {
	for (var i in this.Items) {
		if (this.Items[i].FallsInRange(this.LastSelectedSequence, last_sequence)) {
			this.Items[i].Select();
		}
	}
}

Grid.prototype.CountSelected = function ()
{
	return this.SelectedCount;
}

Grid.prototype.Edit = function() {
	if ( this.CountSelected() == 0 ) return;
	this.DblClick(this.prefix);
}

Grid.prototype.SetDependantToolbarButtons = function($buttons, $direct, $mode) {
	if (!isset($direct)) $direct = true; // direct (use false for invert mode)
	if (!isset($mode)) $mode = 1; // enable/disable (use 2 for show/hide mode)
	for (var i = 0; i < $buttons.length; i++) {
		this.DependantButtons.push(new Array($buttons[i], $direct, $mode));
	}
	//this.DependantButtons = buttons;
	this.CheckDependencies('Grid.SetDependantToolbarButtons');
}

Grid.prototype.CheckDependencies = function($called_from, $run_now) {
	if (!$run_now || $run_now === undefined) {
		// schedule run via document.ready, so we manage to disable
		// toolbar buttons after ToolBar class mass-enables them
		var $me = this;

		$(document).ready(
			function () {
				$me.CheckDependencies($called_from, true);
			}
		);

		return ;
	}

	var enabling = (this.CountSelected() > 0);

	for (var i = 0; i < this.DependantButtons.length; i++) {
		if (this.DependantButtons[i][2] == 1) {
			this.ToolBar.SetEnabled(this.DependantButtons[i][0], enabling == this.DependantButtons[i][1]);
		}
		else {
			this.ToolBar.SetVisible(this.DependantButtons[i][0], enabling == this.DependantButtons[i][1]);
		}
	}
}

Grid.prototype.ClearAlternativeGridsSelection = function (called_from)
{
	$GridManager.ClearAlternativeGridsSelection(this.prefix);
}

Grid.prototype.AddAlternativeGrid = function (alt_grid, reciprocal)
{
	var $dst_prefix = typeof('alt_grid') == 'string' ? alt_grid : alt_grid.prefix;
	$GridManager.AddAlternativeGrid(this.prefix, $dst_prefix, reciprocal);
}

Grid.prototype.FirstSelected = function ()
{
	min_sequence = null;
	var res = null
	for (var i in this.Items) {
		if (!this.Items[i].selected) continue;
		if (min_sequence == null)
			min_sequence = this.Items[i].sequence;
		if (this.Items[i].sequence <= min_sequence) {
			res = this.Items[i].ItemId;
			min_sequence = this.Items[i].sequence;
		}
	}
	return res;
}

Grid.prototype.each = function($callback) {
	var $result = null;

	for (var i in this.Items) {
		$result = $callback.call(this.Items[i], i);
		if ($result === false) {
			break;
		}
	}
}