/// Constructor : Reading received options, checking them,
/// creating background for Slider(s), Slider(s), arrows
/// (spins) (if they are user needed), adds events that work 
/// with slider elements
Zapatec.Slider = function(config)
{
	if (typeof config.div == "string") {	
		config.div = document.getElementById (config.div);
	}

	/// assigning configuration for further usage
	this.dragging = false;
	this.orientation = config.orientation;
	this.onChange = config.onChange || function() {};
	this.newPosition = config.newPosition || function() {};
	this.interval = config.onChangeTime || 0;
	this.length = config.length || 0;
	this.dual = config.dual || false;
	this.range = config.range || [0, this.length];
	this.rangeStep = (this.range[1] - this.range[0]) / this.length;
	this.minDist = 0;
	
	/// checking step on "correctness"
	if (!this.dual && typeof(config.step) == "number") {
		if (config.step >= (this.range[1] - this.range[0])) {
			this.step = this.range[1] - this.range[0] - this.rangeStep;
		} else {
			this.step = config.step;
		}
	} else {
		this.step = 0;
	}

	/// Creating table that would contain
	/// a Slider and possibly a couple of arrows (spins)
	this.table = Zapatec.Utils.createElement("table", config.div);
	this.table.className = "zpSlider";
	this.table.tbody = Zapatec.Utils.createElement("tbody", this.table);
	this.table.border = 0;
	this.table.cellPadding = 0;
	this.table.cellSpacing = 0;
	
	/// Creating cells
	this.table.tbody.spinL = Zapatec.Utils.createElement("tr", this.table.tbody);
	this.table.tbody.spinL = Zapatec.Utils.createElement("td", this.table.tbody.spinL);
	
	this.table.tbody.Bg = Zapatec.Utils.createElement("tr", this.table.tbody);
	this.table.tbody.Bg = Zapatec.Utils.createElement("td", this.table.tbody.Bg);

	this.table.tbody.spinR = Zapatec.Utils.createElement("tr", this.table.tbody);
	this.table.tbody.spinR = Zapatec.Utils.createElement("td", this.table.tbody.spinR);

	/// Creating background for Slider
	var div = Zapatec.Utils.createElement("div", this.table.tbody.Bg);
	this.bgDiv = div;
	this.bgDiv.buttonType = "bg";
	this.bgDiv.style.position = "relative";

	/// Creating a Slider (#1), it's obligatory to have at least one
	div = Zapatec.Utils.createElement("div", this.bgDiv);
	this.slider = div;
	this.slider.buttonType = "first_slider";
	this.slider.style.position = "absolute";

	/// Creating an arrows (spins)
	/// According to the orientation of an arrow, we apply requisite CSS class
	if (this.step > 0) {
		this.rInc = Zapatec.Utils.createElement("div", this.table.tbody.spinR);
		this.rInc.buttonType = "more";
		this.rInc.className = config.downclass; ///<down direction
		this.lInc = Zapatec.Utils.createElement("div", this.table.tbody.spinL);
		this.lInc.buttonType = "less";
		this.lInc.className = config.upclass; ///<up direction
	}

	/// According to the orientation  we apply to our Slider(s)
	/// requisite CSS class, make it(them) "draggable"
	/// and set the style of the background of a Slider

	this.bgDiv.className = config.bgclass;
	this.slider.className = config.buttonclass;
	this.bgDiv.style.height =
	((config.length + this.slider.offsetHeight) + 'px') || '0px';
	var limit = this.getLimit();
	new Zapatec.Utils.Draggable(this.slider, {
	top: limit.min,
	bottom: (limit.min + (Math.round((config.secondStart - this.range[0]) / this.rangeStep) - this.minDist || this.length)),
	vertical: true,
	dragCSS: config.buttonclass
	});

	// set Slider(s) on their positions
	this.setPos(config.start || this.range[0], config.secondStart || this.range[1]);

	/// Customize mouse event handlers
	this.addEvents();
}

/**
 * \internal Returns top offset relative to parent absolute positioned element.
 *
 * \return [number] top offset.
 */
Zapatec.Slider.prototype.getOffsetTop = function () {
  if (typeof this.offsetTop == 'undefined') {
	this.slider.style.top = '0px';
	this.offsetTop = Zapatec.Utils.getAbsolutePos(this.bgDiv).y -
     Zapatec.Utils.getAbsolutePos(this.slider).y;
  }
  return this.offsetTop;
};

/**
 * \internal Returns left offset relative to parent absolute positioned element.
 *
 * \return [number] left offset.
 */
Zapatec.Slider.prototype.getOffsetLeft = function () {
  if (typeof this.offsetLeft == 'undefined') {
    this.slider.style.left = '0px';
    this.offsetLeft = Zapatec.Utils.getAbsolutePos(this.bgDiv).x -
     Zapatec.Utils.getAbsolutePos(this.slider).x;
  }
  return this.offsetLeft;
};

