package io.github.jjsimas.chart2d;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.util.Objects;

public final class ChartTheme {

  private final Color background;
  private final Color plotBackground;
  private final Color gridColor;
  private final Color axisColor;
  private final Color textColor;
  private final Color titleColor;
  private final Color legendBackground;
  private final Color legendBorderColor;
  private final Insets padding;
  private final Dimension preferredPlotSize;
  private final Dimension minimumPlotSize;
  private final int panelGapModel;
  private final int elementGapModel;
  private final int axisTickLengthModel;
  private final int legendSwatchSizeModel;
  private final int categoryGapModel;
  private final int pointDiameterModel;
  private final int minLabelFontSize;
  private final int minLegendFontSize;
  private final int minTitleFontSize;
  private final float axisStrokeWidthModel;
  private final float gridStrokeWidthModel;
  private final float seriesStrokeWidthModel;
  private final float outlineStrokeWidthModel;
  private final Font labelFont;
  private final Font legendFont;
  private final Font titleFont;
  private final Font axisTitleFont;

  private ChartTheme(Builder builder) {
    background = builder.background;
    plotBackground = builder.plotBackground;
    gridColor = builder.gridColor;
    axisColor = builder.axisColor;
    textColor = builder.textColor;
    titleColor = builder.titleColor;
    legendBackground = builder.legendBackground;
    legendBorderColor = builder.legendBorderColor;
    padding = (Insets) builder.padding.clone();
    preferredPlotSize = new Dimension(builder.preferredPlotSize);
    minimumPlotSize = new Dimension(builder.minimumPlotSize);
    panelGapModel = builder.panelGapModel;
    elementGapModel = builder.elementGapModel;
    axisTickLengthModel = builder.axisTickLengthModel;
    legendSwatchSizeModel = builder.legendSwatchSizeModel;
    categoryGapModel = builder.categoryGapModel;
    pointDiameterModel = builder.pointDiameterModel;
    minLabelFontSize = builder.minLabelFontSize;
    minLegendFontSize = builder.minLegendFontSize;
    minTitleFontSize = builder.minTitleFontSize;
    axisStrokeWidthModel = builder.axisStrokeWidthModel;
    gridStrokeWidthModel = builder.gridStrokeWidthModel;
    seriesStrokeWidthModel = builder.seriesStrokeWidthModel;
    outlineStrokeWidthModel = builder.outlineStrokeWidthModel;
    labelFont = builder.labelFont;
    legendFont = builder.legendFont;
    titleFont = builder.titleFont;
    axisTitleFont = builder.axisTitleFont;
  }

  public static Builder builder() {
    return new Builder();
  }

  public static ChartTheme defaults() {
    return builder().build();
  }

  public Color background() {
    return background;
  }

  public Color plotBackground() {
    return plotBackground;
  }

  public Color gridColor() {
    return gridColor;
  }

  public Color axisColor() {
    return axisColor;
  }

  public Color textColor() {
    return textColor;
  }

  public Color titleColor() {
    return titleColor;
  }

  public Color legendBackground() {
    return legendBackground;
  }

  public Color legendBorderColor() {
    return legendBorderColor;
  }

  public Insets padding() {
    return (Insets) padding.clone();
  }

  public Dimension preferredPlotSize() {
    return new Dimension(preferredPlotSize);
  }

  public Dimension minimumPlotSize() {
    return new Dimension(minimumPlotSize);
  }

  public int panelGapModel() {
    return panelGapModel;
  }

  public int elementGapModel() {
    return elementGapModel;
  }

  public int axisTickLengthModel() {
    return axisTickLengthModel;
  }

  public int legendSwatchSizeModel() {
    return legendSwatchSizeModel;
  }

  public int categoryGapModel() {
    return categoryGapModel;
  }

  public int pointDiameterModel() {
    return pointDiameterModel;
  }

  public int minLabelFontSize() {
    return minLabelFontSize;
  }

  public int minLegendFontSize() {
    return minLegendFontSize;
  }

  public int minTitleFontSize() {
    return minTitleFontSize;
  }

  public float axisStrokeWidthModel() {
    return axisStrokeWidthModel;
  }

  public float gridStrokeWidthModel() {
    return gridStrokeWidthModel;
  }

  public float seriesStrokeWidthModel() {
    return seriesStrokeWidthModel;
  }

  public float outlineStrokeWidthModel() {
    return outlineStrokeWidthModel;
  }

  public Font labelFont() {
    return labelFont;
  }

  public Font legendFont() {
    return legendFont;
  }

  public Font titleFont() {
    return titleFont;
  }

  public Font axisTitleFont() {
    return axisTitleFont;
  }

  public Font scaledLabelFont(float scale) {
    return scaledFont(labelFont, scale);
  }

  public Font scaledLegendFont(float scale) {
    return scaledFont(legendFont, scale);
  }

  public Font scaledTitleFont(float scale) {
    return scaledFont(titleFont, scale);
  }

