"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const highcharts_1 = require("@internal/highcharts");
const shared_1 = require("../../common/charts/shared");
const event_utils_1 = require("../../common/event-utils");
const tooltip_marko_1 = __importDefault(require("./tooltip.marko"));
const pointSize = 6; // controls the size of the plot point markers on lines
class LineChart {
    onCreate() {
        this.axisTicksLength = -1;
    }
    handleError(err) {
        this.emit("load-error", err);
    }
    handleSuccess() {
        this._setupChart();
    }
    onMount() {
        (0, highcharts_1.load)()
            .then(() => {
            this.handleSuccess();
        })
            .catch((e) => {
            this.handleError(e);
        });
    }
    onInput(input) {
        this.input = this.input || input;
        // if chartRef does not exist do not try to run setupCharts as it may be server side and highcharts only works on the client side
        if (this.chartRef && this.chartRef.destroy) {
            this.chartRef.destroy();
            this._setupChart();
        }
    }
    getContainerId() {
        return `ebay-line-graph-${this.id}`;
    }
    _setupChart() {
        const colors = [
            // configure the array of colors to use for each series
            shared_1.lineChartPrimaryColor,
            shared_1.lineChartSecondaryColor,
            shared_1.lineChartTertiaryColor,
            shared_1.lineChartQueternaryColor,
            shared_1.lineChartQuinaryColor,
        ];
        // check if a single series was passed in for series and if so add it to a new array
        const series = Array.isArray(this.input.series)
            ? this.input.series
            : [this.input.series];
        if (this.input.trend) {
            // if the trend property exist check value and adjust the first color
            const trend = typeof this.input.trend === "string" &&
                this.input.trend.toLowerCase(); // if trend of type string force to lowercase
            const isPositive = series[0].data[0].y <
                series[0].data[series[0].data.length - 1].y; // auto trend positive check between first and last data values of the single series
            if (trend === "positive" || // if "positive" is passed in for the trend property
                (trend !== "negative" && trend !== "neutral" && isPositive) // if check if trend is does not equal negative or neutral, and if so use the auto positive calculation
            ) {
                colors[0] = shared_1.trendPositiveColor; // set the color to the positive trend color
            }
            else if (trend === "negative" ||
                (trend !== "neutral" && !isPositive)) {
                // if the trend property equals negative, or trend does not equal neutral and isPositive is false
                colors[0] = shared_1.trendNegativeColor; // set the negative trend color
            }
        }
        // configure the symbol used for each series markers
        series.forEach((s, i) => {
            s.marker = {
                symbol: this.getSymbol(i),
            };
        });
        const chart = this.getChartConfig();
        const xAxis = this.getXAxisConfig();
        const yAxis = this.getYAxisConfig(series);
        const legend = this.getLegendConfig();
        const tooltip = this.getTooltipConfig();
        const plotOptions = this.getPlotOptionsConfig(series);
        const title = {
            text: this.input.title,
            align: "left",
            useHTML: true,
            style: {
                fontSize: "18px",
                fontWeight: "700",
            },
        };
        const config = {
            title,
            chart,
            colors,
            xAxis,
            yAxis,
            legend,
            tooltip,
            plotOptions,
            series, // pass in the configured series array
            credits: {
                enabled: false, // hide the highcharts label and link in the bottom right of chart
            },
        };
        // initialize and keep reference to chart
        // eslint-disable-next-line no-undef,new-cap
        this.chartRef = Highcharts.chart(this.getContainerId(), config);
        // call update markers after the initial render to determine which markers to display if plotPoints is set to true
        this.updateMarkers();
    }
    getSymbol(index) {
        let s;
        switch (index) {
            case 1:
                s = "square";
                break;
            case 2:
                s = "triangle";
                break;
            case 3:
                s = "triangle-down";
                break;
            case 4:
                s = "diamond";
                break;
            default: // 0 index
                s = "circle";
                break;
        }
        return s;
    }
    getChartConfig() {
        return {
            type: "line",
            backgroundColor: shared_1.backgroundColor,
            style: {
                fontFamily: shared_1.chartFontFamily,
            },
            events: {
                // on chart redraw trigger updateMarkers to look for changes in xAxis tick marks and adjust series markers visibility accordingly
                redraw: () => this.updateMarkers(),
            },
        };
    }
    getXAxisConfig() {
        return {
            // currently setup to support epoch time values for xAxisLabels.
            // It is possible to set custom non datetime xAxisLabels but will need changes to this component
            type: "datetime",
            labels: {
                // input.xAxisLabelFormat allows overriding the default short month / day label
                // refer to https://api.highcharts.com/class-reference/Highcharts.Time#dateFormat to customize
                format: this.input.xAxisLabelFormat || "{value:%b %e}",
                align: "center",
                style: {
                    color: shared_1.labelsColor, // setting label colors
                },
            },
            tickWidth: 0, // hide the vertical tick on xAxis labels
            tickPositioner: this.input.xAxisPositioner, // optional input to allow configuring the position of xAxis tick marks
        };
    }
    getYAxisConfig(series) {
        const component = this; // component reference used in formatter functions that don't have the same scope
        let yLabelsItterator = 0; // used when yAxisLabels array is provided in input
        let maxVal = 0; // use to determine the highest yAxis value
        // configure the symbol used for each series markers
        for (const seriesItem of series) {
            for (const seriesItemData of seriesItem.data) {
                maxVal = Math.max(seriesItemData.y, maxVal);
            }
        }
        return {
            gridLineColor: shared_1.gridColor, // sets the horizontal grid line colors
            opposite: true, // moves yAxis labels to the right side of the chart
            labels: {
                // if yAxisLabels are not passed in display the standard label
                format: this.input.yAxisLabels ? undefined : "${text}",
                // if yAxisLabels array is passed in this formatter function is needed to
                // return the proper label for each yAxis tick mark
                formatter: this.input.yAxisLabels
                    ? function () {
                        if (this.isFirst) {
                            yLabelsItterator = -1;
                        }
                        yLabelsItterator = yLabelsItterator + 1;
                        return component.input.yAxisLabels[yLabelsItterator];
                    }
                    : undefined,
                style: {
                    color: shared_1.labelsColor, // setting label colors
                },
            },
            max: maxVal,
            title: {
                enabled: false, // hide the axis label next to the axis
            },
            offset: 0, // set to zero for no offset refer to https://api.highcharts.com/highcharts/yAxis.offset
            // passed in function for yAxisPositioner refer to https://api.highcharts.com/highcharts/yAxis.tickPositioner for use
            tickPositioner: this.input.yAxisPositioner,
        };
    }
    getLegendConfig() {
        return {
            // if only a single series is provided do not display the legend
            enabled: Array.isArray(this.input.series) &&
                this.input.series.length > 1,
            symbolRadius: 6, // corner radius on legend identifiers svg element
            symbolWidth: 12, // setting the width of the legend identifiers svg element
            symbolHeight: 12, // setting the height of the legend identifiers svg element
            itemStyle: {
                color: shared_1.legendColor, // set the color of the text in the legend
            },
            itemHiddenStyle: {
                color: shared_1.legendInactiveColor, // set legend text color when legend item has been clicked and hidden
            },
            itemHoverStyle: {
                color: shared_1.legendHoverColor, // set legend text color on hover of legend element
            },
        };
    }
    getTooltipConfig() {
        const component = this; // component reference used in formatter functions that don't have the same scope
        return {
            formatter: function () {
                // refer to https://api.highcharts.com/class-reference/Highcharts.Time#dateFormat for dateFormat variables
                return tooltip_marko_1.default.renderToString({
                    // eslint-disable-next-line no-undef,new-cap
                    date: Highcharts.dateFormat("%b %e, %Y", this.points[0].x, false),
                    points: this.points,
                    seriesLength: Array.isArray(component.input.series) &&
                        component.input.series.length > 1,
                });
            },
            useHTML: true, // allows defining html to format tooltip content
            backgroundColor: shared_1.tooltipBackgroundColor, // sets tooltip background color
            borderWidth: 0, // hide the default border stroke
            borderRadius: 10, // set the border radius of the tooltip
            outside: component.input.renderTooltipOutside !== false, // used to render the tooltip outside of the main SVG element
            shadow: false, // hide the default shadow as it conflicts with designs
            crosshair: {
                dashStyle: "Solid", // makes a yaxis cross hair appear over the hovered xAxis data points
            },
            shared: true, // shared means that if there are multipe series passed in there will be a single tooltip element per xAxis point
            style: {
                filter: shared_1.tooltipShadows, // sets tooltip shadows
                fontSize: "12px",
            },
        };
    }
    getPlotOptionsConfig(series) {
        const mouseOut = (0, event_utils_1.debounce)(() => this.handleMouseOut(), 80);
        const mouseOver = (0, event_utils_1.debounce)((e) => this.handleMouseOver(e), 85); // 85ms delay for debounce so it doesn't colide with mouseOut debounce calls
        return {
            line: {
                events: {
                    // assign mouse events to point hovers
                    mouseOut,
                },
            },
            series: {
                description: this.input.description, // set the description that was passed in
                lineWidth: 3, // sets the line width for series lines
                // sets the starting point of the xAxis to the first data point
                // if not set the auto resizing of the xAxis will often leave a gap in data on the left hand side
                pointStart: series[0].data[0].x,
                point: {
                    // assign mouse events to point hovers
                    events: {
                        mouseOver,
                        mouseOut,
                    },
                },
            },
        };
    }
    handleMouseOut() {
        // this function is debounced to improve performance
        this.chartRef.series.forEach((s) => {
            s.data.forEach((data) => {
                // check if hover is on the xAxis (onTick) for each item,
                // and if they have a className remove and disable the marker
                if (!data.onTick && data.className !== null) {
                    data.update({
                        className: undefined, // nullify className if not active
                        marker: {
                            enabled: false, // disable marker if not active
                        },
                    }, false, // disable auto redraw
                    false);
                }
                else if (data.onTick && data.className === null) {
                    data.update({
                        className: "ebay-line-graph__marker--visible", // set classname
                        onTick: data.onTick, // sets the onTick flag to keep track of the points enabled status for mouse events
                        marker: {
                            enabled: true, // set marker enabled
                            radius: pointSize, // set the size of marker
                            lineColor: shared_1.backgroundColor, // set border color of hover markers
                            lineWidth: 2, // sets the border line width of the marker symbol
                        },
                    }, false, // disable auto redraw
                    false);
                }
            });
        });
        this.chartRef.redraw(); // trigger redraw after all points have been updated
    }
    handleMouseOver(e) {
        // this function is debounced to improve performance
        this.chartRef.series.forEach((s) => {
            s.data.forEach((data) => {
                // if active xAxis hover position matches the data point x update the marker to display
                if (data.x === e.target.x) {
                    data.update({
                        className: "ebay-line-graph__marker--visible", // sets the classname
                        onTick: data.onTick, // sets the onTick flag to keep track of the points enabled status for mouse events
                        marker: {
                            enabled: true, // set marker enabled
                            radius: pointSize, // set the size of marker
                            lineColor: shared_1.backgroundColor, // set border color of hover markers
                            lineWidth: 2, // sets the border line width of the marker symbol
                        },
                    }, false, // disable auto redraw
                    false);
                }
                else if (!data.onTick && data.className !== null) {
                    data.update({
                        className: undefined, // nullify className if not active
                        onTick: data.onTick, // sets the onTick flag to keep track of the points enabled status for mouse events
                        marker: {
                            enabled: false, // disable marker
                        },
                    }, false, // disable auto redraw
                    false);
                }
            });
        });
        this.chartRef.redraw(); // trigger redraw after all points have been updated
    }
    updateMarkers(e) {
        if (this.input.plotPoints) {
            // ticks is an object with the xaxis date values as their keys
            // setting tickValues to the keys of the ticks object and parsing into an int for data matching of xValues in series below
            this.tickValues = Object.keys(this.chartRef.axes[0].ticks).map((value) => parseInt(value, 10));
            // this checks if the resize has adjust the number of xAxis tick marks, and if so make updates
            if (this.axisTicksLength !== this.tickValues.length || e === true) {
                // update the axisTicksLenth variable used for checks in the updateMarkers calls
                this.axisTicksLength = this.tickValues.length;
                // loops through each series if a className exist remove and hide the marker
                this.chartRef.series.forEach((series) => {
                    // looping through each series data array
                    series.data.forEach((data) => {
                        if (data.className !== null) {
                            data.update({
                                className: undefined, // removing className used to help keep track of active markers
                                onTick: false, // sets the onTick flag to keep track of the points enabled status for mouse events
                                marker: {
                                    enabled: false, // disable the marker
                                },
                            }, false, // disable auto redraw
                            false);
                        }
                    });
                });
                // loop through each series again and update markers that line up to xAxis tick marks
                this.chartRef.series.forEach((series) => {
                    // loop through each searies data objects
                    series.data.forEach((data) => {
                        // loop through the tickValues that come from the x axis ticks and are epoch time stamps
                        this.tickValues.forEach((tick) => {
                            // if the current point x value matches the tickValue or the updateMarkers event exist from the redraw event
                            if (tick === data.x || data.x === e) {
                                if (data.className === null) {
                                    data.update({
                                        className: "ebay-line-graph__marker--visible", // add the ebay-line-graph__marker--visible class to boost it's visibility
                                        onTick: true, // sets the onTick flag to keep track of the points enabled status for mouse events
                                        marker: {
                                            enabled: true, // set marker enabled
                                            radius: pointSize, // set the size of the marker
                                            lineColor: shared_1.backgroundColor, // set the border color of the hover markers
                                            lineWidth: 2, // set the border width of the hover markers
                                        },
                                    }, false, // disable auto redraw
                                    false);
                                }
                            }
                        });
                    });
                });
                this.chartRef.redraw(); // trigger redraw after all points have been updated
            }
        }
    }
    onDestroy() {
        this.chartRef.destroy();
    }
}
module.exports = LineChart;
