# Source: components/data_layer/arcs.js

    import * as d3 from 'd3';

    import BaseDataLayer from './base';
    import {merge} from '../../helpers/layouts';
    import {applyStyles} from '../../helpers/common';

    /**
     * @memberof module:LocusZoom_DataLayers~arcs
     */
    const default_layout = {
        color: 'seagreen',
        hitarea_width: '10px',
        style: {
            fill: 'none',
            'stroke-width': '1px',
            'stroke-opacity': '100%',
        },
        tooltip_positioning: 'top',
    };

    /**
     * Arc Data Layer
     * Implements a data layer that will render chromatin accessibility tracks.
     * This layer draws arcs (one per datapoint) that connect two endpoints (x.field1 and x.field2) by means of an arc,
     *  with a height determined by y.field.
     * @alias module:LocusZoom_DataLayers~arcs
     * @see {@link module:LocusZoom_DataLayers~BaseDataLayer} for additional layout options
     */
    class Arcs extends BaseDataLayer {
        /**
         * @param {String|module:LocusZoom_DataLayers~ScalableParameter[]} [layout.color='seagreen'] Specify how to choose the stroke color for each arc
         * @param {number} [layout.hitarea_width='10px'] The width (in pixels) of hitareas. Arcs are only as wide as the stroke,
         *   so a hit area of 5px on each side can make it much easier to select an item for a tooltip.
         * @param {string} [layout.style.fill='none'] The fill color under the area of the arc
         * @param {string} [layout.style.stroke-width='1px']
         * @param {string} [layout.style.stroke_opacity='100%']
         * @param {'horizontal'|'vertical'|'top'|'bottom'|'left'|'right'} [layout.tooltip_positioning='top'] Where to draw the tooltip relative to the datum.
         * @param {string} [layout.x_axis.field1] The field to use for one end of the arc; creates a point at (x1, 0)
         * @param {string} [layout.x_axis.field2] The field to use for the other end of the arc; creates a point at (x2, 0)
         * @param {string} [layout.y_axis.field] The height at the midpoint of the arc, (xmid, y)
         */
        constructor(layout) {
            layout = merge(layout, default_layout);
            super(...arguments);
        }

        // Implement the main render function
        render() {
            const self = this;
            const layout = self.layout;
            const x_scale = self.parent['x_scale'];
            const y_scale = self.parent[`y${layout.y_axis.axis}_scale`];

            // Apply filters to only render a specified set of points
            const track_data = this._applyFilters();

            // Helper: Each individual data point describes a path composed of 3 points, with a spline to smooth the line
            function _make_line(d) {
                const x1 = d[layout.x_axis.field1];
                const x2 = d[layout.x_axis.field2];
                const xmid = (x1 + x2) / 2;
                const coords = [
                    [x_scale(x1), y_scale(0)],
                    [x_scale(xmid), y_scale(d[layout.y_axis.field])],
                    [x_scale(x2), y_scale(0)],
                ];
                // Smoothing options: https://bl.ocks.org/emmasaunders/f7178ed715a601c5b2c458a2c7093f78
                const line = d3.line()
                    .x((d) => d[0])
                    .y((d) => d[1])
                    .curve(d3.curveNatural);
                return line(coords);
            }

            // Draw real lines, and also invisible hitareas for easier mouse events
            const hitareas = this.svg.group
                .selectAll('path.lz-data_layer-arcs-hitarea')
                .data(track_data, (d) => this.getElementId(d));

            const selection = this.svg.group
                .selectAll('path.lz-data_layer-arcs')
                .data(track_data, (d) => this.getElementId(d));

            this.svg.group
                .call(applyStyles, layout.style);

            hitareas
                .enter()
                .append('path')
                .attr('class', 'lz-data_layer-arcs-hitarea')
                .merge(hitareas)
                .attr('id', (d) => this.getElementId(d))
                .style('fill', 'none')
                .style('stroke-width', layout.hitarea_width)
                .style('stroke-opacity', 0)
                .style('stroke', 'transparent')
                .attr('d', (d) => _make_line(d));

            // Add new points as necessary
            selection
                .enter()
                .append('path')
                .attr('class', 'lz-data_layer-arcs')
                .merge(selection)
                .attr('id', (d) => this.getElementId(d))
                .attr('stroke', (d, i) => this.resolveScalableParameter(this.layout.color, d, i))
                .attr('d', (d, i) => _make_line(d));

            // Remove old elements as needed
            selection.exit()
                .remove();

            hitareas.exit()
                .remove();

            // Apply mouse behaviors to arcs
            this.svg.group
                .call(this.applyBehaviors.bind(this));

            return this;
        }

        _getTooltipPosition(tooltip) {
            // Center the tooltip arrow at the apex of the arc. Sometimes, only part of an arc shows on the screen, so we
            //  clean up these values to ensure that the tooltip will appear within the window.
            const panel = this.parent;
            const layout = this.layout;

            const x1 = tooltip.data[layout.x_axis.field1];
            const x2 = tooltip.data[layout.x_axis.field2];

            const y_scale = panel[`y${layout.y_axis.axis}_scale`];

            return {
                x_min: panel.x_scale(Math.min(x1, x2)),
                x_max: panel.x_scale(Math.max(x1, x2)),
                y_min: y_scale(tooltip.data[layout.y_axis.field]),
                y_max: y_scale(0),
            };
        }

    }

    export {Arcs as default};