var MovingViewModel = Class.create();
MovingViewModel.Type = { Box: 1 }
MovingViewModel.prototype = {
    initialize: function(container, type) {
        this.container = $(container);
        this.width = Element.getWidth(this.container);
        this.items = new Hash();
        this.activeEventId = 0;
        this.currentItemId = null;

        //assume all elements have same size
        var arrItem = this.container.select('.item');
        for (var ixItem = arrItem.length - 1; ixItem >= 0; ixItem--) {
            var item = arrItem[ixItem];
            var sItemId = Element.identify(item)
            this.items.set(sItemId, new MovingBoxModel(this, sItemId));
        }
        this.draw();
    },
    resize: function() {
        var iHeight = 0;
        this.items.each(function(pair) {
            var item = pair.value;
            var iBottom = item.top + item.height;
            if (iBottom > iHeight) iHeight = iBottom;
        });
        Element.setStyle(this.container, { height: iHeight + 'px' });
        //Element.scrollTo(this.container);
        _OnResize();
    },
    getCollision: function(sourceItem) {
        var bLeft = false;
        var bTop = false;
        var bRight = false;
        var bBottom = false;
        var bItemBelow = false;
        var iSrcUpperLeft = [sourceItem.left, sourceItem.top];
        var iSrcUpperRight = [sourceItem.left + sourceItem.width, sourceItem.top];
        var iSrcLowerLeft = [sourceItem.left, sourceItem.top + sourceItem.height];
        var iSrcLowerRight = [sourceItem.left + sourceItem.width, sourceItem.top + sourceItem.height];

        var iTop = 0;
        var iLeft = 0;
        this.items.each(function(pair) {
            var item = pair.value;
            var belowThreshold = 5;

            if (item == sourceItem) return;

            var iUpperLeft = [item.left - item.marginLeft - item.paddingLeft, item.top - item.marginTop - item.paddingTop];
            var iUpperRight = [item.left + item.width + item.marginRight + item.paddingRight + item.marginLeft + item.paddingLeft, item.top - item.marginTop - item.paddingTop];
            var iLowerLeft = [item.left - item.paddingLeft - item.marginLeft, item.top + item.height + item.paddingBottom + item.paddingTop + item.marginBottom + item.marginTop];
            var iLowerRight = [item.left + item.width + item.marginRight + item.paddingRight + item.paddingLeft + item.marginLeft, item.top + item.height + item.paddingBottom + item.paddingTop + item.marginBottom + item.marginTop];

            //Upper Left Collision
            if (iSrcUpperLeft[0] < iUpperRight[0] && iSrcUpperLeft[0] > iUpperLeft[0] &&
                iSrcUpperLeft[1] < iLowerRight[1] && iSrcUpperLeft[1] > iUpperRight[1]) {

                if (iLowerRight[0] > iLeft) iLeft = iLowerRight[0];
                if (iLowerRight[1] > iTop) iTop = iLowerRight[1];
            }
            //Lower Left Collision
            if (iSrcLowerLeft[0] < iUpperRight[0] && iSrcLowerLeft[0] > iUpperLeft[0] &&
                iSrcLowerLeft[1] < iLowerRight[1] && iSrcLowerLeft[1] > iUpperRight[1]) {
                if (iUpperRight[0] > iLeft) iLeft = iUpperRight[0];
                bBottom = true;
            }
            //Upper Right Collision
            if (iSrcUpperRight[0] > iUpperLeft[0] && iSrcUpperRight[0] < iUpperRight[0] &&
                iSrcUpperRight[1] < iLowerLeft[1] && iSrcUpperRight[1] > iUpperLeft[1]) {
                bRight = true;
                if (iLowerLeft[1] > iTop) iTop = iLowerLeft[1];
            }
            //Lower Right Collision
            if (iSrcLowerRight[0] > iUpperLeft[0] && iSrcLowerRight[0] < iUpperRight[0] &&
                iSrcLowerRight[1] < iLowerLeft[1] && iSrcLowerRight[1] > iUpperLeft[1]) {
                bRight = true;
                bBottom = true;
            }
            //            if ((iSrcUpperRight[0] > iUpperLeft[0] && iSrcUpperRight[0] < iUpperRight[0] &&
            //                iSrcUpperRight[1] < iLowerLeft[1] && iSrcUpperRight[1] > iUpperLeft[1] - belowThreshold) ||
            //                (iSrcLowerRight[0] > iUpperLeft[0] && iSrcLowerRight[0] < iUpperRight[0] &&
            //                iSrcLowerRight[1] < iLowerLeft[1] && iSrcLowerRight[1] > iUpperLeft[1] - belowThreshold)) {
            //                    bItemBelow = true;
            //                }
            //            }
        } .bind(this));

        var bHasCollision = iLeft || iTop || bRight || bBottom;
        return { hasCollision: bHasCollision, left: iLeft, top: iTop, right: bRight, bottom: bBottom, itemBelow: bItemBelow };
    },
    update: function(itemId, html) {
        this.currentItemId = itemId;

        //Element.scrollTo(this.container.up('div'));
        var oItem = this.items.get(itemId);
        var oResp = null;
        if (oItem) {
            if (oItem.eventId == this.activeEventId) {
                oItem.reset();
                oItem.eventId = -1;
                return;
            }
            this.activeEventId++;
            oItem.eventId = this.activeEventId;
            var oResp = oItem.update(html);
        }
        if (!oResp) return; //no change detected

        setTimeout(this.draw.bind(this), 50);
    },
    draw: function() {
        //Redraw each component while movement in place
        var bMoving = false;
        this.items.each(function(pair) {
            var item = pair.value;
            item.move();
            if (item.isMoving) bMoving = true;
        });
        this.resize();
    },
    reset: function() {
        this.currentItemId = null;
        this.items.each(function(pair) {
            var item = pair.value;
            item.reset();
        });
        this.resize();
    }
}