  public Font scaledAxisTitleFont(float scale) {
    return scaledFont(axisTitleFont, scale);
  }

  public Font scaledFont(Font font, float scale) {
    float pointSize = Math.max(1.0f, font.getSize2D() * Math.max(scale, 0.0f));
    return font.deriveFont(pointSize);
  }

  public static final class Builder {

    private Color background = new Color(250, 252, 255);
    private Color plotBackground = Color.WHITE;
    private Color gridColor = new Color(222, 229, 239);
    private Color axisColor = new Color(72, 83, 96);
    private Color textColor = new Color(46, 56, 68);
    private Color titleColor = new Color(21, 34, 50);
    private Color legendBackground = new Color(255, 255, 255, 238);
    private Color legendBorderColor = new Color(205, 214, 225);
    private Insets padding = new Insets(18, 18, 18, 18);
    private Dimension preferredPlotSize = new Dimension(360, 240);
    private Dimension minimumPlotSize = new Dimension(56, 56);
    private int panelGapModel = 14;
    private int elementGapModel = 8;
    private int axisTickLengthModel = 6;
    private int legendSwatchSizeModel = 12;
    private int categoryGapModel = 8;
    private int pointDiameterModel = 8;
    private int minLabelFontSize = 9;
    private int minLegendFontSize = 9;
    private int minTitleFontSize = 12;
    private float axisStrokeWidthModel = 1.2f;
    private float gridStrokeWidthModel = 1.0f;
    private float seriesStrokeWidthModel = 2.0f;
    private float outlineStrokeWidthModel = 1.0f;
    private Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
    private Font legendFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
    private Font titleFont = new Font(Font.SANS_SERIF, Font.BOLD, 19);
    private Font axisTitleFont = new Font(Font.SANS_SERIF, Font.BOLD, 14);

    private Builder() {
    }

    public Builder background(Color value) {
      background = Objects.requireNonNull(value);
      return this;
    }

    public Builder plotBackground(Color value) {
      plotBackground = Objects.requireNonNull(value);
      return this;
    }

    public Builder gridColor(Color value) {
      gridColor = Objects.requireNonNull(value);
      return this;
    }

    public Builder axisColor(Color value) {
      axisColor = Objects.requireNonNull(value);
      return this;
    }

    public Builder textColor(Color value) {
      textColor = Objects.requireNonNull(value);
      return this;
    }

    public Builder titleColor(Color value) {
      titleColor = Objects.requireNonNull(value);
      return this;
    }

    public Builder legendBackground(Color value) {
      legendBackground = Objects.requireNonNull(value);
      return this;
    }

    public Builder legendBorderColor(Color value) {
      legendBorderColor = Objects.requireNonNull(value);
      return this;
    }

    public Builder padding(Insets value) {
      padding = (Insets) Objects.requireNonNull(value).clone();
      return this;
    }

    public Builder preferredPlotSize(Dimension value) {
      preferredPlotSize = new Dimension(Objects.requireNonNull(value));
      return this;
    }

    public Builder minimumPlotSize(Dimension value) {
      minimumPlotSize = new Dimension(Objects.requireNonNull(value));
      return this;
    }

    public Builder panelGapModel(int value) {
      panelGapModel = value;
      return this;
    }

    public Builder elementGapModel(int value) {
      elementGapModel = value;
      return this;
    }

    public Builder axisTickLengthModel(int value) {
      axisTickLengthModel = value;
      return this;
    }

    public Builder legendSwatchSizeModel(int value) {
      legendSwatchSizeModel = value;
      return this;
    }

    public Builder categoryGapModel(int value) {
      categoryGapModel = value;
      return this;
    }

    public Builder pointDiameterModel(int value) {
      pointDiameterModel = value;
      return this;
    }

    public Builder minLabelFontSize(int value) {
      minLabelFontSize = value;
      return this;
    }

    public Builder minLegendFontSize(int value) {
      minLegendFontSize = value;
      return this;
    }

    public Builder minTitleFontSize(int value) {
      minTitleFontSize = value;
      return this;
    }

    public Builder axisStrokeWidthModel(float value) {
      axisStrokeWidthModel = value;
      return this;
    }

    public Builder gridStrokeWidthModel(float value) {
      gridStrokeWidthModel = value;
      return this;
    }

    public Builder seriesStrokeWidthModel(float value) {
      seriesStrokeWidthModel = value;
      return this;
    }

    public Builder outlineStrokeWidthModel(float value) {
      outlineStrokeWidthModel = value;
      return this;
    }

    public Builder labelFont(Font value) {
      labelFont = Objects.requireNonNull(value);
      return this;
    }

    public Builder legendFont(Font value) {
      legendFont = Objects.requireNonNull(value);
      return this;
    }

    public Builder titleFont(Font value) {
      titleFont = Objects.requireNonNull(value);
      return this;
    }

    public Builder axisTitleFont(Font value) {
      axisTitleFont = Objects.requireNonNull(value);
      return this;
    }

    public ChartTheme build() {
      return new ChartTheme(this);
    }
  }
}
