package usda.weru.weps.reports;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.List;
import net.sf.jasperreports.engine.JRAbstractChartCustomizer;
import net.sf.jasperreports.engine.JRChart;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.LegendItem;
import org.jfree.chart.LegendItemCollection;
import org.jfree.chart.LegendItemSource;
import org.jfree.chart.axis.AxisState;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPosition;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.CategoryTick;
import org.jfree.chart.entity.CategoryLabelEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.data.category.CategoryDataset;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.renderer.category.AreaRenderer;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.text.TextBlock;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;

/**
 *
 * @author Joseph Levin <joelevin@weru.ksu.edu>
 */
public class QuickPlotCustomizer extends JRAbstractChartCustomizer {

    /**
     *
     */
    public static final String PARAMETER_TYPE = "QUICKPLOT_TYPE";

    /**
     * Key for the dataset tied to the left axis of the total graph.
     */
    public static final String PARAMETER_DATA_LEFT = "leftdata";/**
    /**
     * Key for the dataset tied to the right axis of the total graph.
     */
    public static final String PARAMETER_DATA_RIGHT = "rightdata";
    /**
     * Key for the collection of datasets defined for each year; contains those
     * tied to the left axis.
     */
    public static final String COLLECTION_LEFT = "QUICKPLOT_COLLECTION_DATA_LEFT";
    /**
     * Key for the collection of datasets defined for each year; contains those
     * tied to the right axis.
     */
    public static final String COLLECTION_RIGHT = "QUICKPLOT_COLLECTION_DATA_RIGHT";

    /**
     *
     */
    public enum Type {

        /**
         *
         */
        Line("Line", "line"),
        /**
         *
         */
        Bar("Bar", "bar"),
        /**
         *
         */
        BarStacked("Bar Stacked", "barstacked"),
        /**
         *
         */
        Area("Area", "area"),
        /**
         *
         */
        Scatter("Scatter", "scatter");
        private final String title;
        private final String key;

        private Type(String title, String key) {
            this.title = title;
            this.key = key;
        }

        /**
         *
         * @return
         */
        public String title() {
            return title;
        }

        /**
         *
         * @return
         */
        public String key() {
            return key;
        }
    }

