/*
################################################################
              THIS DOCUMENTATION IS OUTDATED!!!
################################################################

...so badly that i had to delete it...

*/
var Slider = (function() {
	var sliders = [];
	var menu_mode, menu_data, click_mode;
	var levels = [];
	var should_hide, hide_timer;
	
	var start_hide = function() {
		should_hide = true;
		var delay = (click_mode) ? 20 : 500;
		
		if (hide_timer) clearTimeout(hide_timer);
		
		hide_timer = setTimeout(function() {
			if (should_hide) levels[0].hide();
			hide_timer = null;
		}, delay);
		
	}
	var cancel_hide = function() {
		should_hide = false;
	}

	var klass = function(args) {
		var self = this;
		sliders.push(self);

		// required args (some depend on certain modes)
		var panel        = self.panel = args.panel;
		var controller   = args.controller;
		var side         = args.side;

		// required args (mode-dependent)
		var level        = args.level;
		var parent       = args.parent;
	
		// these ones have defaults
		var steps        = args.steps || 8;
		var delay        = args.delay || 0;
		var edge_size    = args.edge_size || 0;
		var hidden       = (args.hidden === false) ? false : true; // hidden by default

		var oncomplete, child;
	
		// if we didn't get the required args, throw a fit
		if (!panel)      return alert("slider: missing 'panel' arg. nothing to slide!");
		if (!controller) return alert("slider: missing 'controller' arg");
		if (!side)       return alert("slider: missing 'side' arg. without this, i don't know which way your element wants to slide");
		if (menu_mode) {
			if (typeof level == 'undefined')  return alert("slider: missing 'level' arg. needed for menu mode");
			if (level > 0 && !parent) return alert("slider: missing 'parent' arg. needed for menu mode");
		}
		
		// is this a horizontal or vertical slider?
		if (side == 'top' || side == 'bottom') hv = 'vertical';
		else hv = 'horizontal';

		// give our panel super powers
		extend_element(panel);
		
		// system vars
		var step, timer, height, width, distance, io, hv, oncomplete;
	
		if (hidden) {
			width  = panel.offsetWidth;
			height = panel.offsetHeight;
			if (hv == 'horizontal') distance = width - edge_size;
			else distance = height - edge_size;

			if (side == 'top') {
				panel.style.clip = 'rect(0 ' + width + 'px ' + distance + 'px 0)';
				panel.style.bottom = '-' + distance + 'px';
			}
			else if (side == 'bottom') {
				panel.style.clip = 'rect(' + distance + 'px ' + width + 'px ' + height + 'px 0)';
				panel.style.top = '-' + distance + 'px';
			}
			else if (side == 'left') {
				panel.style.clip = 'rect(0 ' + distance + 'px ' + height + 'px 0)';
				panel.style.right = '-' + distance + 'px';
			}
			else if (side == 'right') {
				panel.style.clip = 'rect(0 ' + width + 'px ' + height + 'px ' + distance + 'px)';
				panel.style.left = '-' + distance + 'px';
			}
		}
	
		self.toggle = function() {
			if (timer) {
				// we're already sliding, so cut this slide short and start it the other way
				if (io == 'out') io = 'in';
				else io = 'out';
				step = 0;
				
				// the timer will expire and take over from here
				return false;
			}
			
			// in or out?
			io = (hidden) ? 'out' : 'in';
		
			// grab dimensions
			width  = panel.offsetWidth;
			height = panel.offsetHeight;
		
			// get it going
			step = 0;
			go();
		
			return false;
		}
		self.hide   = function() {
			// if it's already in (or heading in), then we have nothing to do
			if (hidden || (timer && io == 'in')) return false;
			
			if (child) child.hide();
			levels[level] = null;
			
			return self.toggle();
		}
		self.show   = function() {
			setTimeout(function() { should_hide = false; }, 10);
			
			// if it's already out, then we have nothing to do
			if (! hidden) return false;

			if (menu_mode && levels[level]) levels[level].hide();
			levels[level] = self;
			
			// if this is a child slider, connect it to its parent
			if (parent) parent.set_child(self);

			self.toggle();
		}
		
		// destroy - if you permanently remove the panel from the document, you should call this.
		// i don't think anything will break if you don't, but it'll save resources.
		// basically, the class will lose the reference to this instance. this won't stop an instance
		// from working though, which means, for example, if you want to use single mode but have some
		// sliders be exempt from it, call this on them.
		self.destroy = function() {

			// look for this slider in the collection of sliders
			for (var i = 0; i < sliders.length; i++) {
				
				// take it out
				if (sliders[i] == self) {
					sliders.splice(i, 1);
					break;
				}
			}
		}

		// running - this returns true if a slide is in progress
		// can't really imagine where this might be useful, but it's there if you want it
		self.running = function() {
			if (timer) return true;
			return false;
		}
		

		// go - this is what actually animates the thing.
		var go = function() {
		
			// is this the first frame? if so, mark as visible
			if (!step) hidden = false;
		
			// this one always increments
			step++;
		
			// i base my math on this one, so it can increment or decrement, depending on in or out.
			var math_step = step;
			if (io == 'in') math_step = steps - step;

			// get the distance in pixels for this step
			if (hv == 'vertical') distance = Math.ceil((height - edge_size) / steps * math_step);
			else distance = Math.ceil((width - edge_size) / steps * math_step);

			// update the panel
			if (side == 'top') {
				panel.style.clip = 'rect(0 ' + width + 'px ' + (distance + edge_size) + 'px 0)';
				panel.style.bottom = '-' + (height - distance - edge_size) + 'px';
			}
			else if (side == 'bottom') {
				panel.style.clip = 'rect(' + (height - distance - edge_size) + 'px ' + width + 'px ' + height + 'px 0)';
				panel.style.top = '-' + (height - distance - edge_size) + 'px';
			}
			else if (side == 'left') {
				panel.style.clip = 'rect(0 ' + (distance + edge_size) + 'px ' + height + 'px 0)';
				panel.style.right = '-' + (width - distance - edge_size) + 'px';
			}
			else if (side == 'right') {
				panel.style.clip = 'rect(0 ' + width + 'px ' + height + 'px ' + (width - distance - edge_size) + 'px)';
				panel.style.left = '-' + (width - distance - edge_size) + 'px';
			}

			// are we done?
			if (step < steps) {
				
				// nope. set a timer for the next frame
				timer = setTimeout(go, delay);
			}
			else {
				// we're done
				timer = null;
			
				// if we slid in, mark as hidden
				if (io == 'in') hidden = true;
				else panel.style.clip = 'rect(0, 1000000, 1000000, 0)';
			
				// run the callback, if given
				if (oncomplete) oncomplete();
			}

			// grrr
			// my app needs this to update correctly
			if (args.model) args.model.rerender();
		}


		// don't call this - it's for instance-to-instance communication
		self.set_child = function(_child) {
			child = _child;
		}

		// set up the controller element
		if (click_mode) controller.onclick = self.show;
		else {
			controller.onmouseover = self.show;
			controller.onmouseout  = start_hide;
			panel.onmouseover      = cancel_hide;
			panel.onmouseout       = start_hide;
		};
		
	}

	klass.set_menu_mode   = function(mode) {
		menu_mode = mode;
	}
	klass.set_click_mode = function(mode) {
		click_mode = mode;

		if (click_mode) {
			var body_onclick = document.body.onclick;
			document.body.onclick = function() {
				start_hide();
				if (body_onclick) body_onclick();
			}
		}
	}

	return klass;
})();
