SL.dnd = {};

(function() {

	SL.dnd.SortableTable = Class.create(SL.Component, {

		init : function() {

			this.config.setDefault('callbacks', {});
			this.config.setDefault('grip_class', null);

			this.cb = this.config.ensureHash('cb');
			this.gripClass = this.config.get('grip_class');

			this.root = this.e.down('tbody');
			if (!this.root) {
				this.root = this.e;
			}

			this._initObservedElements();
		},

		_initObservedElements : function() {

			if (this.observed) {
				this.observed.each(function(e) {
					var element = $(e.key);
					if (element) {
						element.stopObserving('mousedown', e.value);
					}
				}.bind(this));
			}

			this.observed = $H();

			this.root.childElements().each(function(e) {
				if (this.gripClass) {
					e.select('.' + this.gripClass).each(function(e, e2) {
						this._observeElement(e, e2);
					}.bind(this, e));
				} else {
					this._observeElement(e);
				}
			}.bind(this));
		},

		_observeElement : function(e, e2) {
			if (!this.observed.get(e2) && !e2.down('form')) {
				var h = this.onMouseDown.bind(this, e);
				e2.observe('mousedown', h);
				this.observed.set(e2.identify(), h);
			}
		},

		onChildComponentChanged : function(component) {
			this._initObservedElements();
		},

		onMouseDown : function(e, event) {
			if (this.mouseMoveEvent || !event.isLeftClick()) {
				return;
			}

			this.mouseMoveEvent = this.onMouseMove.bind(this, e);
			this.mouseUpEvent = this.onMouseUp.bind(this, e);
			document.observe('mousemove', this.mouseMoveEvent);
			document.observe('mouseup', this.mouseUpEvent);

			var offset = e.cumulativeOffset();
			this.xOffset = event.pageX - offset.left;

			event.stop();
		},

		onMouseUp : function(e, event) {

			document.stopObserving('mousemove', this.mouseMoveEvent);
			document.stopObserving('mouseup', this.mouseUpEvent);

			this.mouseMoveEvent = undefined;
			this.mouseUpEvent = undefined;

			if (this.marker) {
				var index = this.marker.up().childElements().indexOf(this.marker);

				if (this.ghost) {
					this.marker.replace(this.ghost.down('tr'));
					this.ghost.remove();
				} else {
					this.ghost.remove();
				}
				this.marker = undefined;
				this.ghost = undefined;

				if (this.cb.dragStop) {
					this.cb.dragStop(this, e, index);
				}
			}
			this.previousIndex = undefined;
		},

		onMouseMove : function(e, event) {

			if (!this.marker) {

				if (this.cb.dragStart) {
					this.cb.dragStart(this, e);
				}

				this.previousIndex = e.up().childElements().indexOf(e);

				this.marker = this.createMarker(e);

				this.ghost = SL.utils.createBodyElement('table', {
					'class' : this.e.readAttribute('class')
				});

				this.ghost.setStyle({
					width : this.e.getWidth() + 'px'
				});
				this.ghost.hide();

				var tb = new Element('tbody');
				;
				this.ghost.appendChild(tb);

				tb.appendChild(e.replace(this.marker));

				this.ghost.setStyle({
					position : 'absolute',
					cursor : 'move',
					zIndex : '1000'
				});
				this.ghost.setOpacity('0.8');

				this.ghost.show();
			}

			this.ghost.setStyle({
				left : event.pageX - this.xOffset + 'px',
				top : event.pageY - (e.getHeight() / 2) + 'px'
			});

			var row = this.findRowAt(event.pageX, event.pageY);

			if (row) {
				if (row.e == this.marker) {
					return;
				}

				if (row.overlap < 0.5) {
					row.e.insert({
						after : this.marker
					});
				} else {
					row.e.insert({
						before : this.marker
					});
				}
			}

			event.stop();
		},

		findRowAt : function(x, y) {

			var rows = this.root.childElements();
			for ( var i = 0; i < rows.length; i++) {
				var e = rows[i];
				var offset = e.cumulativeOffset();
				if (x < offset.left || y < offset.top) {
					continue;
				}
				if (x > offset.left + e.getWidth()) {
					continue;
				}
				if (y > offset.top + e.getHeight()) {
					continue;
				}

				var overlap = 1.0 - ((y - offset.top) / e.getHeight());

				return {
					e : e,
					overlap : overlap
				};
			}

			return false;
		},

		createMarker : function(origRow) {

			var e = SL.elements.createBlindRow(origRow);

			var mdiv = new Element('div');
			mdiv.setStyle({
				margin : '0',
				padding : '0',
				/* width : origRow.getWidth() - 4 + 'px', */
				height : origRow.getHeight() - 4 + 'px',
				border : 'dotted #777 2px'
			});

			e.down('td').appendChild(mdiv);

			return e;
		},

		cleanup : function() {
			if (this.mouseMoveEvent) {
				document.stopObserving('mousemove', this.mouseMoveEvent);
				document.stopObserving('mouseup', this.mouseUpEvent);
			}

			if (this.ghost) {
				this.ghost.remove();
			}
		}
	});

	SL.dnd.Draggable = Class.create(SL.Element, {

		init : function() {

			this.config.setDefault('grip_class', null);
			this.config.setDefault('orientation', 'both');

			this.config.setDefault('spacer_width', 10);

			this.cb = this.config.ensureHash('cb');
			this.orientation = this.config.get('orientation');

			var gripClass = this.config.get('grip_class');

			this.handle = gripClass ? this.e.down('.' + gripClass) : this.e;
			if (!this.handle) {
				console.log("Handle not found:" + gripClass);
				return;
			}

			this.mouseDownHandler = this.onMouseDown.bind(this);

			this.handle.observe('mousedown', this.mouseDownHandler);
			this.handle.setStyle({
				cursor : 'move'
			});

			this.mouseClickHandler = this.onMouseClick.bind(this);
			this.handle.observe('click', this.mouseClickHandler);
		},

		onMouseDown : function(event) {
			if (this.mouseMoveEvent || !event.isLeftClick()) {
				return;
			}
			this.mouseMoveEvent = this.onMouseMove.bind(this);
			this.mouseUpEvent = this.onMouseUp.bind(this);
			document.observe('mousemove', this.mouseMoveEvent);
			document.observe('mouseup', this.mouseUpEvent);

			this.xOffset = event.pointerX() - this.e.offsetLeft;
			this.yOffset = event.pointerY() - this.e.offsetTop;

			event.stop();
		},

		onMouseClick : function(event) {
			event.stop();
		},

		onMouseUp : function(event) {

			document.stopObserving('mousemove', this.mouseMoveEvent);
			document.stopObserving('mouseup', this.mouseUpEvent);

			this.mouseMoveEvent = undefined;
			this.mouseUpEvent = undefined;

			if (this.active) {
				this.active = false;
				if (this.cb.dragStop) {
					this.cb.dragStop(this);
				}
			}
			this.reenableIframes();

			event.stop();
		},

		onMouseMove : function(event) {

			if (!this.active) {
				this.active = true;
				if (this.cb.dragStart) {
					this.cb.dragStart(this);
				}
			}

			var minX = this.config.get('minX') || 0;
			var minY = this.config.get('minY') || 0;

			var op = this.e.getOffsetParent();

			var opWidth = op.getWidth();
			var opHeight = op.getHeight();

			if (op.tagName == 'BODY') {
				var bh = SL.effects.getBodyHeight();
				if (opHeight < bh) {
					opHeight = bh;
				}
			}

			var maxX = opWidth - this.config.get('spacer_width') - (this.config.get('maxX') || 0) - this.e.getWidth();
			var maxY = opHeight - this.config.get('spacer_width') - (this.config.get('maxY') || 9) - this.e.getHeight();

			if (this.orientation == 'horizontal') {
				this.e.setStyle({
					left : Math.min(Math.max(minX, event.pointerX() + 2 - this.xOffset), maxX) + 'px'
				});
			} else if (this.orientation == 'vertical') {
				this.e.setStyle({
					top : Math.min(Math.max(minY, event.pointerY() + 2 - this.yOffset), maxY) + 'px'
				});
			} else {
				this.e.setStyle({
					left : Math.min(Math.max(minX, event.pointerX() + 2 - this.xOffset), maxX) + 'px',
					top : Math.min(Math.max(minY, event.pointerY() + 2 - this.yOffset), maxY) + 'px'
				});
			}

			if (this.cb.moved) {
				this.cb.moved(this);
			}

			this.updateIframes();

			event.stop();
		},

		updateIframes : function() {
			if (this.iframes) {
				this.iframes.each(function(b) {
					b.e.clonePosition(b.frame);
				});
			} else {
				this.iframes = $A();
				$$('iframe').each(function(frame) {
					var e = SL.utils.createBodyElement('div');
					e.clonePosition(frame);
					e.setStyle({
						position : 'absolute',
						backgroundColor : '#ffffff'
					});
					e.setOpacity(0.0);
					this.iframes.push({
						e : e,
						frame : frame
					});
				}.bind(this));
			}
		},

		reenableIframes : function() {
			if (this.iframes) {
				this.iframes.each(function(b) {
					b.e.remove();
				});
				this.iframes = undefined;
			}
		},

		cleanup : function() {
			this.handle.stopObserving('mousedown', this.mouseDownHandler);
			this.reenableIframes();
		},

		updatePos : function(x, y) {

			if (x) {
				this.e.setStyle({
					left : x + 'px'
				});
			}

			if (y) {
				this.e.setStyle({
					top : y + 'px'
				});
			}

			if (this.cb.moved) {
				this.cb.moved(this);
			}
		},

		getPos : function() {
			return this.e.positionedOffset();
		}

	});

	SL.dnd.Dashboard = Class.create(SL.Component, {

		init : function() {
			this.config.setDefault('prefix', '');

			this.prefix = this.config.get('prefix');

			console.log("Init dashboard");

			this.container = this.e.select('.' + this.prefix + 'dashboard_container');
			this.dashlets = $A();

			console.log("Dashboard containers:", this.container.length);

			this.container.each(function(c) {
				c.childElements().each(function(d) {
					if (d.hasClassName(this.prefix + 'dashlet')) {
						new SL.dnd.Dashlet(d, {
							grip_class : this.prefix + 'dashlet_head',
							dashboard : this
						});
						this.dashlets.push(d);
					}
				}.bind(this));
			}.bind(this));

			this.updateHeight();

			this.e.up('.' + this.prefix + 'dashboard').setStyle({
				position : 'relative'
			});
		},

		updateHeight : function() {

			this.container.each(function(c) {
				c.setStyle({
					height : 'auto'
				});
			}.bind(this));

			this._updateHeightDefered.bind(this).defer();
		},

		_updateHeightDefered : function() {

			this.maxHeight = this.e.down().getHeight();

			this.container.each(function(c) {
				if (this.maxHeight < c.getHeight()) {
					this.maxHeight = c.getHeight();
				}
			}.bind(this));

			if (this.maxHeight <= 100) {
				this.maxHeight = 100;
			}

			this.container.each(function(c) {
				c.setStyle({
					height : this.maxHeight + 'px'
				});
			}.bind(this));
		},

		notifyNewPosition : function(dashlet) {

			this.updateHeight();

			var url = this.config.get('notify_url');

			url = url.replace('@container@', dashlet.e.up().id);

			if (dashlet.isNew) {
				url = url.replace('@id@', dashlet.e.id);
				url = url.replace('@type@', dashlet.config.get('dashlet_type'));
			} else {
				url = url.replace('@id@', dashlet.e.down('.' + this.prefix + 'dashlet_body').down().id);
				url = url.replace('@type@', '');
			}

			url = url.replace('@index@', dashlet.index);

			new Ajax.Request(url, {
				onSuccess : function(dashlet, o) {
					eval(o.responseText);
				}.bind(this, dashlet)
			});
		},

		attachNewDashlet : function(dashlet) {
			this.dashlets.push(dashlet.e);
			dashlet.e.addClassName(this.prefix + 'dashlet');
			dashlet.e.setStyle({
				display : 'block'
			});
			dashlet.isNew = undefined;
			this.updateHeight();
		},

		onChildComponentChanged : function() {
			this.updateHeight();
		}

	});

	SL.dnd.Dashlet = Class.create(SL.dnd.Draggable, {

		init : function($super) {
			this.config.setDefault("cb", {
				dragStart : this.dragStart.bind(this),
				dragStop : this.dragStop.bind(this),
				moved : this.moved.bind(this)
			});

			this.dashboard = this.config.get('dashboard');
			this.isNew = this.config.get('is_new');

			$super();
		},

		dragStart : function() {

			this.parent = this.e.up();
			this.index = this.parent.childElements().indexOf(this.e);

			var width = this.e.getWidth();
			var height = this.e.getHeight();

			this.e.setStyle({
				width : width + 'px',
				height : height + 'px',
				position : 'absolute'
			});

			this.ghost = new Element('div');

			this.ghost.setStyle({
				width : '100%',
				height : height + 'px',
				border : 'dotted #aaa 2px'
			});

			if (!this.isNew) {
				this.dashboard.e.up().appendChild(this.e.replace(this.ghost));
			} else {
				this.rep = new Element('div');
				this.rep.setStyle({
					width : width + 'px',
					height : height + 'px'
				});
				this.e.setStyle({
					display : 'table-cell',
					width : 'auto'
				});

				this.dashboard.e.up().appendChild(this.e.replace(this.rep));
			}

			this.config.set('minY', this.e.getHeight() / -2);
			this.config.set('maxY', this.e.getHeight() / -2);
		},

		dragStop : function() {
			if (this.rep && !this.ghost.up()) {
				this.rep.replace(this.e);
			} else {
				this.ghost.replace(this.e);
			}
			this.e.setStyle({
				position : 'static',
				width : 'auto',
				height : 'auto'
			});

			var parent = this.e.up();
			var index = parent.childElements().indexOf(this.e);

			if (this.parent == parent && this.index == index) {
				return;
			}

			this.parent = parent;
			this.index = index;

			this.dashboard.notifyNewPosition.bind(this.dashboard, this).defer();
		},

		moved : function() {
			var offset = this.e.cumulativeOffset();
			var hovered = this.findAt(this.dashboard.dashlets, offset.left + this.e.getWidth() / 2, offset.top + this.e.getHeight() / 2);

			if (hovered) {
				if (hovered.overlap < 0.4) {
					hovered.e.insert({
						after : this.ghost
					});
					this.dashboard.updateHeight();
				} else if (hovered.overlap > 0.6) {
					hovered.e.insert({
						before : this.ghost
					});
					this.dashboard.updateHeight();
				}
				return;
			}

			hovered = this.findAt(this.dashboard.container, offset.left + this.e.getWidth() / 2, offset.top + this.e.getHeight() / 2);

			if (!hovered) {
				return;
			}

			if (this.isNew && hovered.e == this.parent) {
				return;
			}

			hovered.e.appendChild(this.ghost);
			this.dashboard.updateHeight();
		},

		findAt : function(elements, x, y) {

			for ( var i = 0; i < elements.length; i++) {
				var e = elements[i];
				if (!e || e.up == undefined || this.e == e || this.ghost == e || (this.ghost.up() && this.ghost.up() == e)) {
					continue;
				}

				var offset = e.cumulativeOffset();
				if (x < offset.left || y < offset.top) {
					continue;
				}
				if (x > offset.left + e.getWidth()) {
					continue;
				}
				if (y > offset.top + e.getHeight()) {
					continue;
				}

				var overlap = 1.0 - ((y - offset.top) / e.getHeight());

				return {
					e : e,
					overlap : overlap
				};
			}

			return false;
		}

	});

})();

