package de.ivu.elect.presentation.charts;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.google.gson.Gson;

/**
 *  on 29.04.2015.
 */
public class BarChart implements Serializable {

  public static final double ANZAHL_EINTRAEGE_Y_ACHSE = 10d;

  private static final String KEINE_EINHEIT = "";

  private static final String GANZE_ZAHL = "";

  private static final String PROZENT = "%";

  private static final String ZWEI_STELLEN = "-2";

  private final BarChartType chartType;

  private final List<BarChartEntry> entries = new ArrayList<>();

  private final List<BarChartDataset> datasets = new ArrayList<>();

  private transient Gson gson;

  private final String einheit;

  private final String roundNumber;

  private boolean rotateInGraphLabel;

  private BarChartSize size = BarChartSize.AUTO;

  public static BarChart createHorizontalBarChartPercent() {
    return new BarChart(BarChartType.HORIZONTAL, PROZENT, ZWEI_STELLEN);
  }
  public static BarChart createHorizontalBarChart() {
    return new BarChart(BarChartType.HORIZONTAL, KEINE_EINHEIT, GANZE_ZAHL);
  }

  public static BarChart createBarChartPercent() {
    return new BarChart(BarChartType.VERTICAL, PROZENT, ZWEI_STELLEN);
  }

  public static BarChart createBarChart() {
    return new BarChart(BarChartType.VERTICAL, KEINE_EINHEIT, GANZE_ZAHL);
  }

  private BarChart(BarChartType chartType, String einheit, String roundNumber) {
    this.chartType = chartType;
    this.einheit = einheit;
    this.roundNumber = roundNumber;
  }

  public BarChartEntry createEntry(String label) {
    return createEntry(label, null);
  }

  public BarChartEntry createEntry(String label, String color) {
    BarChartEntry entry = new BarChartEntry(label, color);
    if (BarChartType.VERTICAL.equals(chartType)) {
      entries.add(entry);
    } else {
      entries.add(0, entry); // Reihenfolge muss bei horizontalem BarChart genau andersrum sein, damit der erste Eintrag oben ist...
    }
    return entry;
  }

  public BarChartDataset createDataset(String label, double alpha) {
    BarChartDataset dataset = new BarChartDataset(this, label, null, alpha);
    datasets.add(dataset);
    return dataset;
  }

  public BarChartDataset createDataset(String label, String color) {
    BarChartDataset dataset = new BarChartDataset(this, label, color, 1d);
    datasets.add(dataset);
    return dataset;
  }

  public BarChartDataset createDataset(String label, String color, double alpha) {
    BarChartDataset dataset = new BarChartDataset(this, label, color, alpha);
    datasets.add(dataset);
    return dataset;
  }


  public BarChartDataset createDataset(String label) {
    return createDataset(label, 1d);
  }

  public String getLabels() {
    List<String> entryLabels = new ArrayList<>(entries.size());
    for (BarChartEntry entry : entries) {
      entryLabels.add(entry.getLabel());
    }

    return toJson(entryLabels);
  }

  protected String toJson(Object o) {
    if (gson == null) {
      gson = new Gson();
    }
    return gson.toJson(o);
  }

  public List<BarChartDataset> getDatasets() {
    return datasets;
  }

  public List<BarChartEntry> getEntries() {
    return entries;
  }

  public double getMinimumInterval() {
    return bestimmeAchseneinteilung(getMinValue(), getMaxValue());
    //return Math.abs(getGraphMin() - getGraphMax()) / ANZAHL_EINTRAEGE_Y_ACHSE;
  }

  public double getMaxValue() {
    Double highestValue = null;
    for (BarChartDataset dataset : datasets) {
      for (BarChartData barChartData : dataset.getData().values()) {
        double value = barChartData.getValueAsDouble();
        if (highestValue == null || highestValue < value) {
          highestValue = value;
        }
      }
    }
    if (highestValue == null) {
      return 0d;
    }

    if (highestValue <= 0d) {
      return 0d;
    }

    return highestValue;
  }

  public double getGraphMax() {
    return getMaxValue()+(getMinimumInterval()/100d);
  }

  public double getMinValue() {
    Double smallestValue = null;
    for (BarChartDataset dataset : datasets) {
      for (BarChartData barChartData : dataset.getData().values()) {
        double value = barChartData.getValueAsDouble();
        if (smallestValue == null || smallestValue.doubleValue() > value) {
          smallestValue = value;
        }
      }
    }
    if (smallestValue == null) {
      return 0d;
    }
    if (smallestValue.doubleValue() >= 0d) {
      return 0d;
    }

    return smallestValue;
  }

  public double getGraphMin() {

    double minValue = getMinValue();

    if (minValue==0d){
      return minValue;
    }
    return minValue-(getMinimumInterval()/100d);
  }

  public String getBarType() {
    return chartType.getBarType();
  }


  public String getDatasetLabels(){
    List<String> datasetLabels = new ArrayList<>(datasets.size());
    for (BarChartDataset dataset : datasets) {
      datasetLabels.add(dataset.getLabelRaw());
    }
    return toJson(datasetLabels).replace("\"", "'");
  }

  public String getEinheit() {
    return einheit;
  }

  public String getRoundNumber() {
    return roundNumber;
  }


  private static double bestimmeAchseneinteilung(double minimum, double maximum) {
    double abstand = 0;
    if (minimum >= maximum) {
      return 0.001d; // irgendwas zurückgeben...
    }
    // Zu teilender Abstand
    double differenz = maximum - minimum;
    double skalierung = Math.pow(10, -Math.round((Math.log10(differenz))) + 1);
    double skalierteDifferenz = differenz * skalierung;
    double skalierterAbstand;
    if (skalierteDifferenz < 6) {
      skalierterAbstand = 0.5;
    }   else  if (skalierteDifferenz < 11) {
      skalierterAbstand = 1;
    } else if (skalierteDifferenz < 20) {
      skalierterAbstand = 2;
    } else if (skalierteDifferenz < 60) {
      skalierterAbstand = 5;
    } else {
      skalierterAbstand = 10;
    }
    abstand = skalierterAbstand / skalierung;
    return abstand;
  }

  public boolean isRotateInGraphLabel(){
    return rotateInGraphLabel;
  }

  public void setRotateInGraphLabel(boolean rotateInGraphLabel) {
    this.rotateInGraphLabel = rotateInGraphLabel;
  }

  public BarChartSize getSize() {
    return size;
  }

  public void setSize(BarChartSize size) {
    this.size = size;
  }
}
