/*
 *  ******************************************************************************
 *  *
 *  *
 *  * This program and the accompanying materials are made available under the
 *  * terms of the Apache License, Version 2.0 which is available at
 *  * https://www.apache.org/licenses/LICENSE-2.0.
 *  *
 *  *  See the NOTICE file distributed with this work for additional
 *  *  information regarding copyright ownership.
 *  * Unless required by applicable law or agreed to in writing, software
 *  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  * License for the specific language governing permissions and limitations
 *  * under the License.
 *  *
 *  * SPDX-License-Identifier: Apache-2.0
 *  *****************************************************************************
 */

class ChartLine extends Chart implements Renderable {

    private xData: number[][];
    private yData: number[][];
    private seriesNames: string[];

    constructor(jsonStr: string){
        super(ComponentType.ChartLine, jsonStr);

        var json = JSON.parse(jsonStr);
        if(!json["componentType"]) json = json[ComponentType[ComponentType.ChartLine]];

        this.xData = json['x'];
        this.yData = json['y'];
        this.seriesNames = json['seriesNames'];
    }

    render = (appendToObject: JQuery) => {

        var nSeries: number = (!this.xData ? 0 : this.xData.length);
        var s: StyleChart = this.getStyle();
        var margin: Margin = Style.getMargins(s);

        // Set the ranges
        var xScale: d3.scale.Linear<number,number> = d3.scale.linear().range([0, margin.widthExMargins]);
        var yScale: d3.scale.Linear<number,number> = d3.scale.linear().range([margin.heightExMargins, 0]);

        // Define the axes
        var xAxis = d3.svg.axis().scale(xScale)
            .orient("bottom").ticks(5);
        if(this.gridVerticalStrokeWidth != null && this.gridVerticalStrokeWidth > 0){
            xAxis.innerTickSize(-margin.heightExMargins);     //used as grid line
        }


        var yAxis = d3.svg.axis().scale(yScale)
            .orient("left").ticks(5);
        if(this.gridHorizontalStrokeWidth != null && this.gridHorizontalStrokeWidth > 0){
            yAxis.innerTickSize(-margin.widthExMargins);      //used as grid line
        }

        if(this.suppressAxisHorizontal === true) xAxis.tickValues([]);

        if(this.suppressAxisVertical === true) yAxis.tickValues([]);

        // Define the line
        var valueline = d3.svg.line()
            .x(function (d: any) {
                return xScale(d.xPos);
            })
            .y(function (d: any) {
                return yScale(d.yPos);
            });

        // Adds the svg canvas
        //TODO don't hardcode these colors/attributes...
        var svg = d3.select("#" + appendToObject.attr("id"))
            .append("svg")
            .style("stroke-width", ( s && s.getStrokeWidth() ? s.getStrokeWidth() : ChartConstants.DEFAULT_CHART_STROKE_WIDTH))
            .style("fill", "none")
            .attr("width", s.getWidth())
            .attr("height", s.getHeight())
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        // Scale the range of the chart
        var xMin: number;
        var xMax: number;
        var yMin: number;
        var yMax: number;
        if(this.setXMin != null) xMin = this.setXMin;
        else xMin = (this.xData ? TSUtils.min(this.xData) : 0);
        if(this.setXMax != null) xMax = this.setXMax;
        else xMax = (this.xData ? TSUtils.max(this.xData) : 1);
        if(this.setYMin != null) yMin = this.setYMin;
        else yMin = (this.yData ? TSUtils.min(this.yData) : 0);
        if(this.setYMax != null) yMax = this.setYMax;
        else yMax = (this.yData ? TSUtils.max(this.yData) : 1);

        xScale.domain([xMin, xMax]);
        yScale.domain([yMin, yMax]);

        // Add the valueline path.
        var defaultColor: Ordinal<string,string> = d3.scale.category10();
        for (var i = 0; i < nSeries; i++) {
            var xVals: number[] = this.xData[i];
            var yVals: number[] = this.yData[i];

            var data: any[] = xVals.map(function (d, i) {
                return {'xPos': xVals[i], 'yPos': yVals[i]};
            });

            svg.append("path")
                .attr("class", "line")
                .style("stroke", (s && s.getSeriesColor(i) ? s.getSeriesColor(i) : defaultColor(String(i))))
                .attr("d", valueline(data));
        }

        // Add the X Axis
        var xAxisNode = svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + margin.heightExMargins + ")")
            .style("stroke","#000")
            .style("stroke-width", (s != null && s.getAxisStrokeWidth() != null ? s.getAxisStrokeWidth() : ChartConstants.DEFAULT_AXIS_STROKE_WIDTH))
            .style("fill","none")
            .call(xAxis);
        xAxisNode.selectAll('text').style("stroke-width",0).style("fill","#000000");

        if(this.gridVerticalStrokeWidth != null) xAxisNode.selectAll('.axis line').style({'stroke-width': this.gridVerticalStrokeWidth});

        // Add the Y Axis
        var yAxisNode = svg.append("g")
            .attr("class", "y axis")
            .style("stroke","#000")
            .style("stroke-width", (s != null && s.getAxisStrokeWidth() != null ? s.getAxisStrokeWidth() : ChartConstants.DEFAULT_AXIS_STROKE_WIDTH))
            .style("fill","none")
            .call(yAxis);
        yAxisNode.selectAll('text').style("stroke-width",0).style("fill","#000000");

        if(this.gridHorizontalStrokeWidth != null) yAxisNode.selectAll('.axis line').style({'stroke-width': this.gridHorizontalStrokeWidth});

        //Add legend (if present)
        if (this.seriesNames && this.showLegend === true) {
            var legendSpace = margin.widthExMargins / i;
            for (var i = 0; i < nSeries; i++) {
                var values = this.xData[i];
                var yValues = this.yData[i];
                var lastX = values[values.length - 1];
                var lastY = yValues[yValues.length - 1];
                var toDisplay = this.seriesNames[i];
                svg.append("text")
                    .attr("x", (legendSpace / 2) + i * legendSpace) // spacing
                    .attr("y", margin.heightExMargins + (margin.bottom / 2) + 5)
                    .attr("class", "legend")    // style the legend
                    .style("fill", (s && s.getSeriesColor(i) ? s.getSeriesColor(i) : defaultColor(String(i))))
                    .text(toDisplay);
            }
        }

        //Add title (if present)
        if (this.title) {
            var titleStyle: StyleText;
            if(this.style) titleStyle = this.style.getTitleStyle();
            Chart.appendTitle(svg, this.title, margin, titleStyle);
        }
    }
}