package thridparty;

import java.util.HashMap;
import java.util.Map;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextFlow;
import mara.mybox.fxml.FxmlControl;
import mara.mybox.fxml.FxmlControl.ChartCoordinate;
import mara.mybox.fxml.FxmlControl.LabelType;
import mara.mybox.fxml.Logarithmic10Coordinate;
import mara.mybox.fxml.LogarithmicECoordinate;
import mara.mybox.fxml.SquareRootCoordinate;
import mara.mybox.tools.StringTools;
import static mara.mybox.value.AppVariables.logger;

/**
 * @Author Roland
 * https://stackoverflow.com/questions/34286062/how-to-clear-text-added-in-a-javafx-barchart/41494789#41494789
 *
 * #### Changed a little by Mara
 */
public class LabeledHorizontalBarChart<X, Y> extends BarChart<X, Y> {

    protected Map<Node, TextFlow> nodeMap = new HashMap<>();
    protected boolean intValue;
    protected String cssFile;
    protected LabelType labelType;
    protected ChartCoordinate chartCoordinate;
    protected int textSize;

    public static LabeledHorizontalBarChart create(boolean displayCategoryAxis, ChartCoordinate chartCoordinate) {
        CategoryAxis categoryAxis = new CategoryAxis();
        categoryAxis.setSide(Side.LEFT);
        categoryAxis.setTickLabelsVisible(displayCategoryAxis);
        categoryAxis.setGapStartAndEnd(true);

        NumberAxis numberAxis = new NumberAxis();
        numberAxis.setSide(Side.TOP);
        switch (chartCoordinate) {
            case LogarithmicE:
                numberAxis.setTickLabelFormatter(new LogarithmicECoordinate());
                break;
            case Logarithmic10:
                numberAxis.setTickLabelFormatter(new Logarithmic10Coordinate());
                break;
            case SquareRoot:
                numberAxis.setTickLabelFormatter(new SquareRootCoordinate());
                break;
        }

        return new LabeledHorizontalBarChart(numberAxis, categoryAxis)
                .setChartCoordinate(chartCoordinate);
    }

    public LabeledHorizontalBarChart(Axis xAxis, Axis yAxis) {
        super(xAxis, yAxis);
        init();
    }

    private void init() {
        labelType = LabelType.NameAndValue;
//        this.setBarGap(0.0);
//        this.setCategoryGap(2);
        this.setLegendSide(Side.TOP);
        this.setMaxWidth(Double.MAX_VALUE);
        this.setMaxHeight(Double.MAX_VALUE);
        VBox.setVgrow(this, Priority.ALWAYS);
        HBox.setHgrow(this, Priority.ALWAYS);

    }

    public LabeledHorizontalBarChart style(String cssFile) {
        this.getStylesheets().add(LabeledHorizontalBarChart.class.getResource(cssFile).toExternalForm());
        return this;
    }

    @Override
    protected void seriesAdded(Series<X, Y> series, int seriesIndex) {
        super.seriesAdded(series, seriesIndex);
        if (labelType == null || labelType == LabelType.NotDisplay || labelType == LabelType.Pop) {
            return;
        }
        try {
            for (int j = 0; j < series.getData().size(); j++) {
                Data<X, Y> item = series.getData().get(j);
                String name = item.getYValue() + "";
                String value = item.getXValue() + "";
                String labelValue = StringTools.format(FxmlControl.realValue(chartCoordinate, Double.valueOf(value)));
                String label;
                switch (labelType) {
                    case Name:
                        label = name;
                        break;
                    case Value:
                        label = labelValue;
                        break;
                    case NameAndValue:
                    default:
                        label = name + " " + labelValue;
                        break;
                }
                Text text = new Text(label);
                text.setStyle("-fx-font-size: " + textSize + "px;  -fx-text-fill: black;");

                TextFlow textFlow = new TextFlow(text);
                textFlow.setTextAlignment(TextAlignment.CENTER);

                nodeMap.put(item.getNode(), textFlow);
                this.getPlotChildren().add(textFlow);
            }
        } catch (Exception e) {
            logger.debug(e.toString());
        }
    }

    @Override
    protected void seriesRemoved(final Series<X, Y> series) {
        if (labelType != null && labelType != LabelType.NotDisplay && labelType != LabelType.Pop) {
            for (Node bar : nodeMap.keySet()) {
                Node text = nodeMap.get(bar);
                this.getPlotChildren().remove(text);
            }
            nodeMap.clear();
        }
        super.seriesRemoved(series);
    }

    @Override
    protected void layoutPlotChildren() {
        super.layoutPlotChildren();
        if (labelType == null || labelType == LabelType.NotDisplay || labelType == LabelType.Pop) {
            return;
        }
        for (Node bar : nodeMap.keySet()) {
            TextFlow textFlow = nodeMap.get(bar);
            textFlow.relocate(bar.getBoundsInParent().getMaxX() + 10, bar.getBoundsInParent().getMinY());
        }
    }

    public Map<Node, TextFlow> getNodeMap() {
        return nodeMap;
    }

    public LabeledHorizontalBarChart setNodeMap(Map<Node, TextFlow> nodeMap) {
        this.nodeMap = nodeMap;
        return this;
    }

    public boolean isIntValue() {
        return intValue;
    }

    public LabeledHorizontalBarChart setIntValue(boolean intValue) {
        this.intValue = intValue;
        return this;
    }

    public String getCssFile() {
        return cssFile;
    }

    public LabeledHorizontalBarChart setCssFile(String cssFile) {
        this.cssFile = cssFile;
        return this;
    }

    public LabelType getLabelType() {
        return labelType;
    }

    public LabeledHorizontalBarChart setLabelType(LabelType labelType) {
        this.labelType = labelType;
        return this;
    }

    public int getTextSize() {
        return textSize;
    }

    public LabeledHorizontalBarChart setTextSize(int textSize) {
        this.textSize = textSize;
        return this;
    }

    public ChartCoordinate getChartCoordinate() {
        return chartCoordinate;
    }

    public LabeledHorizontalBarChart setChartCoordinate(ChartCoordinate chartCoordinate) {
        this.chartCoordinate = chartCoordinate;
        return this;
    }

}