/**
 * eGroupWare eTemplate2 - Class which contains an management tree for the grid rows
 *
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
 * @package etemplate
 * @subpackage dataview
 * @link http://www.egroupware.org
 * @author Andreas Stöckel
 * @copyright Stylite 2011
 * @version $Id$
 */

"use strict"

/*egw:uses
	et2_core_common; // for et2_range functions
	et2_core_inheritance;
	et2_dataview_interfaces;
	et2_dataview_view_partitionNode;
*/

/**
 * The partition container node base class implements most functionality for
 * nodes which have a container.
 */
var et2_dataview_partitionContainerNode = et2_dataview_partitionNode.extend({

	/**
	 * Copies the given container object and inserts the container into the tree
	 * - if it is not already in there.
	 */
	init: function(_root, _container, _args) {
		this._super(_root);

		// Copy the container parameter and set its "invalidationElement" to
		// this node
		this.container = _container;
		this.container.setInvalidationElement(this);
	},

	/**
	 * Inserts the container into the tree if it is not already inserted.
	 */
	initializeContainer: function() {
		// Obtain the node index and insert the container nodes at the node
		// returned by getNodeAt. If idx is zero, the returned node will be
		// the outer container, so the container nodes have to be prepended
		// to it.
		var idx = this.getStartIndex();
		this.container.insertIntoTree(this.getRoot().getNodeAt(idx - 1),
			idx == 0);

		this.invalidate();
	},

	/**
	 * Destroys the container if it is still set - e.g. the spacer node
	 * sets the container to null before "free" ist called in some cases, in
	 * order to pass the container object to another spacer node.
	 */
	destroy: function() {
		if (this.container)
		{
			this.container.free();
		}

		this._super();
	},

	/**
	 * Returns the height of the container
	 */
	calculateHeight: function() {
		return this.container.getHeight();
	},

	/**
	 * Calls the "insertNodeAfter" function of the container to insert the node.
	 */
	insertNodeAfter: function(_node) {
		this.container.insertNodeAfter(_node);
	},

	/**
	 * Returns the "lastNode" of the container
	 */
	getNodeAt: function(_idx) {
		if (_idx >= this.getStartIndex() && _idx < this.getStopIndex())
		{
			return this.container.getLastNode();
		}

		return null;
	}

});

/**
 * Node which represents a spacer. Complete parts of the tree can be
 * transformed into spacer nodes.
 */
var et2_dataview_partitionSpacerNode = et2_dataview_partitionContainerNode.extend({

	init: function(_root, _count, _avgHeight, _container) {

		// Create the container if it has not been passed as a third parameter
		var container;
		if (typeof _container != "undefined")
		{
			container = _container;
		}
		else
		{
			container = new et2_dataview_spacer(_root.getDataProvider(),
				_root.getRowProvider(), this);
		}

		// Call the inherited constructor
		this._super(_root, container);

		// Copy the count and average height parameters - this updates the height
		// of the outer container
		this.setParameters(_count, _avgHeight);
	},

	getCount: function() {
		return this._count;
	},

	getAvgHeight: function() {
		return this._avgHeight;
	},

	setParameters: function(_count, _avgHeight) {
		if (_count != this._count || _avgHeight != this._avgHeight)
		{

			// Copy the given parameters
			this._count = _count;
			this._avgHeight = _avgHeight;

			// Invalidate this element
			this.invalidate();

			// Call the container function to set the total height
			this.container.setHeight(this._count * this._avgHeight);
		}
	},

	/**
	 * Creates the nodes which fall in the given range and returns them
	 */
	getRangeNodes: function(_range, _create) {

		// If no new nodes should be created, simply return this node
		if (!_create)
		{
			return this._super(_range);
		}

		var insertNodes = [];

		// Copy parent and pidx as we'll have to access those objects after this
		// one gets freed
		var parent = this._parent;
		var pidx = this._pidx;

		// Get the top and bottom of this node
		var t = this.getPosTop();
		var b = this.getPosBottom();

		// Get the start and stop index of the elements which have to be
		// created.
		var ah = this._avgHeight;
		var startIdx = Math.max(0, Math.floor((_range.top - t) / ah));
		var stopIdx = Math.min(this._count, Math.ceil((_range.bottom - t) / ah));

		if (startIdx > 0 && startIdx < this._count)
		{
			// Create a spacer which contains the elements until startIdx
			insertNodes.push(new et2_dataview_partitionSpacerNode(this.getRoot(),
				startIdx, ah, this.container));
			this.container = null;
		}

		// Calculate the current average height
		ah = this.getRoot().getAverageHeight();

		// Create the elements from start to stop index
		for (var i = startIdx; i < stopIdx; i++)
		{
			var rowNode = new et2_dataview_partitionRowNode(this.getRoot(), ah);
			insertNodes.push(rowNode);
		}

		if (stopIdx < this._count - 1 && stopIdx > 0)
		{
			// Create a spacer which contains the elements starting from
			// stop index
			var l = this._count - stopIdx;
			insertNodes.push(new et2_dataview_partitionSpacerNode(this.getRoot(),
				l, ah));
		}

		// Check whether insertNodes really has entrys - this is not the case
		// if the given range is just outside the range of this element
		if (insertNodes.length > 0)
		{
			// Free this element
			this.free();

			// Insert the newly created nodes at the original place of this node
			parent.insertNodes(pidx, insertNodes);

			// Insert the newly created elements into the DOM-Tree
			for (var i = 0; i < insertNodes.length; i++)
			{
				insertNodes[i].initializeContainer();
			}

			return false;
		}

		return [];
	},

	getAvgHeightData: function(_data) {
		// Do nothing here, as the spacers should not be inside the average
		// height statistic.
	}

});

var et2_dataview_partitionRowNode = et2_dataview_partitionContainerNode.extend({

	init: function(_root, _avgHeight) {

		var container = new et2_dataview_row(_root.getDataProvider(),
			_root.getRowProvider(), this, _avgHeight);

		this._super(_root, container);
	},

	initializeContainer: function() {
		this._super();

		this.container.setIdx(this.getStartIndex());
	},

	getIdxNode: function(_node, _create) {
		return this.node;
	}

});