/**
 * @class Ext.chart.interactions.ItemEdit
 * @extends Ext.chart.interactions.ItemHighlight
 *
 * The 'itemedit' interaction allows the user to edit store data
 * by dragging series items in the chart.
 *
 * The 'itemedit' interaction extends the
 * {@link Ext.chart.interactions.ItemHighlight 'itemhighlight'} interaction,
 * so it also acts like one. If you need both interactions in a single chart,
 * 'itemedit' should be sufficient. Hovering/tapping will result in highlighting,
 * and dragging will result in editing.
 */
Ext.define('Ext.chart.interactions.ItemEdit', {
    extend: 'Ext.chart.interactions.ItemHighlight',
    requires: [
        'Ext.tip.ToolTip'
    ],

    type: 'itemedit',
    alias: 'interaction.itemedit',

    isItemEdit: true,

    config: {
        /**
         * @cfg {Object} [style=null]
         * The style that will be applied to the series item on dragging.
         * By default, series item will have no fill,
         * and will have a dashed stroke of the same color.
         */
        style: null,

        /**
         * @cfg {Function/String} [renderer=null]
         * A function that returns style attributes for the item that's being dragged.
         * This is useful if you want to give a visual feedback to the user when
         * they dragged to a certain point.
         *
         * @param {Object} [data] The following properties are available:
         *
         * @param {Object} data.target The object containing the xField/xValue or/and
         * yField/yValue properties, where the xField/yField specify the store records
         * being edited and the xValue/yValue the target values to be set when
         * the interaction ends. The object also contains the 'index' of the record
         * being edited.
         * @param {Object} data.style The style that is going to be used for the dragged item.
         * The attributes returned by the renderer will be applied on top of this style.
         * @param {Object} data.item The series item being dragged.
         * This is actually the {@link Ext.chart.AbstractChart#highlightItem}.
         *
         * @return {Object} The style attributes to be set on the dragged item.
         */
        renderer: null,

        /**
         * @cfg {Object/Boolean} [tooltip=true]
         */
        tooltip: true,

        gestures: {
            dragstart: 'onDragStart',
            drag: 'onDrag',
            dragend: 'onDragEnd'
        },

        cursors: {
            ewResize: 'ew-resize',
            nsResize: 'ns-resize',
            move: 'move'
        }

        /**
         * @private
         * @cfg {Boolean} [sticky=false]
         */
    },

    /**
     * @event beginitemedit
     * Fires when item edit operation (dragging) begins.
     * @param {Ext.chart.AbstractChart} chart The chart the interaction belongs to.
     * @param {Ext.chart.interactions.ItemEdit} interaction The interaction.
     * @param {Object} item The item that is about to be edited.
     */

    /**
     * @event enditemedit
     * Fires when item edit operation (dragging) ends.
     * @param {Ext.chart.AbstractChart} chart The chart the interaction belongs to.
     * @param {Ext.chart.interactions.ItemEdit} interaction The interaction.
     * @param {Object} item The item that was edited.
     * @param {Object} target The object containing target values the were used.
     */

    item: null, // Item being edited.

    applyTooltip: function (tooltip) {
        if (tooltip) {
            var config = Ext.apply({}, tooltip, {
                renderer: this.defaultTooltipRenderer,
                constrainPosition: true,
                shrinkWrapDock: true,
                autoHide: true,
                offsetX: 10,
                offsetY: 10
            });

            tooltip = new Ext.tip.ToolTip(config);
        }

        return tooltip;
    },

    defaultTooltipRenderer: function (tooltip, item, target, e) {
        var parts = [];

        if (target.xField) {
            parts.push(target.xField + ': ' + target.xValue);
        }
        if (target.yField) {
            parts.push(target.yField + ': ' + target.yValue);
        }

        tooltip.setHtml(parts.join('<br>'));
    },

    onDragStart: function (e) {
        var me = this,
            chart = me.getChart(),
            item = chart.getHighlightItem();

        if (item) {
            chart.fireEvent('beginitemedit', chart, me, me.item = item);

            // If ItemEdit interaction comes before other interactions
            // in the chart's 'interactions' config, this will
            // prevent other interactions hijacking the 'dragstart'
            // event. We only stop event propagation is there's
            // an item to edit under cursor/finger, otherwise we
            // let other interactions (e.g. 'panzoom') handle the event.
            return false;
        }
    },

    onDrag: function (e) {
        var me = this,
            chart = me.getChart(),
            item = chart.getHighlightItem(),
            type = item && item.sprite.type;

        if (item) {
            switch (type) {
                case 'barSeries':
                    return me.onDragBar(e);
                    break;
                case 'scatterSeries':
                    return me.onDragScatter(e);
                    break;
            }
        }
    },

    highlight: function (item) {
        var me = this,
            chart = me.getChart(),
            flipXY = chart.getFlipXY(),
            cursors = me.getCursors(),
            type = item && item.sprite.type,
            style = chart.el.dom.style;

        me.callParent([item]);

        if (item) {
            switch (type) {
                case 'barSeries':
                    if (flipXY) {
                        style.cursor = cursors.ewResize;
                    } else {
                        style.cursor = cursors.nsResize;
                    }
                    break;
                case 'scatterSeries':
                    style.cursor = cursors.move;
                    break;
            }
        } else {
            chart.el.dom.style.cursor = 'default';
        }
    },

    onDragBar: function (e) {
        var me = this,
            chart = me.getChart(),
            isRtl = chart.getInherited().rtl,
            flipXY = chart.isCartesian && chart.getFlipXY(),
            item = chart.getHighlightItem(),
            marker = item.sprite.getMarker('items'),
            instance = marker.getMarkerFor(item.sprite.getId(), item.index),
            surface = item.sprite.getSurface(),
            surfaceRect = surface.getRect(),
            xy = surface.getEventXY(e),
            matrix = item.sprite.attr.matrix,
            renderer = me.getRenderer(),
            style, changes, params, positionY;

        if (flipXY) {
            positionY = isRtl ? surfaceRect[2] - xy[0] : xy[0];
        } else {
            positionY = surfaceRect[3] - xy[1];
        }
        style = {
            x: instance.x,
            y: positionY,
            width: instance.width,
            height: instance.height + (instance.y - positionY),
            radius: instance.radius,
            fillStyle: 'none',
            lineDash: [4, 4],
            zIndex: 100
        };
        Ext.apply(style, me.getStyle());

        if (Ext.isArray(item.series.getYField())) { // stacked bars
            positionY = positionY - instance.y - instance.height;
        }
        me.target = {
            index: item.index,
            yField: item.field,
            yValue: (positionY - matrix.getDY()) / matrix.getYY()
        };

        params = [chart, {
            target: me.target,
            style: style,
            item: item
        }];
        changes = Ext.callback(renderer, null, params, 0, chart);
        if (changes) {
            Ext.apply(style, changes);
        }
        // The interaction works by putting another series item instance
        // under 'itemedit' ID with a slightly different style (default) or
        // whatever style the user provided.
        item.sprite.putMarker('items', style, 'itemedit');

        me.showTooltip(e, me.target, item);
        surface.renderFrame();
    },

    onDragScatter: function (e) {
        var me = this,
            chart = me.getChart(),
            isRtl = chart.getInherited().rtl,
            flipXY = chart.isCartesian && chart.getFlipXY(),
            item = chart.getHighlightItem(),
            marker = item.sprite.getMarker('items'),
            instance = marker.getMarkerFor(item.sprite.getId(), item.index),
            surface = item.sprite.getSurface(),
            surfaceRect = surface.getRect(),
            xy = surface.getEventXY(e),
            matrix = item.sprite.attr.matrix,
            xAxis = item.series.getXAxis(),
            isEditableX = xAxis && xAxis.getLayout().isContinuous,
            renderer = me.getRenderer(),
            style, changes, params, positionX, positionY;

        if (flipXY) {
            positionY = isRtl ? surfaceRect[2] - xy[0] : xy[0];
        } else {
            positionY = surfaceRect[3] - xy[1];
        }
        if (isEditableX) {
            if (flipXY) {
                positionX = surfaceRect[3] - xy[1];
            } else {
                positionX = xy[0];
            }
        } else {
            positionX = instance.translationX;
        }
        style = {
            translationX: positionX,
            translationY: positionY,
            scalingX: instance.scalingX,
            scalingY: instance.scalingY,
            r: instance.r,
            fillStyle: 'none',
            lineDash: [4, 4],
            zIndex: 100
        };
        Ext.apply(style, me.getStyle());

        me.target = {
            index: item.index,
            yField: item.field,
            yValue: (positionY - matrix.getDY()) / matrix.getYY()
        };
        if (isEditableX) {
            Ext.apply(me.target, {
                xField: item.series.getXField(),
                xValue: (positionX - matrix.getDX()) / matrix.getXX()
            });
        }

        params = [chart, {
            target: me.target,
            style: style,
            item: item
        }];
        changes = Ext.callback(renderer, null, params, 0, chart);
        if (changes) {
            Ext.apply(style, changes);
        }
        item.sprite.putMarker('items', style, 'itemedit');

        me.showTooltip(e, me.target, item);
        surface.renderFrame();
    },

    showTooltip: function (e, target, item) {
        var tooltip = this.getTooltip(),
            config, chart;

        if (tooltip && Ext.toolkit !== 'modern') {
            config = tooltip.config;
            chart = this.getChart();
            Ext.callback(config.renderer, null, [tooltip, item, target, e], 0, chart);
            tooltip.show([
                e.x + config.offsetX,
                e.y + config.offsetY
            ]);
        }
    },

    hideTooltip: function () {
        var tooltip = this.getTooltip();

        if (tooltip && Ext.toolkit !== 'modern') {
            tooltip.hide();
        }
    },

    onDragEnd: function (e) {
        var me = this,
            target = me.target,
            chart = me.getChart(),
            store = chart.getStore(),
            record;

        if (target) {
            record = store.getAt(target.index);
            if (target.yField) {
                record.set(target.yField, target.yValue, {
                    convert: false
                });
            }
            if (target.xField) {
                record.set(target.xField, target.xValue, {
                    convert: false
                });
            }
            if (target.yField || target.xField) {
                me.getChart().onDataChanged();
            }
            me.target = null;
        }

        me.hideTooltip();

        if (me.item) {
            chart.fireEvent('enditemedit', chart, me, me.item, target);
        }
        me.highlight(me.item = null);
    }

});