(function ($) {

    /* вспомогательные функции */
    function startTracking(e, ui) {
        startX = $(ui.helper).position().left;
        startY = $(ui.helper).position().top;
        startTime = e.timeStamp;
    }

    function computeAcceleration(e, ui) {
        var dX = ui.position.left - startX;
        var dY = ui.position.top - startY;
        var dTime = e.timeStamp - startTime;
        var a = 500 * (Math.sqrt(dX*dX + dY*dY) / ((dTime <= 0) ? 1 : dTime));
        a = ((a > 100) ? 100 : a) / 20;
        return {'top': dY * a, 'left': dX * a};
    }

    function stopTracking(e, ui) {
        var item = $(ui.helper).picsaPhoto(), a, pos;
        if (item) {
            //a = computeAcceleration(e, ui);
            a = {'top': 0, 'left': 0};
            pos = C._fixPosition(item,
                (ui.position.top + a.top) / C.SCALE,
                (ui.position.left + a.left) / C.SCALE);
                $(ui.helper).appendTo(C.root)
                        .animate({'top': pos.top * C.SCALE, 'left': pos.left * C.SCALE}, 500, 'linear',
                            function () {
                                item.setPosition(C.SCALE);
                        });
        }
    }

    var C = $.extend(Observable(), {

        /** аттрибуты **/
        SCALE: 1,

        IMAGEWIDTH: 200,

        GAP: 25,

        TOPMARGIN: 20,

        LEFTMARGIN: 10,

        STICK: true,

        //MAXCOLUMNS: 6,

        CURCOLUMN: 0,

        /** методы **/
        _alignToColumn: function (x) {
            var w = C.IMAGEWIDTH + C.GAP;
            return C.LEFTMARGIN + w * Math.round(x / w);
        },

        _fixPosition: function (item, top, left) {
            // fix horizontal position
            if (C.STICK) {
                left = C._alignToColumn(left);
            }
            left = (left < 0) ? 0 : left;


            // fix vertical position
            var eles, i, obj, t;
            var distTop = 1e8, clTop, distBottom = 1e8, clBottom;
            if (C.STICK) {
                eles = $(item.layer).siblings();
                for (i = 0; i < eles.length; i++) {
                    obj = $(eles[i]).picsaPhoto();
                    if (left === C._alignToColumn(obj.left)) {
                        // check for top
                        t = Math.abs(obj.top + obj.height + C.GAP - top);
                        if (t < distTop) {
                            distTop = t;
                            clTop = obj;
                        }
                        // check for bottom
                        t = Math.abs(obj.top - C.GAP - top - item.height);
                        if (t < distBottom) {
                            distBottom = t;
                            clBottom = obj;
                        }
                    }
                }

                if (item.height > Math.min(distTop, distBottom)) {
                    if (distTop > distBottom) {
                        // STICK to bottom
                        top = clBottom.top - item.height - C.GAP;
                    } else {
                        // STICK to top
                        top = clTop.top + clTop.height + C.GAP;
                    }
                }
            }

            top = (top < C.TOPMARGIN) ? C.TOPMARGIN : top;

            // apply new positions to photo item
            item.top = top;
            item.left = left;

            return {'top': top, 'left': left};
        },

        init: function (root, data, allowDrag, allowCover) {
            $("body").append('<div id="photos-overlay"></div>');

            C.root = root;
            C.isOwner = PICSA.isOwner || false;
            C.allowDrag = allowDrag;
            C.allowCover = allowCover;
            $("div.picsa-photo", root).each(function (i, ele) {
                C.addPhoto(data[i], ele);
            });

            C.countColumns();
            $(window).resize(function(){
                C.countColumns();
            });

            $(window).unload( function () {
                var item={};
                if(C.isOwner && allowDrag){
                    item = $("div.picsa-photo:first", root).picsaPhoto();
                    if(item){
                        item.submitPositions();
                    }
                }
            });
        },

        /**
         * Подсчет количества колонок на холсте
         */
        countColumns: function() {
            var width = $(window).width();
            C.MAXCOLUMNS = Math.floor((width - C.LEFTMARGIN) / C.SCALE / (C.IMAGEWIDTH + C.GAP));
        },

        /**
         * Добавляем картинку на холст
         * @param   data    {Object}    метаданные
         * @param   markup  {Node}      существующая разметка
         *
         * @return  {PICSA.Photo}       добавленная картинка
         */
        addPhoto: function (data, markup) {
            var item = new PICSA.Photo(data, markup, this.isOwner, this.allowCover);
            var me = this;

            item.attachObserver("loaded", function () {
                item.setScale(C.SCALE, true);
                $(item.layer).appendTo(C.root);

                if(C.allowDrag){
                    $(item.layer).draggable({
                                zIndex: 120,
                                cursor: 'crosshair',
                                refreshPositions: true,
                                start: function (e, ui) {
                                    item.hideInfo();
                                    
                                    $(item.layer).addClass("dragging");
                                    $(ui.helper).appendTo("#photos-overlay");
                                    startTracking(e, ui);
                                },
                                drag: startTracking,
                                stop: function (e, ui) {
                                    $(ui.helper).appendTo(C.root);
                                    $(ui.helper).removeClass('drag-preview');
                                    $(ui.helper).css({marginTop:'', marginLeft:''});

                                    $(item.layer).removeClass("dragging");
                                    stopTracking(e, ui);
                                }
                    });
                    $(item.layer).hover(function(){
                        item.showDragMark();
                    },function(){
                        item.hideDragMark();
                    });
                }
            }, true);

            item.attachObserver("remove", function () {
                /*
                    в IE при удалении слоя из документа
                    сносит крышу у виджета draggable
                */
                item.layer.css("display", "none");
                (function () {
                    if (item.layer.hasClass("ui-draggable-dragging")) {
                        setTimeout(arguments.callee, 500);
                    } else {
                        item.layer.remove();
                    }
                })();

                if(me.empty()){
                    me.notify('empty');
                }
            });
            item.attachObserver("cover", function(data){
                C.notify("cover", data);
            });

            return item;
        },

        _moveAway: function (item) {
            var items = [];

            var x1 = item.left, x2 = item.left + item.width;
            var y1 = item.top, y2 = item.top + item.height;

            $(item.layer).siblings().each(function () {
                var obj = $(this).picsaPhoto();
                if ( ((x1 <= obj.left && obj.left <= x2) || (x1 <= obj.left + obj.width && obj.left + obj.width <= x2) || (x1 >= obj.left && obj.left + obj.width >= x2) || (x1 <= obj.left && obj.left + obj.width <= x2)) &&
                    ((y1 <= obj.top && obj.top <= y2) || (y1 <= obj.top + obj.height && obj.top + obj.height <= y2) || (y1 >= obj.top && obj.top + obj.height >= y2) || (y1 <= obj.top && obj.top + obj.height <= y2)) ) {
                    items.push(obj);
                }
            });

            $.each(items, function () {
                var sx = item.left + item.width / 2,
                    sy = item.top + item.height / 2;
                var dx = this.left + this.width / 2 - sx,
                    dy = this.top + this.height / 2 - sy;

                $(this.layer).animate({
                    'top': (dy >= 0 ? "+=" : "-=") + Math.abs(dy),
                    'left': (dx >= 0 ? "+=" : "-=") + Math.abs(dx)
                }, 500);
            });
        },

        alignToGrid: function () {
            var items = [], i, h, lastX = -1e8, curX, item;

            $("> *", C.root).each(function() {
                var item = $(this).picsaPhoto();
                if (item) {
                    item.columnIndex = Math.round(item.left / (C.IMAGEWIDTH + C.GAP));
                    items.push(item);
                }
            });

            items = items.sort(function (a, b) {
                var d = a.columnIndex - b.columnIndex;
                return d ? d : a.top - b.top;
            });

            for (i = 0; i < items.length; i++) {
                item = items[i];
                curX = C._alignToColumn(item.left);
                if (lastX < curX) {
                    h = C.TOPMARGIN;
                    lastX = curX;
                }

                item.top = h;
                item.left = curX;
                h = h + item.height + C.GAP;
            }

            C._redraw();
        },

        changeZoom: function (data) {
            C.SCALE = data;
            C._redraw();
        },

        shiftColumn: function (data, column, preventSubmit) {
            var items = [];
            var me = this;
            C.CURCOLUMN = column;

            $("> *", C.root).each(function () {
                var item = $(this).picsaPhoto();
                
                if (item) {
                    if (Math.round((item.left - C.LEFTMARGIN) / (C.IMAGEWIDTH + C.GAP)) == column) {//C.CURCOLUMN) {
                        items.push(item.layer.get(0));
                    }
                }
            });

            var item = new PICSA.Photo(data, null, this.isOwner, this.allowCover);

            item.attachObserver("loaded", function () {
                item.left = C.LEFTMARGIN + column * (C.IMAGEWIDTH + C.GAP);//C.CURCOLUMN * (C.IMAGEWIDTH + C.GAP);
                item.top = C.TOPMARGIN - C.GAP - item.height;
                item.setScale(C.SCALE, true);
                $(item.layer).appendTo(C.root);

                items.push(item.layer.get(0));

                if(C.allowDrag){
                    $(item.layer).draggable({
                                zIndex: 120,
                                cursor: 'crosshair',
                                refreshPositions: true,
                                start: function (e, ui) {
                                    item.hideInfo();
                                    
                                    $(item.layer).addClass("dragging");
                                    $(ui.helper).appendTo("#photos-overlay");
                                    startTracking(e, ui);
                                },
                                drag: startTracking,
                                stop: function (e, ui) {
                                    $(ui.helper).appendTo(C.root);
                                    $(ui.helper).removeClass('drag-preview');
                                    $(ui.helper).css({marginTop:'', marginLeft:''});
                                    
                                    $(item.layer).removeClass("dragging");
                                    stopTracking(e, ui);
                                }
                    });
                    $(item.layer).hover(function(){
                        item.showDragMark();
                    },function(){
                        item.hideDragMark();
                    });
                }

                $(items).each(function(){
                    var ele = $(this);
                    var pic = $(this).picsaPhoto();

                    ele.animate({'top': '+=' + (C.GAP + item.height) * C.SCALE}, 'fast', function () {});

                    pic.top = (ele.position().top + (C.GAP + item.height) * C.SCALE);
                    pic.setPosition(C.SCALE, preventSubmit);
                });
            });
            

            item.attachObserver("remove", function () {
                /*
                    в IE при удалении слоя из документа
                    сносит крышу у виджета draggable
                */
                item.layer.css("display", "none");
                (function () {
                    if (item.layer.hasClass("ui-draggable-dragging")) {
                        setTimeout(arguments.callee, 500);
                    } else {
                        item.layer.remove();
                    }
                })();

                if(me.empty()){
                    me.notify('empty');
                }
            });
            item.attachObserver("cover", function(data){
                C.notify("cover", data);
            });
        },

        safeShiftColumn: function (data, preventSubmit) {
            var left =0;
            var column_left = C.LEFTMARGIN + C.CURCOLUMN * (C.IMAGEWIDTH + C.GAP);
            //C.CURCOLUMN = column;

            var photo = $("div.picsa-photo-active");
            if(photo.length > 0){
                left = $('div.picsa-photo-active').offset().left;
                if(Math.abs(left - column_left) < C.IMAGEWIDTH){
                      C.incrementColumn();
                }
            }

            C.shiftColumn(data, C.CURCOLUMN, preventSubmit);
            return C.CURCOLUMN;
        },

        incrementColumn: function() {
            C.CURCOLUMN++;
            if (C.CURCOLUMN > C.MAXCOLUMNS-1) {
                C.CURCOLUMN = 0;
            }
            return C.CURCOLUMN;
        },

        fillWidth: function(width){
            var items = [], columns = Math.floor((width - C.LEFTMARGIN) / C.SCALE / (C.IMAGEWIDTH + C.GAP));
            var i, j, item, x, y;

            $("> *", C.root).each(function() {
                var item = $(this).picsaPhoto();
                if (item) {
                    items.push(item);
                }
            });

            items = items.sort(function (a, b) {
                var d = a.top - b.top;
                return d ? d : a.left - b.left;
            });

            for (x = C.LEFTMARGIN, i = 0; i < columns; i++) {
                for (y = C.TOPMARGIN, j = i; j < items.length; j = j + columns) {
                    item = items[j];
                    item.top = y;
                    item.left = x;
                    y = y + item.height + C.GAP;
                }
                x = x + C.IMAGEWIDTH + C.GAP;
            }

            C._redraw(true);
            return columns;
        },

        fillWindow: function () {
            C.fillWidth($(window).width());
        },

        _redraw: function (preventSubmit) {
            var scale = C.SCALE;
            $("> *", C.root).each(function() {
                var item = $(this).picsaPhoto();
                item.setScale(scale, preventSubmit);
            });
        },

        empty: function(){
            var photo = null,
                me = this;
            $("div.picsa-photo:first", me.root).each(function(){
                photo = this;
            });
            return !photo;
        }
    });

    PICSA.Canvas = C;

})(jQuery);
