///<reference path="../TESCO.js" />
///<reference path="../system/event.js" />
///<reference path="../system/event.manager.js" />
///<reference path="../system/exception.js" />
///<reference path="../system/DOM.node.js" />

/*
	Example:
	
	var mydiv = new TESCO.UI.Rectangle(document.createElement("div"));
	console.log(mydiv.x);
*/
TESCO.$("UI").Rectangle = (function() {

	//  constructor
	function _constructor(rect, x, y, width, height) {
		/// <summary>creates object from a node, and exposes dimension properties and methods for comparison</summary>
		/// <param name="rect">node</param>
		/// <param name="x">number (optional)</param>
		/// <param name="y">number (optional)</param>
		/// <param name="width">number (optional)</param>
		/// <param name="height">number (optional)</param>
		/// <returns>instance</returns>
		
		//  public properties
		this.rect = rect;
		this.css = rect.style;
		this.x = x || parseInt(this.css.left, 10) || rect.offsetLeft;
		this.y = y || parseInt(this.css.top, 10) || rect.offsetTop;
		this.width = width || rect.offsetWidth;
		this.height = height || rect.offsetHeight;
		return this;
	}

	_constructor.prototype.NAME = "TESCO.UI.Rectangle";
	
	//  public methods
	_constructor.prototype.intersects = function(rect) {
		return ((this.y - rect.y) < rect.height && (rect.y - this.y) < this.height) && ((this.x - rect.x) < rect.width && (rect.x - this.x) < this.width);
	}

	_constructor.prototype.isSameSize = function(rect) {
		return (this.height == rect.height && this.width == rect.width);
	}

	_constructor.prototype.isSamePosition = function(rect) {
		return (this.y == rect.y && this.x == rect.x);
	}

	_constructor.prototype.isSameSizeAndPosition = function(rect) {
		return (this.isSameSize(rect) && this.isSamePosition(rect));
	}

	_constructor.prototype.toString = function() {
		return this.NAME + ", version " + this.VERSION;
	}

	_constructor.prototype.getPageCoordinates = function() {
		/// <summary>measures x and y from page, instead of closest descendant with a position style</summary>
		/// <returns>coordinates object</returns>
		if (this.rect.offsetTop || this.rect.offsetTop === 0) {
			// todo: make this xbrowser compatible
			var o = this.rect;
			var y = 0, x = 0;
			while (o && (o.offsetLeft != null) && (o.offsetTop != null)) {
				x += o.offsetLeft;
				y += o.offsetTop;
				o = o.offsetParent;
			}
			o = this.rect;
			return {
				"x": x,
				"y": y
			}
		}
	}

	_constructor.prototype.getScreenCoordinates = function() {
		var _o = this.rect
		var pc = this.getPageCoordinates();
		// add the scroll position to get screen coordinates
		while ((new String(_o.tagName)).toUpperCase() != "BODY" && (_o.scrollLeft != null) && (_o.scrollTop != null)) {
			pc.x -= _o.scrollLeft;
			pc.y -= _o.scrollTop;
			_o = _o.parentNode;
		}
		return pc;
	}

	_constructor.prototype.getScroll = function() {
		var scrOfX = 0, scrOfY = 0;
		if (typeof (this.rect.pageYOffset) == 'number') {
			//Netscape compliant
			scrOfY = this.rect.pageYOffset;
			scrOfX = this.rect.pageXOffset;
		} else if (this.rect.scrollLeft || this.rect.scrollTop) {
			//DOM compliant
			scrOfY = this.rect.scrollTop;
			scrOfX = this.rect.scrollLeft;
		}
		return [scrOfX, scrOfY];
	}
	
	//  return _constructor as a function pointer
	return _constructor;
})();

