package com.xinyilu.common.utils.jreechart;

import org.apache.commons.collections4.CollectionUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.ui.RectangleInsets;
import org.jfree.chart.ui.TextAnchor;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;

import java.awt.*;
import java.text.NumberFormat;
import java.util.List;

/**
 * @Description JFreeChart 工具类
 * @Author 纪寒
 * @Date 2022-09-22 15:34:31
 * @Version 1.0
 */
public class FreeChartUtil {

    public static String NO_DATA_MSG = "数据加载失败";

    /**
     * 生成主题
     *
     * @param fontName 字体名称（默认为宋体）
     * @return 主题类容
     */
    public static StandardChartTheme createChartTheme(String fontName) {
        StandardChartTheme theme = new StandardChartTheme("unicode") {
            private static final long serialVersionUID = 3646025237593908995L;

            @Override
            public void apply(JFreeChart chart) {
                chart.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING,
                        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
                super.apply(chart);
            }
        };
        theme.setExtraLargeFont(getDefaultFont(Font.PLAIN, 20f));
        theme.setLargeFont(getDefaultFont(Font.PLAIN, 14f));
        theme.setRegularFont(getDefaultFont(Font.PLAIN, 12f));
        theme.setSmallFont(getDefaultFont(Font.PLAIN, 10f));
        return theme;
    }

    /**
     * 获取默认字体
     *
     * @param style 字体风格
     * @param size  字体大小
     * @return 字体样式
     */
    public static Font getDefaultFont(int style, Float size) {
        //如 果不使用Font,中文将显示不出来
        return new Font("新宋体", Font.BOLD, 15);
    }

    /**
     * 创建饼图数据集合
     *
     * @param legendNameList 图例名称列表
     * @param dataList       数据列表
     * @return 饼图数据集合
     */
    public static DefaultPieDataset createDefaultPieDataset(List<String> legendNameList, List<Object> dataList) {
        DefaultPieDataset dataset = new DefaultPieDataset();
        //图例名称列表或数据列表为空，直接返回
        if (CollectionUtils.isEmpty(legendNameList) || CollectionUtils.isEmpty(dataList)) {
            return dataset;
        }
        //循环设置值
        for (int i = 0; i < legendNameList.size() && legendNameList.size() == dataList.size(); i++) {
            String value = dataList.get(i).toString();
            dataset.setValue(legendNameList.get(i), Double.valueOf(value));
        }
        return dataset;
    }