/// Internal setsup the events forSlider
///
Zapatec.Slider.prototype.addEvents = function () {
	var self = this, target = null;
	Zapatec.Utils.addEvent(this.table, "mousedown", function (ev) {
		ev = ev || window.event; 
		target = Zapatec.Utils.getTargetElement(ev);
		while(!target.buttonType && (target != self.table) && (target != document.body)) {
			target = target.parentNode;
		}
		if (!target.buttonType) target = null;
		Zapatec.Slider.mouseDown(ev, self, target);
		if (target) return Zapatec.Utils.stopEvent(ev);
	});
	Zapatec.Utils.addEvent(window.document, "mousemove", function (ev) {
		ev = ev || window.event; 
		Zapatec.Slider.mouseMove(ev, self, target);
		if (target) return Zapatec.Utils.stopEvent(ev);
	});
	Zapatec.Utils.addEvent(window.document, "mouseup", function (ev) {
		ev = ev || window.event; 
		Zapatec.Slider.mouseUp(ev, self, target);
		target = null;
		if (target) return Zapatec.Utils.stopEvent(ev);
	});
}

Zapatec.Slider.mouseDown = function (ev, self, target) {
	if (target) {
		switch (target.buttonType) {
			case "first_slider" : {
				if (self.dual === true) {
					var zIndex1 = parseInt(self.slider.style.Zindex, 10) || 100;
					var zIndex2 = parseInt(self.slider1.style.Zindex, 10) || 100;
					if (zIndex1 == zIndex2) {
						++zIndex1;
					} else if (zIndex1 < zIndex2) {
						zIndex1 = zIndex2 - zIndex1;
						zIndex2 -= zIndex1;
						zIndex1 += zIndex2;
					}
					self.slider.style.zIndex = zIndex1;
					self.slider1.style.zIndex = zIndex2;
				}
				
				break;
			}

			case "second_slider" : {
				if (self.dual === true) {
					var zIndex1 = parseInt(self.slider.style.Zindex, 10) || 100;
					var zIndex2 = parseInt(self.slider1.style.Zindex, 10) || 100;
					if (zIndex1 == zIndex2) {
						++zIndex2;
					} else if (zIndex2 < zIndex1) {
						zIndex1 = zIndex2 - zIndex1;
						zIndex2 -= zIndex1;
						zIndex1 += zIndex2;
					}
					self.slider.style.zIndex = zIndex1;
					self.slider1.style.zIndex = zIndex2;
				}
				
				break;
			}
			
			case "bg" : {
				if (self.dual != true) {
					var shift = 0;
					/// checking and calculating new position
					if (self.orientation == "V") {
						shift = ev.clientY - Zapatec.Utils.getAbsolutePos(self.bgDiv).y - Math.floor(self.slider.offsetHeight / 2);
					} else {
						shift = ev.clientX - Zapatec.Utils.getAbsolutePos(self.bgDiv).x - Math.floor(self.slider.offsetWidth / 2);
					}
					shift = (shift > self.length ? self.length : shift);
					shift = (shift < 0 ? 0 : shift);
					shift = self.range[0] + self.rangeStep * shift;
					/// setting Slider on a new position and retrieving result to user function
					self.setPos(shift);
					self.onChange(shift);
					self.newPosition(shift);
				}
				
				break;
			}

		}
	}
}

Zapatec.Slider.mouseMove = function (ev, self, target) {
	if (target) {
		switch (target.buttonType) {
			case "first_slider" : {
				var pos = self.getPos();
				/// controls onChange call's frequency.
				if (self.onChange && !(self.lastChanged && ((new Date()).getTime() - self.lastChanged.getTime() < self.interval))) {
					self.lastCanged = new Date();
					self.onChange(pos.rangeValue1, pos.rangeValue2);
				}
								
				break;
			}

			case "second_slider" : {		
				var pos = self.getPos();
				/// controls onChange call's frequency.
				if (self.onChange && !(self.lastChanged && ((new Date()).getTime() - self.lastChanged.getTime() < self.interval))) {
					self.lastCanged = new Date();
					self.onChange(pos.rangeValue1, pos.rangeValue2);
				}
				
				break;
			}
		}
	}
}

