/**
 * @private
 */
Ext.define('Ext.chart.series.ItemPublisher', {
    extend: 'Ext.event.publisher.Publisher',

    targetType: 'series',

    handledEvents: [
    /**
     * @event itemmousemove
     * Fires when the mouse is moved on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemmousemove',
    /**
     * @event itemmouseup
     * Fires when a mouseup event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemmouseup',
    /**
     * @event itemmousedown
     * Fires when a mousedown event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemmousedown',
    /**
     * @event itemmouseover
     * Fires when the mouse enters a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemmouseover',
    /**
     * @event itemmouseout
     * Fires when the mouse exits a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemmouseout',
    /**
     * @event itemclick
     * Fires when a click event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemclick',
    /**
     * @event itemdoubleclick
     * Fires when a doubleclick event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemdoubleclick',
    /**
     * @event itemtap
     * Fires when a tap event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemtap',
    /**
     * @event itemtapstart
     * Fires when a tapstart event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemtapstart',
    /**
     * @event itemtapend
     * Fires when a tapend event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemtapend',
    /**
     * @event itemtapcancel
     * Fires when a tapcancel event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemtapcancel',
    /**
     * @event itemtaphold
     * Fires when a taphold event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemtaphold',
    /**
     * @event itemdoubletap
     * Fires when a doubletap event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemdoubletap',
    /**
     * @event itemsingletap
     * Fires when a singletap event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemsingletap',
    /**
     * @event itemtouchstart
     * Fires when a touchstart event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemtouchstart',
    /**
     * @event itemtouchmove
     * Fires when a touchmove event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemtouchmove',
    /**
     * @event itemtouchend
     * Fires when a touchend event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemtouchend',
    /**
     * @event itemdragstart
     * Fires when a dragstart event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemdragstart',
    /**
     * @event itemdrag
     * Fires when a drag event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemdrag',
    /**
     * @event itemdragend
     * Fires when a dragend event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemdragend',
    /**
     * @event itempinchstart
     * Fires when a pinchstart event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itempinchstart',
    /**
     * @event itempinch
     * Fires when a pinch event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itempinch',
    /**
     * @event itempinchend
     * Fires when a pinchend event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itempinchend',
    /**
     * @event itemswipe
     * Fires when a swipe event occurs on a series item.
     * @param {Ext.chart.series.Series} series
     * @param {Object} item
     * @param {Event} event
     */
        'itemswipe'
    ],

    delegationRegex: /^item([a-z]+)$/i,

    getSubscribers: function (chartId) {
        var subscribers = this.subscribers;

        if (!subscribers.hasOwnProperty(chartId)) {
            subscribers[chartId] = {};
        }

        return subscribers[chartId];
    },

    subscribe: function (target, eventName) {
        var match = target.match(this.idSelectorRegex),
            dispatcher = this.dispatcher,
            targetType = this.targetType,
            subscribers, series, id;

        if (!match) {
            return false;
        }

        id = match[1];
        series = Ext.ComponentManager.get(id);
        if (!series) {
            return false;
        }

        if (!series.getChart()) {
            dispatcher.addListener(targetType, target, 'chartattached', 'attachChart', this, [series, eventName], 'before');
        } else {
            this.attachChart(series.getChart(), [series, eventName]);
        }

        return true;
    },

    unsubscribe: function (target, eventName, all) {
        var match = target.match(this.idSelectorRegex),
            dispatcher = this.dispatcher,
            targetType = this.targetType,
            subscribers, series, id;

        if (!match) {
            return false;
        }

        id = match[1];
        series = Ext.ComponentManager.get(id);
        if (!series) {
            return false;
        }

        subscribers = this.getSubscribers(target, false);
        if (!subscribers) {
            return false;
        }

        subscribers.$length--;
        if (subscribers.hasOwnProperty(eventName)) {
            subscribers[eventName]--;
            if (series.getChart()) {
                this.detachChart(series.getChart(), [series, eventName, subscribers]);
            }
        }
        return true;
    },

    relayMethod: function (e, sender, args) {
        var chart = args[0],
            eventName = args[1],
            dispatcher = this.dispatcher,
            targetType = this.targetType,
            chartXY = chart.getEventXY(e),
            x = chartXY[0],
            y = chartXY[1],
            subscriber = this.getSubscribers(chart.getId())[eventName],
            i, ln;
        if (subscriber) {
            for (i = 0, ln = subscriber.length; i < ln; i++) {
                var series = subscriber[i],
                    item = series.getItemForPoint(x, y);
                if (item) {
                    dispatcher.doDispatchEvent(targetType, '#' + series.getId(), eventName, [series, item, e]);
                    return;
                }
            }
        }
    },

    detachChart: function (chart, args) {
        var dispatcher = this.dispatcher,
            targetType = this.targetType,
            series = args[0],
            eventName = args[1],
            subscribers = args[2],
            match = eventName.match(this.delegationRegex);
        if (match) {
            var chartEventName = match[1];
            if (subscribers.hasOwnProperty(eventName)) {
                Ext.remove(subscribers[eventName], series);
                if (subscribers[eventName].length === 0) {
                    chart.element.un(chartEventName, "relayMethod", this, [chart, series, eventName]);
                }
            }
            dispatcher.removeListener(targetType, '#' + series.getId(), 'chartdetached', 'detachChart', this, [series, eventName, subscribers], 'after');
        }
    },

    attachChart: function (chart, args) {
        var dispatcher = this.dispatcher,
            targetType = this.targetType,
            series = args[0],
            eventName = args[1],
            subscribers = this.getSubscribers(chart.getId()),
            match = eventName.match(this.delegationRegex);
        if (match) {
            var chartEventName = match[1];
            if (!subscribers.hasOwnProperty(eventName)) {
                subscribers[eventName] = [];
                dispatcher.addListener(targetType, '#' + series.getId(), 'chartdetached', 'detachChart', this, [series, eventName, subscribers], 'after');
                chart.element.on(chartEventName, "relayMethod", this, [chart, eventName]);
            }
            subscribers[eventName].push(series);
            return true;
        } else {
            return false;
        }
    }
}, function () {

});