/**
 * @private
 */
Ext.define('Ext.view.DragZone', {
    extend: 'Ext.dd.DragZone',
    
    containerScroll: false,
 
    constructor: function(config) {
        var me = this,
            view, ownerCt, el;
 
        Ext.apply(me, config);
 
        // Create a ddGroup unless one has been configured.
        // User configuration of ddGroups allows users to specify which
        // DD instances can interact with each other. Using one
        // based on the id of the View would isolate it and mean it can only
        // interact with a DropZone on the same View also using a generated ID.
        if (!me.ddGroup) {
            me.ddGroup = 'view-dd-zone-' + me.view.id;
        }
 
        // Ext.dd.DragDrop instances are keyed by the ID of their encapsulating element.
        // So a View's DragZone cannot use the View's main element because the DropZone
        // must use that because the DropZone may need to scroll on hover at a scrolling boundary,
        // and it is the View's main element which handles scrolling.
        // We use the View's parent element to drag from. Ideally, we would use the internal
        // structure, but that is transient; DataViews recreate the internal structure dynamically
        // as data changes.
        // TODO: Ext 5.0 DragDrop must allow multiple DD objects to share the same element.
        view = me.view;
 
        // This is for https://www.w3.org/TR/pointerevents/ platforms.
        // On these platforms, the pointerdown event (single touchstart) is reserved for
        // initiating a scroll gesture. Setting the items draggable defeats that and
        // enables the touchstart event to trigger a drag.
        //
        // Two finger dragging will still scroll on these platforms.
        view.setItemsDraggable(true);
 
        ownerCt = view.ownerCt;
        
        // We don't just grab the parent el, since the parent el may be
        // some el injected by the layout
        if (ownerCt) {
            el = ownerCt.getTargetEl().dom;
        }
        else {
            el = view.el.dom.parentNode;
        }
        
        me.callParent([el]);
 
        me.ddel = document.createElement('div');
        me.ddel.className = Ext.baseCSSPrefix + 'grid-dd-wrap';
    },
 
    init: function(id, sGroup, config) {
        var me = this,
            eventSpec = {
                itemmousedown: me.onItemMouseDown,
                scope: me
            };
 
        // If there may be ambiguity with touch/swipe to scroll and a drag gesture
        // trigger drag start on longpress and a *real* mousedown.
        if (Ext.supports.Touch) {
            eventSpec.itemlongpress = me.onItemLongPress;
 
            // Longpress fires contextmenu in some touch platforms, so if we are using longpress
            // inhibit the contextmenu on this element
            eventSpec.contextmenu = {
                element: 'el',
                fn: me.onViewContextMenu
            };
        }
        
        me.initTarget(id, sGroup, config);
        me.view.mon(me.view, eventSpec);
    },
 
    onValidDrop: function(target, e, id) {
        this.callParent([target, e, id]);
        
        // focus the view that the node was dropped onto so that keynav will be enabled.
        if (!target.el.contains(Ext.Element.getActiveElement())) {
            target.el.focus();
        }
    },
 
    onViewContextMenu: function(e) {
        if (e.pointerType !== 'mouse') {
            e.preventDefault();
        }
    },
 
    onItemMouseDown: function(view, record, item, index, e) {
        // Ignore touchstart.
        // For touch events, we use longpress.
        if (e.pointerType === 'mouse') {
            this.onTriggerGesture(view, record, item, index, e);
        }
    },
 
    onItemLongPress: function(view, record, item, index, e) {
        // Ignore long mousedowns.
        // The initial mousedown started the drag.
        // For touch events, we use longpress.
        if (e.pointerType !== 'mouse') {
            this.onTriggerGesture(view, record, item, index, e);
        }
    },
 
    onTriggerGesture: function(view, record, item, index, e) {
        var navModel;
 
        // Only respond to longpress for touch dragging.
        // Reject drag start if mousedown is on the actionable cell of a grid view
        if ((e.pointerType === 'touch' && e.type !== 'longpress') ||
            (e.position && e.position.isEqual(e.view.actionPosition))) {
            return;
        }
 
        if (!this.isPreventDrag(e, record, item, index)) {
            navModel = view.getNavigationModel();
 
            // Since handleMouseDown prevents the default behavior of the event, which
            // is to focus the view, we focus the view now.  This ensures that the view
            // remains focused if the drag is cancelled, or if no drag occurs.
            //
            // A Table event will have a position property which is a CellContext
            if (e.position) {
                navModel.setPosition(e.position);
            }
            // Otherwise, just use the item index
            else {
                navModel.setPosition(index);
            }
            
            this.handleMouseDown(e);
        }
    },
 
    /**
     * @protected
     * Template method called upon mousedown. May be overridden in subclasses, or configured
     * into an instance.
     *
     * Return `true` to prevent drag start.
     * @param {Ext.event.Event} e The mousedown event.
     * @param {Ext.data.Model} record The record mousedowned upon.
     * @param {HTMLElement} item The grid row mousedowned upon.
     * @param {Number} index The row number mousedowned upon.
     */
    isPreventDrag: function(e, record, item, index) {
        return !!e.isInputFieldEvent;
    },
 
    getDragData: function(e) {
        var view = this.view,
            item = e.getTarget(view.getItemSelector());
 
        if (item) {
            return {
                copy: view.copy || (view.allowCopy && e.ctrlKey),
                event: e,
                view: view,
                ddel: this.ddel,
                item: item,
                records: view.getSelectionModel().getSelection(),
                fromPosition: Ext.fly(item).getXY()
            };
        }
    },
 
    onInitDrag: function(x, y) {
        var me = this,
            data = me.dragData,
            view = data.view,
            selectionModel = view.getSelectionModel(),
            record = view.getRecord(data.item);
 
        // Update the selection to match what would have been selected if the user had
        // done a full click on the target node rather than starting a drag from it
        if (!selectionModel.isSelected(record)) {
            selectionModel.selectWithEvent(record, me.DDMInstance.mousedownEvent);
        }
        
        data.records = selectionModel.getSelection();
 
        Ext.fly(me.ddel).setHtml(me.getDragText());
        me.proxy.update(me.ddel);
        me.onStartDrag(x, y);
        
        return true;
    },
 
    getDragText: function() {
        var count = this.dragData.records.length;
        
        return Ext.String.format(this.dragText, count, count === 1 ? '' : 's');
    },
 
    getRepairXY: function(e, data) {
        return data ? data.fromPosition : false;
    }
});