Zapatec.Slider.mouseUp = function (ev, self, target) {
	if (target) {
		switch (target.buttonType) {
			case "more" : {
				/// getting current position
				var pos = self.getPos();
				/// checking and then calculating
				if (pos.rangeValue1 + self.step < self.range[1]) {
					pos.rangeValue1 += self.step;
				} else {
					pos.rangeValue1 = self.range[1];			
				}
				/// setting Slider on new position and retrieving result to user function
				self.setPos(pos.rangeValue1);
				pos = self.getPos();
				self.onChange(pos.rangeValue1);
				self.newPosition(pos.rangeValue1);
				
				break;
			}

			case "less" : {
				/// getting current position
				var pos = self.getPos();
				/// checking and then calculating
				if (pos.rangeValue1 - self.step > self.range[0]) {
					pos.rangeValue1 -= self.step;
				} else {
					pos.rangeValue1 = self.range[0];
				}
				/// setting Slider on new position and retrieving result to user function
				self.setPos(pos.rangeValue1, 0, true);
				pos = self.getPos();
				self.onChange(pos.rangeValue1);
				self.newPosition(pos.rangeValue1);
				
				break;
			}

			case "first_slider" : case "second_slider" : {
				var pos = self.getPos();
				if ((self.dual === true) && (target.buttonType == "first_slider")) {
					/// setting new limits to another Slider(the one that has not been moved)
					if (self.orientation == "V") {
						self.slider1.Atr.top = self.getOffsetTop() + pos.value1 + self.minDist;
					} else {
						self.slider1.Atr.left = self.getOffsetLeft() + pos.value1 + self.minDist;
					}
				}
				if ((self.dual === true) && (target.buttonType == "second_slider")) {
					/// setting new limits to another Slider(the one that has not been moved)
					if (self.orientation == "V") {
						self.slider.Atr.bottom = self.getOffsetTop() + pos.value2 - self.minDist;
					} else {
						self.slider.Atr.right = self.getOffsetLeft() + pos.value2 - self.minDist;
					}
				}
				self.newPosition(pos.rangeValue1, pos.rangeValue2);
				
				break;
			}
		}
	}
}


/// Sets the Slider(s) on its(their) position
///
/// @param pos1 [number] position of the Slider#1
/// @param pos2 [number] position of the Slider#2
Zapatec.Slider.prototype.setPos = function (pos1, pos2, debug) {
	/// Checks position(s) of the Slider(s) on "correctness"
	//alert(pos1);
	pos1 = parseInt(pos1, 10) || 0;
	pos1 = Math.floor((pos1 - this.range[0]) / this.rangeStep);
	if (this.dual) {
		pos2 = parseInt(pos2, 10) || 0;
		pos2 = Math.floor((pos2 - this.range[0]) / this.rangeStep);
		if (pos2 > this.length) {
			pos2 = this.length;
		}
		if (pos2 <= pos1) {
			pos1 = pos2 - 1;
		}
		if (pos1 < 0) {
			pos1 = 0;
		}
	} else {	
		if (pos1 > this.length) 
		{
			pos1 = this.length;
		}
		if (pos1 < 0) {
			pos1 = 0;
		}
	}
	
	/// set Slider(s) on its(their) position(s)
	if (this.orientation == "V") { ///< if vertical
		this.slider.style.top = this.getOffsetTop() + pos1 + "px"; ///< #1 by Y coordinate 
		if (this.dual) { ///< #2 (if exist)
			this.slider1.style.top = this.getOffsetTop() + pos2 + "px"; ///< by Y coordinate
		}
	} else {
		this.slider.style.left = this.getOffsetLeft() + pos1 + "px"; ///< #1 by X coordinate
		if (this.dual) { ///< #2 (if exist)
			this.slider1.style.left = this.getOffsetLeft() + pos2 + "px"; ///< by X coordinate
		}
	}
}

/// Retrieves the relative position (relative to background of Slider(s)) of a Sliders

/// @return [object] { x, y } containing the limits.
Zapatec.Slider.prototype.getPos = function () {
	var pos = {};
	/// getting absolute position of background and of Slider(s)
	var bg = Zapatec.Utils.getAbsolutePos(this.bgDiv);
	var sl1 = Zapatec.Utils.getAbsolutePos(this.slider);
	if (this.dual) {
		var sl2 = Zapatec.Utils.getAbsolutePos(this.slider1);
	}

	/// calculating position(s)
	if (this.orientation == "V") {
		pos.value1 = sl1.y - bg.y; ///< #1 by Y coordinate
		if (this.dual) { //< #2 (if exist)
			pos.value2 = sl2.y - bg.y; ///< by Y coordinate
		}
	} else {
		pos.value1 = sl1.x - bg.x;///< #1 by X coordinate
		if (this.dual) { //< #2 (if exist)
			pos.value2 = sl2.x - bg.x; ///< by X coordinate
		}
	}
	pos.rangeValue1 = this.range[0] + this.rangeStep * pos.value1; 
	if (pos.value2) {
		pos.rangeValue2 = this.range[0] + this.rangeStep * pos.value2;
	}
	return pos; ///< returning position(s)
}

/// Retrieves the absolute limits (relative to <body>) of a background of Slider(s)

/// @return [object] { x, y } containing the limits.
Zapatec.Slider.prototype.getLimit = function () {
	var limit = {};
	/// calculating minimum
	if (this.orientation == "V") {
		limit.min = this.getOffsetTop();
	} else {
		limit.min = this.getOffsetLeft();		
	}
	limit.max = limit.min + this.length; ///< calculating maximum
	return limit; ///< returning limit
}
;
Zapatec.Utils.addEvent(window, 'load', Zapatec.Utils.checkActivation);