    /**
     *
     * @param jfc
     * @param jrc
     */
    @Override
    public void customize(JFreeChart jfc, JRChart jrc) {
        Object set0 = getFieldValue(PARAMETER_DATA_LEFT);
        Object set1 = getFieldValue(PARAMETER_DATA_RIGHT);
        if(set0 == null && set1 == null) return;
        if (set0 instanceof XYDataset) {
            jfc.getXYPlot().setDataset((XYDataset) set0);
        } else if (set0 instanceof CategoryDataset) {
            CategoryAxis axis2 = createAxis();
            axis2.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
            if(set1 instanceof CategoryDataset)
            {
                NumberAxis axis0 = new NumberAxis();
                axis0.setLabel("See legend for units");
                axis0.setRange((Double) getParameterValue("LMin"), (Double) getParameterValue("LMax"));
                NumberAxis axis1 = new NumberAxis();
                axis1.setLabel("See legend for units");
                axis1.setRange((Double) getParameterValue("RMin"), (Double) getParameterValue("RMax"));
                
                //We initialize the left axis to null, because we will override
                //the axis if needed and we want it to be black otherwise.
                jfc.getCategoryPlot().setRangeAxis(0, null);
                //Test to make sure there is data in the left axis;  we don't
                //want to display the axis if there is not.
                if(((CategoryDataset) set0).getColumnCount() != 0)
                {
                    jfc.getCategoryPlot().setRangeAxis(0, axis0);
                    jfc.getCategoryPlot().setDataset(0, (CategoryDataset) set0);
                    jfc.getCategoryPlot().mapDatasetToRangeAxis(0, 0);
                    jfc.getCategoryPlot().mapDatasetToDomainAxis(0, 0);
                    //Override yellow about 15 out.
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(3, Color.BLACK);
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(4, Color.decode("#008080"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(5, Color.decode("#0E2F44"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(6, Color.decode("#3399FF"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(7, Color.decode("#990000"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(8, Color.decode("#40E0D0"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(9, Color.decode("#468499"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(10, Color.decode("#CD7F32"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(11, Color.decode("#C0C0C0"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(12, Color.decode("#CFB53B"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(13, Color.decode("#663399"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(14, Color.decode("#228B22"));
                    jfc.getCategoryPlot().getRenderer().setSeriesPaint(15, Color.decode("#4169E1"));
                }
                if(((CategoryDataset) set0).getRowCount() <
                        ((CategoryDataset) set1).getRowCount())
                {
                    jfc.getCategoryPlot().setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
                }
                //Test to make sure there is data in the right axis;  we don't
                //want to display the axis if there is not.
                if(((CategoryDataset) set1).getColumnCount() != 0)
                {
                    jfc.getCategoryPlot().setRangeAxis(1, axis1);
                    jfc.getCategoryPlot().setDataset(1, (CategoryDataset) set1);
                    jfc.getCategoryPlot().mapDatasetToRangeAxis(1, 1);
                    jfc.getCategoryPlot().setDomainAxis(axis2);
                    jfc.getCategoryPlot().mapDatasetToDomainAxis(1, 0);
                    jfc.getCategoryPlot().setRenderer(1, createRenderer(jfc.getCategoryPlot().getRenderer()));
                }
            }
            else
            {
                jfc.getCategoryPlot().setDomainAxis(axis2);
                jfc.getCategoryPlot().setDataset((CategoryDataset) set0);
            }
            //We want to not display values in the legend if thier title starts with empty.
            LegendItemCollection old = jfc.getCategoryPlot().getLegendItems();
            LegendItemCollection replace = new LegendItemCollection();
            for(int index = 0; index < old.getItemCount(); index ++)
            {
                Object key = old.get(index).getSeriesKey();
                String val = "";
                if(key instanceof String) val = (String) key;
                if(!val.startsWith("empty")) replace.add(old.get(index));
            }
            LegendItemSource source = new LegendItemSource() {
                LegendItemCollection col = new LegendItemCollection();
                { col.addAll(replace); }
                @Override
                public LegendItemCollection getLegendItems() {
                  return col;  
                };
            };
            jfc.removeLegend();
            jfc.addLegend(new LegendTitle(source));
            jfc.getLegend().setBorder(1, 1, 1, 1);
            jfc.getLegend().setPosition(RectangleEdge.BOTTOM);
        }
    }
    
    public CategoryAxis createAxis()
    {
        CategoryAxis axis2 = new CategoryAxis() {
                private static final long serialVersionUID = 1L;

                /**
                 * Draws the category labels and returns the updated axis state.
                 *
                 * @param g2  the graphics device (<code>null</code> not permitted).
                 * @param plotArea  the plot area (<code>null</code> not permitted).
                 * @param dataArea  the area inside the axes (<code>null</code> not
                 *                  permitted).
                 * @param edge  the axis location (<code>null</code> not permitted).
                 * @param state  the axis state (<code>null</code> not permitted).
                 * @param plotState  collects information about the plot (<code>null</code>
                 *                   permitted).
                 *
                 * @return The updated axis state (never <code>null</code>).
                 */
                @Override
                protected AxisState drawCategoryLabels(Graphics2D g2, Rectangle2D plotArea,
                        Rectangle2D dataArea, RectangleEdge edge, AxisState state, PlotRenderingInfo plotState) {

                    if (state == null) {
                        throw new IllegalArgumentException("Null 'state' argument.");
                    }

                    if (isTickLabelsVisible()) {
                        List<?> ticks = refreshTicks(g2, state, plotArea, edge);
                        state.setTicks(ticks);

                        int categoryIndex = 0;
                        Iterator<?> iterator = ticks.iterator();

                        //keep track of the last drawn label
                        Area lastDrawnLabel = null;
                        while (iterator.hasNext()) {

                            CategoryTick tick = (CategoryTick) iterator.next();
                            g2.setFont(getTickLabelFont(tick.getCategory()));
                            g2.setPaint(getTickLabelPaint(tick.getCategory()));

                            CategoryLabelPosition position = this.getCategoryLabelPositions().getLabelPosition(edge);
                            double x0 = 0.0;
                            double x1 = 0.0;
                            double y0 = 0.0;
                            double y1 = 0.0;
                            if (edge == RectangleEdge.TOP) {
                                x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
                                x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
                                y1 = state.getCursor() - this.getCategoryLabelPositionOffset();
                                y0 = y1 - state.getMax();
                            } else if (edge == RectangleEdge.BOTTOM) {
                                x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
                                x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
                                y0 = state.getCursor() + this.getCategoryLabelPositionOffset();
                                y1 = y0 + state.getMax();
                            } else if (edge == RectangleEdge.LEFT) {
                                y0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
                                y1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
                                x1 = state.getCursor() - this.getCategoryLabelPositionOffset();
                                x0 = x1 - state.getMax();
                            } else if (edge == RectangleEdge.RIGHT) {
                                y0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
                                y1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
                                x0 = state.getCursor() + this.getCategoryLabelPositionOffset();
                                x1 = x0 - state.getMax();
                            }
                            Rectangle2D area = new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0));
                            Point2D anchorPoint = RectangleAnchor.coordinates(area, position.getCategoryAnchor());
                            TextBlock block = tick.getLabel();
                            Shape bounds = block.calculateBounds(g2, (float) anchorPoint.getX(),
                                    (float) anchorPoint.getY(), position.getLabelAnchor(),
                                    (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getAngle());
                            Area labelArea = new Area(bounds);

                            if (lastDrawnLabel == null || !lastDrawnLabel.intersects(area)) {
                                block.draw(g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(),
                                        position.getLabelAnchor(), (float) anchorPoint.getX(),
                                        (float) anchorPoint.getY(), position.getAngle());
                                lastDrawnLabel = labelArea;
                            }
                            if (plotState != null && plotState.getOwner() != null) {
                                EntityCollection entities = plotState.getOwner().getEntityCollection();
                                if (entities != null) {
                                    String tooltip = getCategoryLabelToolTip(tick.getCategory());
                                    entities.add(new CategoryLabelEntity(tick.getCategory(), bounds, tooltip, null));
                                }
                            }
                            categoryIndex++;
                        }

                        if (edge.equals(RectangleEdge.TOP)) {
                            double h = state.getMax() + this.getCategoryLabelPositionOffset();
                            state.cursorUp(h);
                        } else if (edge.equals(RectangleEdge.BOTTOM)) {
                            double h = state.getMax() + this.getCategoryLabelPositionOffset();
                            state.cursorDown(h);
                        } else if (edge == RectangleEdge.LEFT) {
                            double w = state.getMax() + this.getCategoryLabelPositionOffset();
                            state.cursorLeft(w);
                        } else if (edge == RectangleEdge.RIGHT) {
                            double w = state.getMax() + this.getCategoryLabelPositionOffset();
                            state.cursorRight(w);
                        }
                    }
                    return state;
                }
            };
        return axis2;
    }
    
    /**
     * Creates a new renderer for the graph based upon the old one.
     * @param input
     * @return 
     */
    private CategoryItemRenderer createRenderer(Object input)
    {
        if(input instanceof AreaRenderer)
        {
            AreaRenderer output = new AreaRenderer();
            output.setSeriesPaint(0, Color.decode("#CD7F32"));
            output.setSeriesPaint(1, Color.decode("#C0C0C0"));
            output.setSeriesPaint(2, Color.decode("#CFB53B"));
            output.setSeriesPaint(3, Color.decode("#663399"));
            output.setSeriesPaint(4, Color.decode("#228B22"));
            output.setSeriesPaint(5, Color.decode("#C87533"));
            output.setSeriesPaint(6, Color.decode("#8E388E"));
            output.setSeriesPaint(7, Color.decode("#C5B358"));
            output.setSeriesPaint(8, Color.decode("#8B4789"));
            output.setSeriesPaint(9, Color.decode("#473C8B"));
            output.setSeriesPaint(10, Color.decode("#698B22"));
            output.setSeriesPaint(11, Color.decode("#E3CF57"));
            output.setSeriesPaint(12, Color.decode("#33A1C9"));
            output.setSeriesPaint(13, Color.decode("#8B7B8B"));
            output.setSeriesPaint(14, Color.decode("#E3A869"));
            output.setSeriesPaint(15, Color.decode("#292421"));
            return output;
        }
        else if(input instanceof StackedBarRenderer)
        {
            StackedBarRenderer output = new StackedBarRenderer();
            output.setShadowVisible(false);
            ((StackedBarRenderer) input).setShadowVisible(false);
            output.setSeriesPaint(0, Color.decode("#CD7F32"));
            output.setSeriesPaint(1, Color.decode("#C0C0C0"));
            output.setSeriesPaint(2, Color.decode("#CFB53B"));
            output.setSeriesPaint(3, Color.decode("#663399"));
            output.setSeriesPaint(4, Color.decode("#228B22"));
            output.setSeriesPaint(5, Color.decode("#C87533"));
            output.setSeriesPaint(6, Color.decode("#8E388E"));
            output.setSeriesPaint(7, Color.decode("#C5B358"));
            output.setSeriesPaint(8, Color.decode("#8B4789"));
            output.setSeriesPaint(9, Color.decode("#473C8B"));
            output.setSeriesPaint(10, Color.decode("#698B22"));
            output.setSeriesPaint(11, Color.decode("#E3CF57"));
            output.setSeriesPaint(12, Color.decode("#33A1C9"));
            output.setSeriesPaint(13, Color.decode("#8B7B8B"));
            output.setSeriesPaint(14, Color.decode("#E3A869"));
            output.setSeriesPaint(15, Color.decode("#292421"));
            return output;
        }
        else if(input instanceof BarRenderer)
        {
            BarRenderer output = new BarRenderer();
            output.setShadowVisible(false);
            ((BarRenderer) input).setShadowVisible(false);
            output.setSeriesPaint(0, Color.decode("#CD7F32"));
            output.setSeriesPaint(1, Color.decode("#C0C0C0"));
            output.setSeriesPaint(2, Color.decode("#CFB53B"));
            output.setSeriesPaint(3, Color.decode("#663399"));
            output.setSeriesPaint(4, Color.decode("#228B22"));
            output.setSeriesPaint(5, Color.decode("#4169E1"));
            output.setSeriesPaint(0, Color.decode("#CD7F32"));
            output.setSeriesPaint(1, Color.decode("#C0C0C0"));
            output.setSeriesPaint(2, Color.decode("#CFB53B"));
            output.setSeriesPaint(3, Color.decode("#663399"));
            output.setSeriesPaint(4, Color.decode("#228B22"));
            output.setSeriesPaint(5, Color.decode("#C87533"));
            output.setSeriesPaint(6, Color.decode("#8E388E"));
            output.setSeriesPaint(7, Color.decode("#C5B358"));
            output.setSeriesPaint(8, Color.decode("#8B4789"));
            output.setSeriesPaint(9, Color.decode("#473C8B"));
            output.setSeriesPaint(10, Color.decode("#698B22"));
            output.setSeriesPaint(11, Color.decode("#E3CF57"));
            output.setSeriesPaint(12, Color.decode("#33A1C9"));
            output.setSeriesPaint(13, Color.decode("#8B7B8B"));
            output.setSeriesPaint(14, Color.decode("#E3A869"));
            output.setSeriesPaint(15, Color.decode("#292421"));
            return output;
        }
        else if(input instanceof LineAndShapeRenderer)
        {
            LineAndShapeRenderer output = new LineAndShapeRenderer();
            output.setBaseLinesVisible(((LineAndShapeRenderer) input).getBaseLinesVisible());
            output.setSeriesPaint(0, Color.decode("#CD7F32"));
            output.setSeriesPaint(1, Color.decode("#C0C0C0"));
            output.setSeriesPaint(2, Color.decode("#CFB53B"));
            output.setSeriesPaint(3, Color.decode("#663399"));
            output.setSeriesPaint(4, Color.decode("#228B22"));
            output.setSeriesPaint(5, Color.decode("#4169E1"));
            output.setSeriesPaint(0, Color.decode("#CD7F32"));
            output.setSeriesPaint(1, Color.decode("#C0C0C0"));
            output.setSeriesPaint(2, Color.decode("#CFB53B"));
            output.setSeriesPaint(3, Color.decode("#663399"));
            output.setSeriesPaint(4, Color.decode("#228B22"));
            output.setSeriesPaint(5, Color.decode("#C87533"));
            output.setSeriesPaint(6, Color.decode("#8E388E"));
            output.setSeriesPaint(7, Color.decode("#C5B358"));
            output.setSeriesPaint(8, Color.decode("#8B4789"));
            output.setSeriesPaint(9, Color.decode("#473C8B"));
            output.setSeriesPaint(10, Color.decode("#698B22"));
            output.setSeriesPaint(11, Color.decode("#E3CF57"));
            output.setSeriesPaint(12, Color.decode("#33A1C9"));
            output.setSeriesPaint(13, Color.decode("#8B7B8B"));
            output.setSeriesPaint(14, Color.decode("#E3A869"));
            output.setSeriesPaint(15, Color.decode("#292421"));
            return output;
        }
        else return null;
    }
}
