/****************************************************************
 * Copyright (c) 2006, 2018 IBM Corporation and others.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Red Hat Inc. - ongoing maintenance
 ****************************************************************
 */
package org.eclipse.linuxtools.internal.systemtap.graphing.ui.charts;

import org.eclipse.linuxtools.internal.systemtap.graphing.ui.charts.listeners.BarChartMouseMoveListener;
import org.eclipse.linuxtools.systemtap.graphing.core.adapters.IAdapter;
import org.eclipse.swt.widgets.Composite;
import org.swtchart.IBarSeries;
import org.swtchart.ISeries;
import org.swtchart.ISeries.SeriesType;
import org.swtchart.Range;

/**
 * A {@link Composite} for building a bar chart.
 * @author Qi Liang
 */
public class BarChartBuilder extends AbstractChartWithAxisBuilder {
    public static final String ID = "org.eclipse.linuxtools.systemtap.graphing.ui.charts.barchartbuilder"; //$NON-NLS-1$

    public BarChartBuilder(Composite parent, int style, String title, IAdapter adapter) {
        super(adapter, parent, style, title);
    }

    @Override
    protected double getChartMarginYL() {
        return 0;
    }

    @Override
    protected void createChart() {
        this.chart = new BarChart(this, getStyle());
        applyTitleBoundsListener();
        chartMouseMoveListener = new BarChartMouseMoveListener((BarChart) chart, chart.getPlotArea());
    }

    @Override
    protected ISeries createChartISeries(int i) {
        IBarSeries series = (IBarSeries)chart.getSeriesSet().
            createSeries(SeriesType.BAR, adapter.getLabels()[i+1]);
        series.setBarColor(COLORS[i % COLORS.length]);
        return series;
    }

    @Override
    protected void buildXSeries() {
        Object data[][] = adapter.getData();
        if (data == null || data.length == 0) {
            return;
        }

        int start = 0, len = Math.min(this.maxItems, data.length), leny = data[0].length-1;
        if (this.maxItems < data.length) {
            start = data.length - this.maxItems;
        }

        String[] allValx = new String[len];
        Double[][] allValy = new Double[leny][len];
         // Want to show x-axis if possible, so default max/min is 0.
        double maxY = 0;
        double minY = 0;

        // Read in from the data array all x/y points to plot.
        // If a y-axis value is empty (null), set it to 0.
        // If an x-axis category is empty, ignore the entire category.
        for (int i = 0; i < len; i++) {
            Object label = data[start + i][0];
            if (label != null) {
                allValx[i] = label.toString();
                for (int j = 1; j < leny + 1; j++) {
                    Double val = getDoubleOrNullValue(data[start + i][j]);
                    if (val == null) {
                        val = 0.0;
                    }
                    allValy[j-1][i] = val;
                    maxY = Math.max(val, maxY);
                    minY = Math.min(val, minY);
                }
            }
        }

        // Now create dense arrays of x/y values that exclude null values,
        // and plot those values to the chart.

        String valx[] = new String[len];
        int lenTrim = 0;
        for (int i = 0; i < len; i++) {
            if (allValx[i] != null) {
                valx[lenTrim] = allValx[i];
                lenTrim++;
            }
        }
        String[] valxTrim = new String[lenTrim];
        System.arraycopy(valx, 0, valxTrim, 0, lenTrim);

        ISeries allSeries[] = chart.getSeriesSet().getSeries();
        for (int i = 0; i < leny; i++) {
            ISeries series;
            if (i >= allSeries.length) {
                series = createChartISeries(i);
            } else {
                series = chart.getSeriesSet().getSeries()[i];
            }

            double[] valy = new double[len];
            int lenyTrim = 0;
            for (int j = 0; j < len; j++) {
                if (allValy[i][j] != null) {
                    valy[lenyTrim] = allValy[i][j].doubleValue();
                    lenyTrim++;
                }
            }
            double[] valyTrim = new double[lenyTrim];
            System.arraycopy(valy, 0, valyTrim, 0, lenyTrim);
            series.setYSeries(valyTrim);
        }

        ((BarChart) chart).suspendUpdate(true);
        ((BarChart) chart).setCategorySeries(getUniqueNames(valxTrim));
        applyCategoryRange(valxTrim.length);
        applyRangeY(minY, maxY);
        ((BarChart) chart).suspendUpdate(false);
        chart.redraw();
    }

    /**
     * This updates the visible range of the chart's x-axis.
     */
    private void applyCategoryRange(int numItems) {
        int itemRange = Math.max(1, (int) Math.ceil(numItems * scale)); // The number of items to display
        int lower = (int) Math.round((numItems - itemRange) * scroll);
        chart.getAxisSet().getXAxis(0).setRange(new Range(lower, lower + itemRange - 1));
    }

    @Override
    public void updateDataSet() {
        buildXSeries();
        chartMouseMoveListener.update();
    }
}