/*
	Example:
	
	var mydiv = new TESCO.UI.Rectangle.Position(document.createElement("div"));
		mydiv.raise();
		
	IMPORTANT: Note that this class uses a z-index of 1000 as a starting point for stacking. 
				Do not use a z-index of over 1000 in CSS!
*/
//	Position object, derived from rectangle
TESCO.UI.Rectangle.Position = (function() {

    //	public static property to record highest z index
    _constructor.zCeiling = 1000;
    
    /*@cc_on
    var _iframeSrc = document.location.protocol + "//" + document.location.host + TESCO.sites.Configuration.application.path + "/blank.htm";
    @*/

    //  constructor
    function _constructor(rect, iframe) {
		/// <summary>creates positionalble object from a node, derived from TESCO.UI.Rectangle</summary>
		/// <param name="rect">node</param>
		/// <param name="iframe">boolean (for IE6 to prevent select boxes showing through)</param>
		/// <returns>instance</returns>
		
        _constructor.base.constructor.call(this, rect);

        //  create iframe for ie6 to stop select boxes showing through layers
        /*@cc_on
        if (iframe) {
            this.iframe = new TESCO.UI.Rectangle.Position(document.createElement("IFRAME"));
			this.iframe.rect.setAttribute("src", _iframeSrc);
			this.iframe.css.position = "absolute";
			this.iframe.hide();
            if (this.rect.parentNode) 
				this.rect.parentNode.appendChild(this.iframe.rect);
        }
        @*/

        this.v = TESCO.system.DOM.node.getStyle(this.rect, "visibility");
        this.z = parseInt(TESCO.system.DOM.node.getStyle(this.rect, "zIndex"), 10) || 0;
        _constructor.zCeiling = Math.max(this.z, _constructor.zCeiling);
        return this;
    }
    _constructor.extend(TESCO.UI.Rectangle);

    //  
    _constructor.prototype.NAME = "TESCO.UI.Rectangle.Position";
    
    //  public methods
    _constructor.prototype.moveToZ = function(i) {
        _constructor.zCeiling = Math.max(i, _constructor.zCeiling);
        this.z = this.css.zIndex = i;
    }

    _constructor.prototype.raise = function() {
        this.z = this.css.zIndex = (_constructor.zCeiling += 1);
    }

    _constructor.prototype.resize = function(w, h) {
        this.resizeW(w);
        this.resizeH(h);
    }

    _constructor.prototype.resizeW = function(w) {
        /*@cc_on
        if (this.iframe) this.iframe.resizeW(w);
        @*/
        if (this.rect.tagName.toLowerCase() == "iframe") {
            this.rect.width = w + TESCO.system.browser.px;
        }
        this.css.width = Math.max(0, w) + TESCO.system.browser.px;
        this.width = this.rect.offsetWidth;
    }

    _constructor.prototype.resizeH = function(h) {
        /*@cc_on
        if (this.iframe) this.iframe.resizeH(h);
        @*/
        if (this.rect.tagName.toLowerCase() == "iframe") {
            this.rect.height = h + TESCO.system.browser.px;
        }
        this.css.height = Math.max(0, h) + TESCO.system.browser.px;
        this.height = this.rect.offsetHeight;
    }

    _constructor.prototype.moveTo = function(x, y) {
        this.moveToX(x);
        this.moveToY(y);
    }

    _constructor.prototype.moveToX = function(x) {
        /*@cc_on
        if (this.iframe) {
			this.iframe.moveToX(x);
		}
        @*/
        this.x = parseInt(this.css.left = x + TESCO.system.browser.px, 10);
    }

    _constructor.prototype.moveToY = function(y) {
        /*@cc_on
        if (this.iframe) {
			this.iframe.moveToY(y);
		}
        @*/
        this.y = parseInt(this.css.top = y + TESCO.system.browser.px, 10);
    }

    _constructor.prototype.moveByX = function(x) {
        this.moveToX(this.x + x);
    }

    _constructor.prototype.moveByY = function(y) {
        this.moveToY(this.y + y);
    }

    _constructor.prototype.moveBy = function(x, y) {
        this.moveTo(this.x + x, this.y + y);
    }

    _constructor.prototype.show = function() {
        /*@cc_on
        if (this.iframe) this.iframe.show();
        @*/
        this.v = this.css.visibility = "visible";
    }

    _constructor.prototype.hide = function() {
        /*@cc_on
        if (this.iframe) this.iframe.hide();
        @*/
        this.v = this.css.visibility = "hidden";
    }

    _constructor.prototype.scrollBy = function(x, y) {
        if (typeof (this.rect.pageYOffset) == 'number') {
            //Netscape compliant
            this.rect.pageYOffset -= y;
            this.rect.pageXOffset -= x;
        } else if ((this.rect.scrollLeft != null) && (this.rect.scrollTop != null)) {
            //DOM compliant
            this.rect.scrollTop -= y;
            this.rect.scrollLeft -= x;
        }
    }

    _constructor.prototype.scrollTo = function(x, y) {
        if (typeof (this.rect.pageYOffset) == "number") {
            //Netscape compliant
            this.rect.pageYOffset = y;
            this.rect.pageXOffset = x;
        } else if ((this.rect.scrollLeft != null) && (this.rect.scrollTop != null)) {
            //DOM compliant
            this.rect.scrollTop = y;
            this.rect.scrollLeft = x;
        }
    }

    _constructor.prototype.makeCentreOfWindow = function() {
        var winSize = TESCO.UI.document.getWindowSize();
        this.moveTo((winSize.innerWidth - this.rect.offsetWidth) / 2, ((winSize.innerHeight - this.rect.offsetHeight) / 2) + ((TESCO.system.DOM.node.getStyle(this.rect, "position") === "fixed") ? 0 : TESCO.UI.document.getScrollXY()[1]));
    }

    _constructor.prototype.moveIntoView = function(x, y) {	//	Move the element to given location but force onto the viewable screen if necessary
        var _winSize = TESCO.UI.document.getWindowSize();
        //this.moveTo(	Math.max( Math.min( x || this.x , 0 + _winSize.width - this.width), 0),
        //				Math.max( Math.min( y || this.y , 0 + _winSize.height - this.height), 0));
        this.moveTo(Math.max(Math.min(x || this.x, 0 + _winSize.width - this.width), 0), (y || this.y));
    }
    //  return _constructor as a function pointer
    return _constructor;
})();