var MovingBoxModel = Class.create();
MovingBoxModel.prototype = {
    initialize: function(observer, elementId) {
        this.observer = observer;
        elementId = $(elementId).identify();
        this.id = elementId;
        //this.html = $(elementId).innerHTML;
        this.width = Element.getWidth(elementId);
        this.height = Element.getHeight(elementId);
        this.isMoving = false;
        Element.absolutize(this.id);
        this.left = parseInt(Element.getStyle(elementId, 'left')) || 0;
        this.top = parseInt(Element.getStyle(elementId, 'top')) || 0;
        this.paddingLeft = parseInt(Element.getStyle(elementId, 'paddingLeft')) || 0;
        this.paddingTop = parseInt(Element.getStyle(elementId, 'paddingTop')) || 0;
        this.paddingRight = parseInt(Element.getStyle(elementId, 'paddingRight')) || 0;
        this.paddingBottom = parseInt(Element.getStyle(elementId, 'paddingBottom')) || 0;
        this.marginLeft = parseInt(Element.getStyle(elementId, 'marginLeft')) || 0;
        this.marginTop = parseInt(Element.getStyle(elementId, 'marginTop')) || 0;
        this.marginRight = parseInt(Element.getStyle(elementId, 'marginRight')) || 0;
        this.marginBottom = parseInt(Element.getStyle(elementId, 'marginBottom')) || 0;
        this.width = this.width - this.paddingLeft - this.paddingRight;
        this.height = this.height - this.paddingTop - this.paddingBottom;
        this.originalLeft = this.left;
        this.originalTop = this.top;
        this.originalHeight = this.height;
        this.originalWidth = this.width;
        Element.setStyle(this.id, { width: this.width + 'px', height: this.height + 'px' });
        this.eventId = -1;
        this.reset();
        if (this.observer.resize) this.observer.resize();
    },
    _handleValidUpdate: function() {
        if (this.eventId == this.observer.activeEventId) return true;
        this.isMoving = false;
        this.reset();
        return false;
    },
    reset: function() {
        Element.removeClassName(this.id, 'active');
        this.isMoving = false;
        Element.setStyle(this.id, { height: this.originalHeight + 'px', width: this.originalWidth + 'px', overflow: 'hidden' });
        this.height = this.originalHeight;
        this.width = this.originalWidth;
        this.left = this.originalLeft;
        this.top = this.originalTop;
        this.draw();
        //$(this.id).innerHTML = this.html;
    },
    draw: function() {
        Element.setStyle(this.id, { left: this.left + 'px', top: this.top + 'px' });
        _OnResize();
    },
    update: function(html) {

        if (!this._handleValidUpdate()) return null;
        if (this.isMoving) return null;
        var element = $(this.id);
        if (!element) return null;

        this.isMoving = true;
        Element.addClassName(this.id, 'active');
        Element.addClassName(this.id, 'moving');
        Element.setStyle(this.id, { height: 'auto', width: '', overflow: '' });
        //element.innerHTML = html;
        var iNewHeight = parseInt(Element.getStyle(this.id, 'height')) || 0;
        if (iNewHeight < this.originalHeight) iNewHeight = this.originalHeight;
        var iNewWidth = parseInt(Element.getStyle(this.id, 'width')) || 0;
        if (iNewWidth < this.originalWidth) iNewWidth = this.originalWidth;
        Element.setStyle(this.id, { height: this.height + 'px', width: this.width + 'px', overflow: 'hidden' });
        Element.removeClassName(this.id, 'active');

        var ef = new Effect.Tween(null, -1, 1, { queue: { scope: 'movingViewItem' }, duration: .5, afterFinish: function() {
            Element.removeClassName(this.id, 'moving');
            if (!this._handleValidUpdate()) return;
            this.height = iNewHeight;
            Element.setStyle(this.id, { height: this.height + 'px' });
            this.observer.draw.bind(this);
            setTimeout(this.observer.draw.bind(this.observer), 0);
            setTimeout(this.observer.draw.bind(this.observer), 500);
            setTimeout(this.observer.draw.bind(this.observer), 1000);
        } .bind(this)
        }, function(p) {
            if (!this._handleValidUpdate()) return;
            if (p < 0) return;
            Element.addClassName(this.id, 'active');
            this.height = this.originalHeight + (parseInt((iNewHeight - this.originalHeight) * (p * p)) || 0);
            this.width = this.originalWidth + (parseInt((iNewWidth - this.originalWidth) * (p * p)) || 0);
            Element.setStyle(this.id, { height: this.height + 'px', width: this.width + 'px' });
            this.observer.draw();
        } .bind(this));

        this.move();

        return { newHeight: iNewHeight, newWidth: iNewWidth };
    },
    _move: function(direction) {
        var oCollision = this.observer.getCollision(this);
        if (!oCollision.hasCollision) { this.isMoving = false; return false; }
        this.isMoving = true;

        if (direction == 'down') {
            if (oCollision.top) this.top = oCollision.top;
        } else if (direction == 'right') {
            if (oCollision.left) this.left = oCollision.left;
        }
        //if (oCollision.bottom) this.bottom = oCollision.bottom;
        //if (oCollision.right) this.right = oCollision.right;

        this.draw();
        return true;
    },
    move: function() {

        if (!this._move('right')) return;
        if (!this._move('down')) return;

        var oCollision = this.observer.getCollision(this);
        if (!oCollision.hasCollision) this.isMoving = false;
    }
}