package hudson.util;

import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.labels.CategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.AreaRendererEndType;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.StackedAreaRenderer;
import org.jfree.chart.urls.CategoryURLGenerator;
import org.jfree.data.category.CategoryDataset;
import org.jfree.ui.RectangleEdge;

import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Paint;
import java.awt.geom.Rectangle2D;

/**
 * Modified {@link StackedAreaRenderer}.
 *
 * <ol>
 * <li>Built-in support for {@link #generateToolTip(CategoryDataset, int, int) tooltip}
 *     and {@link #generateURL(CategoryDataset, int, int) hyperlinks} for clickable map.
 * <li>Clickable map hit test is modified so that the entire area is clickable,
 *     not just the small area around each data point.
 * <li>Rendering algorithm is modified so that
 *     {@link #getItemPaint(int, int) different color on the same data series}
 *     will look more natural.
 * </ol>
 *
 * @author Kohsuke Kawaguchi
*/
public class StackedAreaRenderer2 extends StackedAreaRenderer
    implements CategoryToolTipGenerator, CategoryURLGenerator {

    public StackedAreaRenderer2() {
        setEndType(AreaRendererEndType.TRUNCATE);
        setItemURLGenerator(this);
        setToolTipGenerator(this);
    }

    /**
     * Override this method to specify the hyperlink target of the given data point.
     */
    public String generateURL(CategoryDataset dataset, int row, int column) {
        return null;
    }

    /**
     * Override this method to specify the tool tip text of the given data point.
     */
    public String generateToolTip(CategoryDataset dataset, int row, int column) {
        return null;
    }

    /**
     * Override this method to specify the color to draw the given area.
     */
    public Paint getItemPaint(int row, int column) {
        return super.getItemPaint(row, column);
    }

    // Mostly copied from the base class.
    @Override
    public void drawItem(Graphics2D g2,
                         CategoryItemRendererState state,
                         Rectangle2D dataArea,
                         CategoryPlot plot,
                         CategoryAxis domainAxis,
                         ValueAxis rangeAxis,
                         CategoryDataset dataset,
                         int row,
                         int column,
                         int pass) {

        // plot non-null values...
        Number dataValue = dataset.getValue(row, column);
        if (dataValue == null) {
            return;
        }

        double value = dataValue.doubleValue();

        // leave the y values (y1, y0) untranslated as it is going to be be
        // stacked up later by previous series values, after this it will be
        // translated.
        double xx1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
                dataArea, plot.getDomainAxisEdge());

        double previousHeightx1 = getPreviousHeight(dataset, row, column);
        double y1 = value + previousHeightx1;
        RectangleEdge location = plot.getRangeAxisEdge();
        double yy1 = rangeAxis.valueToJava2D(y1, dataArea, location);

        g2.setPaint(getItemPaint(row, column));
        g2.setStroke(getItemStroke(row, column));

        // add an item entity, if this information is being collected
        EntityCollection entities = state.getEntityCollection();

        // in column zero, the only job to do is draw any visible item labels
        // and this is done in the second pass...
        if (column == 0) {
            if (pass == 1) {
                // draw item labels, if visible
                if (isItemLabelVisible(row, column)) {
                    drawItemLabel(g2, plot.getOrientation(), dataset, row, column,
                            xx1, yy1, (y1 < 0.0));
                }
            }
        } else {
            Number previousValue = dataset.getValue(row, column - 1);
            if (previousValue != null) {

                double xx0 = domainAxis.getCategoryMiddle(column - 1,
                        getColumnCount(), dataArea, plot.getDomainAxisEdge());
                double y0 = previousValue.doubleValue();


                // Get the previous height, but this will be different for both
                // y0 and y1 as the previous series values could differ.
                double previousHeightx0 = getPreviousHeight(dataset, row,
                        column - 1);

                // Now stack the current y values on top of the previous values.
                y0 += previousHeightx0;

                // Now translate the previous heights
                double previousHeightxx0 = rangeAxis.valueToJava2D(
                        previousHeightx0, dataArea, location);
                double previousHeightxx1 = rangeAxis.valueToJava2D(
                        previousHeightx1, dataArea, location);

                // Now translate the current y values.
                double yy0 = rangeAxis.valueToJava2D(y0, dataArea, location);

                if (pass == 0) {
                    // left half
                    Polygon p = new Polygon();
                    p.addPoint((int) xx0, (int) yy0);
                    p.addPoint((int) (xx0+xx1)/2, (int) (yy0+yy1)/2);
                    p.addPoint((int) (xx0+xx1)/2, (int) (previousHeightxx0+previousHeightxx1)/2);
                    p.addPoint((int) xx0, (int) previousHeightxx0);

                    g2.setPaint(getItemPaint(row, column-1));
                    g2.setStroke(getItemStroke(row, column-1));
                    g2.fill(p);

                    if (entities != null)
                        addItemEntity(entities, dataset, row, column-1, p);

                    // right half
                    p = new Polygon();
                    p.addPoint((int) xx1, (int) yy1);
                    p.addPoint((int) (xx0+xx1)/2, (int) (yy0+yy1)/2);
                    p.addPoint((int) (xx0+xx1)/2, (int) (previousHeightxx0+previousHeightxx1)/2);
                    p.addPoint((int) xx1, (int) previousHeightxx1);

                    g2.setPaint(getItemPaint(row, column));
                    g2.setStroke(getItemStroke(row, column));
                    g2.fill(p);

                    if (entities != null)
                        addItemEntity(entities, dataset, row, column, p);
                } else {
                    if (isItemLabelVisible(row, column)) {
                        drawItemLabel(g2, plot.getOrientation(), dataset, row,
                                column, xx1, yy1, (y1 < 0.0));
                    }
                }
            }
        }
    }
}