//  animation class
TESCO.UI.Rectangle.Position.Animation = (function() {
    
    //  constructor
	function _constructor(rect, iFrame) {
		/// <summary>creates a animatable object from a node, derived from TESCO.UI.Rectangle.Position</summary>
		/// <param name="rect">node</param>
		/// <param name="iframe">boolean (for IE6 to prevent select boxes showing through)</param>
		/// <returns>instance</returns>
		
	    _constructor.base.constructor.apply(this, arguments);
	    return this;
    }
    _constructor.extend(TESCO.UI.Rectangle.Position);
    
    _constructor.prototype.NAME = "TESCO.UI.Rectangle.Position.Animation";
    
    //  public methods
    _constructor.prototype.slide = function(distance, xAxis, yAxis, action, speed) {
	    var _self = this;
	    if (!this.slidePos) this.slidePos = 0;
	    this.slidePos += xAxis;
	    this.moveBy(xAxis*action, yAxis*action);
	    if (this.slidePos <= distance) {
		    this.timer = setTimeout(
		        function() {
			        _self.slide(distance, xAxis, yAxis, action, speed);
		        }, speed
            );
	    } else {
		    this.slidePos = 0;
	    }
    }
    
    _constructor.prototype.scroll = function(xAxis, yAxis, speed) {
	    var _self = this;
	    if (this.timer)
		    clearTimeout(this.timer);
	    var _scrollPos  = this.getScroll();
	    var _scrX       = _scrollPos[0];
	    var _scrY       = _scrollPos[1];
	    var _distX      = _scrX - xAxis;
	    if (_distX != 0) {
		    var _move = Math.round(_distX / 3);
		        _move = (_move == 0) ? _distX/Math.abs(_distX) : _move;
		    this.scrollBy(_move, 0);
		    this.timer = setTimeout(
			    function() {
				    _self.scroll(xAxis, yAxis, speed);
			    }, speed
            );
	    }
    }
    
    //  return _constructor as a function pointer
    return _constructor;
})();