    /**
     * 设置饼状图渲染
     *
     * @param plot 饼图
     */
    public static void setPieRender(Plot plot) {
        plot.setNoDataMessage(NO_DATA_MSG);
        plot.setInsets(new RectangleInsets(10, 10, 5, 10));
        PiePlot piePlot = (PiePlot) plot;
        piePlot.setInsets(new RectangleInsets(0, 0, 0, 0));
        // 圆形
        piePlot.setCircular(true);
        // 简单标签
        piePlot.setLabelGap(0.01);
        piePlot.setInteriorGap(0.05D);
        // 图例形状
        piePlot.setLegendItemShape(new Rectangle(10, 10));
        piePlot.setIgnoreNullValues(true);
        // 去掉标签背景色
        piePlot.setLabelBackgroundPaint(null);
        //去掉图表背景颜色
        piePlot.setBackgroundPaint(null);
        // 去掉阴影
        piePlot.setLabelShadowPaint(null);
        // 去掉边框
        piePlot.setLabelOutlinePaint(null);
        piePlot.setShadowPaint(null);
        // 显示标签数据
        piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}:{2}"));
    }

    /**
     * 创建类别数据集合(柱形图、折线图)
     *
     * @param legendNameList 图例名称列表
     * @param xAxisNameList  x轴名称列表
     * @param dataList       数据列表
     * @return 数据集合
     */
    public static DefaultCategoryDataset createDefaultCategoryDataset(List<String> legendNameList,
                                                                      List<String> xAxisNameList,
                                                                      List<List<Object>> dataList) {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        //图例名称列表、x轴名称列表或数据列表为空
        if (CollectionUtils.isEmpty(legendNameList) || CollectionUtils.isEmpty(xAxisNameList) || CollectionUtils.isEmpty(dataList)) {
            return dataset;
        }
        //循环设置数据
        for (int yAxisIndex = 0; yAxisIndex < legendNameList.size() && legendNameList.size() == dataList.size(); yAxisIndex++) {
            // 横坐标名称
            String legendName = legendNameList.get(yAxisIndex);
            //每行的数据
            List<Object> rowList = dataList.get(yAxisIndex);
            //该组数据不存在或该组数据总数不等于x轴数据数量
            if (CollectionUtils.isEmpty(rowList) || rowList.size() != xAxisNameList.size()) {
                continue;
            }
            //循环为每行数据塞值`
            for (int xAxisIndex = 0; xAxisIndex < rowList.size(); xAxisIndex++) {
                //每行数据
                String rowValue = rowList.get(xAxisIndex).toString();
                dataset.setValue(Double.parseDouble(rowValue), legendName, xAxisNameList.get(xAxisIndex));
            }
        }
        return dataset;
    }

    /**
     * 创建简单柱状图数据集合
     *
     * @param legendNameList 图例名称列表
     * @param xAxisNameList  x轴名称列表
     * @param dataList       数据列表
     * @return 数据集合
     */
    public static DefaultCategoryDataset createSimpleBarDataset(List<String> legendNameList,
                                                                List<String> xAxisNameList,
                                                                List<Object> dataList) {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        //图例名称列表、x轴名称列表或数据列表为空
        if (CollectionUtils.isEmpty(legendNameList) || CollectionUtils.isEmpty(xAxisNameList) || CollectionUtils.isEmpty(dataList)) {
            return dataset;
        }
        //循环设置数据
        for (int i = 0; i < legendNameList.size() && legendNameList.size() == dataList.size(); i++) {
            //横坐标名称
            String legendName = legendNameList.get(i);
            //X轴名称和数据列不相等则跳过
            if (dataList.size() != xAxisNameList.size()) {
                continue;
            }
            //循环为每行数据塞值
            dataset.setValue(Double.parseDouble(dataList.get(i).toString()), legendName, xAxisNameList.get(i));
        }
        return dataset;
    }

    /**
     * 设置柱状图渲染
     *
     * @param plot             布局
     * @param isShowDataLabels 显示数据值标记
     */
    public static void setBarRenderer(CategoryPlot plot, boolean isShowDataLabels) {
        plot.setNoDataMessage(NO_DATA_MSG);
        plot.setInsets(new RectangleInsets(10, 10, 5, 10));
        BarRenderer renderer = (BarRenderer) plot.getRenderer();
        // 设置柱子最大宽度
        renderer.setMaximumBarWidth(0.175);
        // 设置图表背景颜色(透明)
        plot.setBackgroundPaint(null);
        //显示数据值标记
        if (isShowDataLabels) {
            renderer.setDefaultItemLabelsVisible(true);
        }
        //设置默认项目标签样式
        renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());
        //注意：此句很关键，若无此句，那数字的显示会被覆盖，给人数字没有显示出来的问题
        renderer.setDefaultPositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER));
        //设置X轴样式
        setAbscissa(plot);
        //设置Y轴样式
        setOrdinate(plot);
    }

    /**
     * 设置折线图样式
     *
     * @param plot             布局
     * @param isShowDataLabels 是否显示数据标签
     * @param isShapesVisible  是否显示数据点
     */
    public static void setLineRender(CategoryPlot plot, boolean isShowDataLabels, boolean isShapesVisible) {
        plot.setNoDataMessage(NO_DATA_MSG);
        plot.setInsets(new RectangleInsets(10, 10, 0, 10), false);
        LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
        //设置图表背景颜色(透明)
        plot.setBackgroundPaint(null);
        renderer.setDefaultStroke(new BasicStroke(1.5F));
        //显示数据标签
        if (isShowDataLabels) {
            renderer.setDefaultItemLabelsVisible(true);
            renderer.setDefaultItemLabelGenerator(
                    new StandardCategoryItemLabelGenerator(
                            StandardCategoryItemLabelGenerator.DEFAULT_LABEL_FORMAT_STRING, NumberFormat.getInstance()));
            // 位置
            renderer.setDefaultPositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE1, TextAnchor.BOTTOM_CENTER));
        }
        // 数据点绘制形状
        renderer.setDefaultShapesVisible(isShapesVisible);
        //设置X轴样式
        setAbscissa(plot);
        //设置Y轴样式
        setOrdinate(plot);

    }

    /**
     * 设置散点图样式
     *
     * @param plot 样式
     */
    public static void setScatterRender(XYPlot plot) {
        plot.setNoDataMessage(NO_DATA_MSG);
        plot.setInsets(new RectangleInsets(10, 10, 0, 10), false);
        //设置图表背景颜色(透明)
        plot.setBackgroundPaint(null);
        //设置X轴样式
        setAbscissa(plot);
        //设置Y轴样式
        setOrdinate(plot);
    }

    /**
     * 设置类别图表(CategoryPlot) X坐标轴线条颜色和样式
     *
     * @param plot 布局样式
     */
    public static void setAbscissa(CategoryPlot plot) {
        Color lineColor = new Color(31, 121, 170);
        // X坐标轴颜色
        plot.getDomainAxis().setAxisLinePaint(lineColor);
        // X坐标轴标记|竖线颜色
        plot.getDomainAxis().setTickMarkPaint(lineColor);
    }

    /**
     * 设置图表(XYPlot) X坐标轴线条颜色和样式
     *
     * @param plot 布局样式
     */
    public static void setAbscissa(XYPlot plot) {
        Color lineColor = new Color(31, 121, 170);
        // X坐标轴颜色
        plot.getDomainAxis().setAxisLinePaint(lineColor);
        // X坐标轴标记|竖线颜色
        plot.getDomainAxis().setTickMarkPaint(lineColor);
        // x轴网格线条
        plot.setDomainGridlinePaint(new Color(192, 192, 192));
    }

    /**
     * 设置类别图表(CategoryPlot) Y坐标轴线条颜色和样式 同时防止数据无法显示
     *
     * @param plot 布局样式
     */
    public static void setOrdinate(CategoryPlot plot) {
        Color lineColor = new Color(192, 208, 224);
        ValueAxis axis = plot.getRangeAxis();
        // Y坐标轴颜色
        axis.setAxisLinePaint(lineColor);
        // Y坐标轴标记|竖线颜色
        axis.setTickMarkPaint(lineColor);
        // 隐藏Y刻度
        axis.setAxisLineVisible(false);
        axis.setTickMarksVisible(false);
        // Y轴网格线条
        plot.setRangeGridlinePaint(new Color(192, 192, 192));
        plot.setRangeGridlineStroke(new BasicStroke(1));
        // 设置顶部Y坐标轴间距,防止数据无法显示
        plot.getRangeAxis().setUpperMargin(0.1);
        // 设置底部Y坐标轴间距
        plot.getRangeAxis().setLowerMargin(0.1);
    }

    /**
     * 设置图表(XYPlot) Y坐标轴线条颜色和样式 同时防止数据无法显示
     *
     * @param plot 布局样式
     */
    public static void setOrdinate(XYPlot plot) {
        Color lineColor = new Color(192, 208, 224);
        ValueAxis axis = plot.getRangeAxis();
        // Y坐标轴颜色
        axis.setAxisLinePaint(lineColor);
        // Y坐标轴标记|竖线颜色
        axis.setTickMarkPaint(lineColor);
        // 隐藏Y刻度
        axis.setAxisLineVisible(false);
        axis.setTickMarksVisible(false);
        // Y轴网格线条
        plot.setRangeGridlinePaint(new Color(192, 192, 192));
        // 设置顶部Y坐标轴间距,防止数据无法显示
        plot.getRangeAxis().setUpperMargin(0.1);
        // 设置底部Y坐标轴间距
        plot.getRangeAxis().setLowerMargin(0.1);
    }

}
