(function($){
    "use strict";

    // drag and drop editor
    $.fn.pixeDragDrop = function(){

        var editor = $(this);

        // mouse down on drag
        editor.on('mousedown', '.pixe-action.pixe-drag', function(e){
            if( e.buttons != 1 ) return;
            e.preventDefault();

            var item = $(this).closest('.pixe-wrapper, .pixe-column, .pixe-item');
            var itemType = $.pixeDragDrop.getItemType(item);

            // create action for history
            var subtype, effectColumns;
            if( item.hasClass('pixe-column') ){
                subtype = 'column';
                effectColumns = $();
            }
            var action = $.pixeHistory.createAction(item, 'move', subtype);
            
            // create handler
            var handler = $('<div class="pixe-dragging-handler" ></div>');
            handler.attr('data-type', itemType);
            handler.css({'left': e.clientX, 'top': e.clientY});
            
            // set handler title
            var settings = item.data('settings');
            if( settings._type == 'element' ){
                handler.append(pixeData.elements[item.data('settings')._element].title);
            }else if( settings._type == 'column' ){
                handler.append('Column');
            }else if( settings._type == 'wrapper' ){
                handler.append('Container');
            }else{
                if( settings._subtype == 'carousel' ){
                    handler.append('Carousel');
                }else if( settings._subtype == 'slider' ){
                    handler.append('Slider');
                }
            }

            $(document.body).append(handler);

            // dragging event
            $.pixeDragDrop.dragging(item, editor, {
                handler: handler,

                // save action to history
                beforeDropItem: function(dropTarget, item, editor){
                    if( subtype != 'column' ) return;

                    // save item & siblings
                    var row = item.attr('data-row');
                    effectColumns = effectColumns.add(item);
                    effectColumns = effectColumns.add(item.siblings('[data-row="' + row + '"]'));
                    
                    // target item & siblings
                    if( dropTarget.hasClass('pixe-h') ){
                        var targetColumn = dropTarget.closest('.pixe-column');
                        var targetRow = targetColumn.attr('data-row');
                        effectColumns = effectColumns.add(targetColumn);
                        effectColumns = effectColumns.add(targetColumn.siblings('[data-row="' + targetRow + '"]'));
                    }

                    // save previous value
                    effectColumns.each(function(){
                        $.pixeHistory.setActionStep(action, $(this), 'width', $(this).data('settings').width, true);
                    });
                },
                afterDropItem: function(item, editor){

                    // save new value
                    if( effectColumns ){
                        effectColumns.each(function(){
                            $.pixeHistory.setActionStep(action, $(this), 'width', $(this).data('settings').width);
                        });
                    }

                    // render style again for extra selector ( eg. hover propagation )
                    var affectItems = item;
                        affectItems = affectItems.add(item.find('.pixe-item, .pixe-column'));
                        $.pixeRenderElementStyles(affectItems);
                        
                    // update new location & add action
                    $.pixeHistory.setActionLocation(action, 'target');
                    $.pixeHistory.addAction(action);
                }
            });

        });

    }

    $.pixeDragDrop = {

        scrollArea: $(window),

        // return item type based on class
        getItemType: function(item){
            var itemType; 
            if( item.length > 1 || item.hasClass('pixe-wrapper') ){
                itemType = 'wrapper';
            }else if( item.hasClass('pixe-column') ){
                if( item.attr('data-type') == 'query' || item.find('.pixe-column[data-type="query"]').length ){
                    itemType = 'query';
                }else{
                    itemType = 'column';
                }
            }else if( item.hasClass('pixe-item') ){
                itemType = 'item';
            }else if( item.hasClass('pixe-block-container') ){
                var settings = item.data('settings');
                itemType = settings.type;
            }

            return itemType;
        },
        getContainerType: function(container){
            if( container.hasClass('pixe-editor') ){
                return 'editor';
            }
            if( container.hasClass('pixe-wrapper') || container.hasClass('pixe-wrapper-content') ){
                return 'wrapper';
            }
            if( container.attr('data-type') == 'query' || container.closest('.pixe-column[data-type="query"]').length ){
                return 'query';
            }
            return 'column';
        },

        // add dropable area to container
        addDropableArea: function(item, itemType, editor){

            function addArea(container){
                if( container.attr('data-droppable') == 'no' || container.closest('.pixe-preview-element').length ) return $();

                // assign row & column attributes for utility
                $.pixeColumn.setColumnAtts(container);

                // create dropable area object
                var dropable = $();
                var droparea;
    
                container.children().each(function(){
    
                    // add drop area before wrapper, item
                    if( $(this).hasClass('pixe-wrapper') || $(this).hasClass('pixe-item') ){
                        if( !$(this).is(item) ){
                            droparea = $('<div class="pixe-dropable-area pixe-v " ></div>');
                            droparea.insertBefore($(this));
                            dropable = dropable.add(droparea);
                        }
                    }else if( $(this).hasClass('pixe-column') ){
                        
                        // drop area for column item
                        if( $(this).is('[data-col="0"]') ){
                            droparea = $('<div class="pixe-dropable-area pixe-v" ></div>');
                            droparea.insertBefore($(this));
                            dropable = dropable.add(droparea);
                        }
                        if( itemType == 'column' || itemType == 'query' ){

                            // add dropable area to the column
                            if( !$(this).is(item) ){
                                droparea = $('<div class="pixe-dropable-area pixe-h" ></div>');
                                $(this).append(droparea);
                                dropable = dropable.add(droparea);
        
                                if( $(this).is('[data-col="0"]') ){
                                    droparea = $('<div class="pixe-dropable-area pixe-h pixe-left" ></div>');
                                    $(this).append(droparea);
                                    dropable = dropable.add(droparea);
                                }

                            // remove dropable area of previous column in the same row
                            }else{
                                if( !$(this).is('[data-col="0"]') ){
                                    droparea = $(this).prev().children('.pixe-dropable-area:not(.pixe-left)');
                                    if( droparea.length ){
                                        dropable = dropable.not(droparea);
                                        droparea.remove();
                                    }
                                }
                            }
                        }
                    }
    
                });
    
                // last drop area
                droparea = $('<div class="pixe-dropable-area pixe-v " ></div>');
                container.append(droparea);
                dropable = dropable.add(droparea);
    
                // remove next dropable area if current item is item / wrapper
                if( item.hasClass('pixe-wrapper') || item.hasClass('pixe-item') ){
                    droparea = item.next('.pixe-dropable-area');
                    if( droparea.length ){
                        dropable = dropable.not(droparea);
                        droparea.remove();
                    }
                }
                return dropable;
            }

            var dropables = addArea(editor); // editor

            if( itemType == 'query' || itemType == 'column' || itemType == 'item' ){
                editor.find('.pixe-wrapper-content, .pixe-column-content, .pixe-tab-container').each(function(){

                    // do nothing if inside dragging item
                    if( $(this).closest(item).length || $(this).closest('.pixe-item-post-list, .pixe-item-synced-block, .pixe-item-taxonomy-list').length ) return;

                    // add dropable area to wrapper & column
                    dropables = dropables.add(addArea($(this)));
                });
            }

            return dropables;
        },

        // save & return position while dragging
        itemScrollOffset: 0,
        saveScrollPos: function(item){
            var t = this;
            if( item.hasClass('pixe-editor') ){
                t.centerScrollElement = $(document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2));
                
                // find another element if it's a dropable area
                if( t.centerScrollElement.hasClass('pixe-dropable-area') ){
                    if( t.centerScrollElement.hasClass('pixe-v') ){
                        if( t.centerScrollElement.prev().length ){
                            t.centerScrollElement = t.centerScrollElement.prev();
                        }else{
                            t.centerScrollElement = t.centerScrollElement.next();
                        }
                    }else{
                        t.centerScrollElement = t.centerScrollElement.parent();
                    }
                }
                t.itemScrollOffset = t.centerScrollElement.offset().top - t.scrollArea.scrollTop();
            }else{
                t.itemScrollOffset = item.offset().top - t.scrollArea.scrollTop();
            }
        },
        returnScrollPos: function(item){
            var t = this;
            if( item.hasClass('pixe-editor') ){
                t.scrollArea.scrollTop(t.centerScrollElement.offset().top - t.itemScrollOffset);
                t.centerScrollElement = undefined;
            }else{
                t.scrollArea.scrollTop(item.offset().top - t.itemScrollOffset);
            }
                
        },

        // add & remove handler
        draggingStart: function(itemType){
            $(document.body).addClass('pixe-is-dragging');
            $(document.body).attr('data-drag-type', itemType);
        },
        draggingEnd: function(){
            $(document.body).removeClass('pixe-is-dragging');
            $(document.body).removeClass('pixe-with-drop-target');
            $(document.body).removeAttr('data-drag-type');
        },

        // dragging
        dragging: function(item, editor, settings){
            settings = $.extend({
                scrollTarget: item,
                events: 'mousemove mouseup'
            }, settings);

            var t = this;
            var itemType = $.pixeDragDrop.getItemType(item);

            // start drag
            t.draggingStart(itemType);

            // add dropable area
            var droparea;
            $.pixeDragDrop.saveScrollPos(settings.scrollTarget);
            droparea = $.pixeDragDrop.addDropableArea(item, itemType, editor);
            $.pixeDragDrop.returnScrollPos(settings.scrollTarget);

            // mouse in - set target
            var dropTarget;
            droparea.on('mouseenter', function(){
                dropTarget = $(this);
                $(this).addClass('pixe-current-target');
                $(document.body).addClass('pixe-with-drop-target');

            // mouse out - remove target
            }).on('mouseout', function(){
                dropTarget = false;
                $(this).removeClass('pixe-current-target');
                $(document.body).removeClass('pixe-with-drop-target');
            });
            
            // drag events
            function dragElement(e){
                e.preventDefault();
                if( settings.onMousemove ){ settings.onMousemove(e); }

                // move handler if exists
                if( settings.handler ){ settings.handler.css({'left': e.clientX, 'top': e.clientY}); }

                // mouse up - drop action
                if( e.buttons != 1 ){
                    if( settings.onMouseup ){ settings.onMouseup(e); }

                    // get drop container area
                    var dropContainer;
                    if( dropTarget ){
                        if( dropTarget.hasClass('pixe-h') ){
                            dropContainer = dropTarget.closest('.pixe-column').parent().closest('.pixe-editor, .pixe-wrapper, .pixe-column, .pixe-item');
                        }else{
                            dropContainer = dropTarget.closest('.pixe-editor, .pixe-wrapper, .pixe-column, .pixe-item');
                        }
                    }
 
                    // if drop target exists
                    if( dropTarget && t.isDroppable(item, dropContainer) ){

                        // before drop item action
                        if( settings.beforeDropItem ){ settings.beforeDropItem(dropTarget, item, editor); }

                        // save scroll position
                        $.pixeDragDrop.saveScrollPos(dropTarget);

                        // drop item to target
                        $.pixeDragDrop.dropItem(item, dropTarget);
                        if( settings.onDropItem ){ settings.onDropItem(item, editor); }

                        // remove dropable area
                        droparea.remove();
                        $.pixeDragDrop.returnScrollPos(item);

                        // set each item status
                        $.pixeEditor.setStatus();

                        // after drop item action
                        if( settings.afterDropItem ){ settings.afterDropItem(item, editor); }

                    // if drop target not exists
                    }else{
                        $.pixeDragDrop.saveScrollPos(settings.scrollTarget);
                        droparea.remove();
                        $.pixeDragDrop.returnScrollPos(settings.scrollTarget);
                    }

                    // destory handler if exists
                    if( settings.handler ){ settings.handler.remove(); }

                    // return settings & remove dropable area
                    $.pixeDragDrop.draggingEnd();
                    $(document.body).off(settings.events, dragElement);
                }
            }
            
            // dragging & dropping
            $(document.body).on(settings.events, dragElement);

        },

        // drop item
        dropItem: function(item, dropTarget){
            
            // move column to target
            if( item.hasClass('pixe-column') || item.hasClass('pixe-block-container') ){

                var itemRow = item.attr('data-row');
                var oldSiblings = item.siblings('[data-row="' + itemRow + '"]');

                // move to target and reset column width
                if( dropTarget.hasClass('pixe-v') ){

                    // move target
                    item.insertAfter(dropTarget);

                    // resize target
                    $.pixeColumn.setWidth(item, '1/1', false);

                    // resize old siblings
                    oldSiblings.each(function(){
                        $.pixeColumn.setWidth($(this), '1/' + oldSiblings.length, false);
                    });
                    
                }else if( dropTarget.hasClass('pixe-h') ){

                    var targetColumn = dropTarget.parent('.pixe-column');
                    var targetRow = targetColumn.attr('data-row');
                    var targets = targetColumn.add(targetColumn.siblings('[data-row="' + targetRow + '"]'));

                    // move target
                    if( dropTarget.hasClass('pixe-left') ){
                        item.insertBefore(targetColumn);
                    }else{
                        item.insertAfter(targetColumn);
                    }

                    // if not in the same row
                    if( !targets.is(item) ){

                        // resize target
                        targets = targets.add(item);
                        targets.each(function(){
                            $.pixeColumn.setWidth($(this), '1/' + targets.length, false);
                        });

                        // resize old row
                        oldSiblings.each(function(){
                            $.pixeColumn.setWidth($(this), '1/' + oldSiblings.length, false);
                        });
                    }

                }
                
            // move item / wrapper to target
            }else{
                item.insertAfter(dropTarget);
            }

        },

        // check if item is droppable
        isDroppable: function(item, container, alert = true){
            if( !(item && item.length) || !(container && container.length) ) return false;
            
            var t = this;
            var iType = t.getItemType(item);
            if( iType == 'item' || iType == 'column' ) return true;

            var cType = t.getContainerType(container);
            if( iType == 'query' && cType == 'query' ){
                if( alert ){
                    $.pixeConfirmationBox({
                        title: 'Denied',
                        message: 'Cannot place "Query Column" inside another "Query Column".',
                        cancelText: ''
                    });
                }
                
                return false;
            }else if( iType == 'wrapper' && cType == 'wrapper' ){
                if( alert ){
                    $.pixeConfirmationBox({
                        title: 'Denied',
                        message: 'Cannot nested "Container" elements.',
                        cancelText: ''
                    });
                }
                return false;
            }

            return true;
        }

    };


})(jQuery);	