'use strict';
import H from './../../parts/Globals.js';
import './../../parts/Chart.js';

import U from './../../parts/Utilities.js';
var defined = U.defined,
    objectEach = U.objectEach,
    splat = U.splat;

import './../../parts/SvgRenderer.js';

/**
 * Options for configuring markers for annotations.
 *
 * An example of the arrow marker:
 * <pre>
 * {
 *   arrow: {
 *     id: 'arrow',
 *     tagName: 'marker',
 *     refY: 5,
 *     refX: 5,
 *     markerWidth: 10,
 *     markerHeight: 10,
 *     children: [{
 *       tagName: 'path',
 *       attrs: {
 *         d: 'M 0 0 L 10 5 L 0 10 Z',
 *         strokeWidth: 0
 *       }
 *     }]
 *   }
 * }
 * </pre>
 * @type {Object}
 * @sample highcharts/annotations/custom-markers/
 *         Define a custom marker for annotations
 * @sample highcharts/css/annotations-markers/
 *         Define markers in a styled mode
 * @since 6.0.0
 * @apioption defs
 */
var defaultMarkers = {
    arrow: {
        tagName: 'marker',
        render: false,
        id: 'arrow',
        refY: 5,
        refX: 9,
        markerWidth: 10,
        markerHeight: 10,
        children: [{
            tagName: 'path',
            d: 'M 0 0 L 10 5 L 0 10 Z', // triangle (used as an arrow)
            strokeWidth: 0
        }]
    },

    'reverse-arrow': {
        tagName: 'marker',
        render: false,
        id: 'reverse-arrow',
        refY: 5,
        refX: 1,
        markerWidth: 10,
        markerHeight: 10,
        children: [{
            tagName: 'path',
            // reverse triangle (used as an arrow)
            d: 'M 0 5 L 10 0 L 10 10 Z',
            strokeWidth: 0
        }]
    }
};

H.SVGRenderer.prototype.addMarker = function (id, markerOptions) {
    var options = { id: id };

    var attrs = {
        stroke: markerOptions.color || 'none',
        fill: markerOptions.color || 'rgba(0, 0, 0, 0.75)'
    };

    options.children = markerOptions.children.map(function (child) {
        return H.merge(attrs, child);
    });

    var marker = this.definition(H.merge(true, {
        markerWidth: 20,
        markerHeight: 20,
        refX: 0,
        refY: 0,
        orient: 'auto'
    }, markerOptions, options));

    marker.id = id;

    return marker;
};

var createMarkerSetter = function (markerType) {
    return function (value) {
        this.attr(markerType, 'url(#' + value + ')');
    };
};

/**
 * @private
 * @mixin
 */
var markerMixin = {
    markerEndSetter: createMarkerSetter('marker-end'),
    markerStartSetter: createMarkerSetter('marker-start'),

    /*
     * Set markers.
     *
     * @param {Controllable} item
     */
    setItemMarkers: function (item) {
        var itemOptions = item.options,
            chart = item.chart,
            defs = chart.options.defs,
            fill = itemOptions.fill,
            color = defined(fill) && fill !== 'none' ?
                fill :
                itemOptions.stroke,

            setMarker = function (markerType) {
                var markerId = itemOptions[markerType],
                    def,
                    predefinedMarker,
                    key,
                    marker;

                if (markerId) {
                    for (key in defs) {
                        def = defs[key];

                        if (
                            markerId === def.id && def.tagName === 'marker'
                        ) {
                            predefinedMarker = def;
                            break;
                        }
                    }

                    if (predefinedMarker) {
                        marker = item[markerType] = chart.renderer
                            .addMarker(
                                (itemOptions.id || H.uniqueKey()) + '-' +
                                predefinedMarker.id,
                                H.merge(predefinedMarker, { color: color })
                            );

                        item.attr(markerType, marker.attr('id'));
                    }
                }
            };

        ['markerStart', 'markerEnd'].forEach(setMarker);
    }
};

// In a styled mode definition is implemented
H.SVGRenderer.prototype.definition = function (def) {
    var ren = this;

    function recurse(config, parent) {
        var ret;

        splat(config).forEach(function (item) {
            var node = ren.createElement(item.tagName),
                attr = {};

            // Set attributes
            objectEach(item, function (val, key) {
                if (
                    key !== 'tagName' &&
                    key !== 'children' &&
                    key !== 'textContent'
                ) {
                    attr[key] = val;
                }
            });
            node.attr(attr);

            // Add to the tree
            node.add(parent || ren.defs);

            // Add text content
            if (item.textContent) {
                node.element.appendChild(
                    H.doc.createTextNode(item.textContent)
                );
            }

            // Recurse
            recurse(item.children || [], node);

            ret = node;
        });

        // Return last node added (on top level it's the only one)
        return ret;
    }
    return recurse(def);
};

H.addEvent(H.Chart, 'afterGetContainer', function () {
    this.options.defs = H.merge(defaultMarkers, this.options.defs || {});

    objectEach(this.options.defs, function (def) {
        if (def.tagName === 'marker' && def.render !== false) {
            this.renderer.addMarker(def.id, def);
        }
    }, this);
});

export default markerMixin;