//	drag and drop class
//	IMPORTANT: incomplete!
TESCO.UI.Rectangle.Position.Animation.DragDrop = (function() {
    
    //  public static properties/methods
    _constructor.activeItem = null;

    //  private static variables/functions
    var _hoverItem = null;
    var _self = this; 
    var _event = TESCO.system.event;
    var _detach = _event.detach;
    var _attach = _event.attach;
    
    function _drag(e) {
        if (_constructor.activeItem) {
            _constructor.activeItem.moveTo(
                Math.max(e.pageX - _constructor.activeItem.offsetX, 0), 
                Math.max(e.pageY - _constructor.activeItem.offsetY, 0)
            );
        }
    }
    
    function _mouseDown(e) {
        if (_hoverItem) {
            _constructor.activeItem = _hoverItem;
            TESCO.system.DOM.node.addClassName(_constructor.activeItem.rect, "dragging");
            _constructor.activeItem.raise();
            _constructor.activeItem.offsetX = e.pageX - _constructor.activeItem.x;
            _constructor.activeItem.offsetY = e.pageY - _constructor.activeItem.y;
        };
    }
    
    function _drop() {
        TESCO.system.DOM.node.removeClassName(_constructor.activeItem.rect, "dragging");
        _constructor.events.dispatchEvent("drop", _constructor.activeItem);
        _constructor.activeItem = null;
    }
    
    function _mouseOver(e, o) {
        _hoverItem = o;
        _hoverItem.css.cursor = "move";
    }
    
    function _mouseOut() {
        _hoverItem.css.cursor = "auto";
        _hoverItem = null;
    }
    
    //  create public static events object
    TESCO.system.event.manager.call((_constructor.events = {}), "drop");
    
    //  constructor
	function _constructor(rect, iFrame) {
		/// <summary>create a drag/droppable object from a node, derived from TESCO.UI.Rectangle.Position.Animation</summary>
		/// <param name="rect">node</param>
		/// <param name="iframe">boolean (for IE6 to prevent select boxes showing through)</param>
		/// <returns>instance</returns>
	    _constructor.base.constructor.apply(this, arguments);
	    return this;
    }
    _constructor.extend(TESCO.UI.Rectangle.Position.Animation);

    //  public properties/methods
    _constructor.prototype.NAME = "TESCO.UI.Rectangle.Position.Animation.DragDrop";

    _constructor.prototype.activate = function() {
        _attach(document, "mousemove", _drag);
        _attach(document, "mousedown", _mouseDown);
        _attach(document, "mouseup", _drop);
        _attach(this.rect, "mouseover", _mouseOver, this);
        _attach(this.rect, "mouseout", _mouseOut);
    }
    
    _constructor.prototype.deactivate = function() {
        _detach(document, "mousemove", _drag);
        _detach(document, "mousedown", _mouseDown);
        _detach(document, "mouseup", _drop);
        _detach(this.rect, "mouseover", _mouseOver);
        _detach(this.rect, "mouseout", _mouseOut);
        if (_constructor.activeItem) {
            if (this.rect === _constructor.activeItem.rect)
                _drop();
        }
        if (_hoverItem) {
            if (this.rect === _hoverItem.rect)
                _mouseOut();
        }
    }
  
    //  return constructor as a function pointer
    return _constructor;
})();

/*
	Example:
	
	console.log(TESCO.UI.document.width);
*/
TESCO.UI.document = (function() {
	/// <summary>static document object, provides cross browser methods to return dimensions</summary>
	
	function _constructor() {
		return this;
	}

	_constructor.prototype.NAME = "TESCO.UI.document";

	_constructor.prototype.width = function() {
		return document.documentElement.scrollWidth;
		// TODO: this needs to use the relevant property depending on browser/support/etc...
		//	return document.body.scrollWidth;
	}
	_constructor.prototype.height = function() {
		return document.documentElement.scrollHeight;
		// TODO: this needs to use the relevant property depending on browser/support/etc...
		//	return document.body.scrollHeight;
	}

	_constructor.prototype.getScrollXY = function() {
		var scrOfX = 0, scrOfY = 0;
		if (typeof (window.pageYOffset) == 'number') {//Netscape compliant
			scrOfY = window.pageYOffset;
			scrOfX = window.pageXOffset;
		} else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {//DOM compliant
			scrOfY = document.body.scrollTop;
			scrOfX = document.body.scrollLeft;
		} else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {//IE6 standards compliant mode
			scrOfY = document.documentElement.scrollTop;
			scrOfX = document.documentElement.scrollLeft;
		}
		return [scrOfX, scrOfY];
	}

	_constructor.prototype.getWindowSize = function() {
		var innerWidth, innerHeight;
		var width, height;
		if (window.clientHeight) {
			width = window.clientWidth, height = window.clientHeight;
		}
		if (window.innerHeight) {
			innerWidth = window.innerWidth; innerHeight = window.innerHeight;
		}
		var de = document.documentElement;
		if (de && de.clientWidth) {
			width = width || de.clientWidth;
			height = height || de.clientHeight
		}
		return {
			width: width, height: height, innerWidth: (innerWidth || width), innerHeight: (innerHeight || height)
		}
	}

	return new _constructor();
})();
