
package de.ivu.elect.business.gruppe.boundary;

import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.control.AbstractGruppeNachErfassungsgebietBuilder;
import de.ivu.elect.business.gruppe.entity.GebietDarstellung;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.GruppeKumulierteStimmen;
import de.ivu.elect.business.gruppe.entity.GruppenDarstellung;
import de.ivu.elect.presentation.charts.BarChart;
import de.ivu.elect.presentation.charts.BarChartDataset;
import de.ivu.elect.presentation.charts.BarChartEntry;
import de.ivu.elect.presentation.gebietsbaum.GebietNummerFormatter;
import de.ivu.elect.presentation.gruppe.table.AbstractGruppeKumulierteStimmenColumn;
import de.ivu.elect.presentation.gruppe.table.GruppeNachErfassungsgebietStimmenColumnBuilder;

public class GruppeNachErfassungsgebietBarChartBuilder extends AbstractGruppeNachErfassungsgebietBuilder<BarChart> {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient GebietNummerFormatter gebietNummerFormatter;

  public GruppeNachErfassungsgebietBarChartBuilder() {
    super();
  }

  @Override
  public BarChart buildInternal(GruppeNachErfassungsgebietStimmenSearchParameters searchParameters, List<GruppeKumulierteStimmen> searchResult,
      List<AbstractGruppeKumulierteStimmenColumn> darzustellendeWerteGrafisch, GruppeNachErfassungsgebietStimmenColumnBuilder gruppeNachErfassungsgebietStimmenColumnBuilder) {

    // YEAH! Code zum Austoben ;)

    boolean isPercent = isProzentualeAngaben(darzustellendeWerteGrafisch);

    BarChart result;
    if (searchParameters.getSuchergebnisDarstellung().isHorizontal()) {
      if (isPercent) {
        result = BarChart.createHorizontalBarChartPercent();
      } else {
        result = BarChart.createHorizontalBarChart();
      }
    } else {
      if (isPercent) {
        result = BarChart.createBarChartPercent();
      } else {
        result = BarChart.createBarChart();
      }
    }
    result.setRotateInGraphLabel(searchParameters.getSuchergebnisDarstellung().isRotateInGraphLabel());
    result.setSize(searchParameters.getChartGroesse().getBarChartSize());

    List<Gruppe> gruppen = bestimmeGruppenImSuchergebnis(searchResult);
    List<Gebiet> gebiete = bestimmeGebieteImSuchergebnis(searchResult);

    boolean isNachPartei = searchParameters.getGruppeNachErfassungsgebietStimmenGruppierung().isNachGruppe();

    Map<Object, Map<AbstractGruppeKumulierteStimmenColumn, BarChartDataset>> datasetMap = new HashMap<>();

    int multiplikator;
    if (isNachPartei) {
      multiplikator = gebiete.size();
    } else {
      multiplikator = 1;
    }
    int anzahlBalken = darzustellendeWerteGrafisch.size() * multiplikator;
    double alphaDiff = bestimmeAlphaDiff(anzahlBalken);
    double startAlpha = 1d - alphaDiff * (anzahlBalken - 1);
    double alpha = startAlpha;

    if (isNachPartei) {
      for (Gebiet gebiet : gebiete) {
        datasetMap.put(gebiet, new HashMap<>());

        for (AbstractGruppeKumulierteStimmenColumn columnEntry : darzustellendeWerteGrafisch) {
          BarChartDataset ds = result
              .createDataset(getWertBezeichnung(gruppeNachErfassungsgebietStimmenColumnBuilder, columnEntry) + "<br/>" + getGebietBezeichnung(searchParameters.getGebietDarstellung(), gebiet), alpha);
          datasetMap.get(gebiet).put(columnEntry, ds);
          alpha += alphaDiff;
        }
      }
    } else {
      for (Gruppe gruppe : gruppen) {
        alpha = startAlpha;
        datasetMap.put(gruppe, new HashMap<>());
        for (AbstractGruppeKumulierteStimmenColumn columnEntry : darzustellendeWerteGrafisch) {
          BarChartDataset ds = result.createDataset(
              getWertBezeichnung(gruppeNachErfassungsgebietStimmenColumnBuilder, columnEntry) + "<br/>" + getGruppenBezeichnung(searchParameters.getGruppenDarstellung(), gruppe), gruppe.getFarbe(),
              alpha);
          datasetMap.get(gruppe).put(columnEntry, ds);
          alpha += alphaDiff;
        }
      }
    }
    Map<Object, BarChartEntry> barChartEntryMap = new HashMap<>();

    for (Gebiet gebiet : gebiete) {
      for (Gruppe gruppe : gruppen) {
        BarChartEntry entry;
        if (isNachPartei) {
          if (!barChartEntryMap.containsKey(gruppe)) {
            barChartEntryMap.put(gruppe, result.createEntry(getGruppenBezeichnung(searchParameters.getGruppenDarstellung(), gruppe), gruppe.getFarbe()));
          }
          entry = barChartEntryMap.get(gruppe);
        } else {
          if (!barChartEntryMap.containsKey(gebiet)) {
            barChartEntryMap.put(gebiet, result.createEntry(getGebietBezeichnung(searchParameters.getGebietDarstellung(), gebiet)));
          }
          entry = barChartEntryMap.get(gebiet);
        }

        for (GruppeKumulierteStimmen gruppeKumulierteStimmen : searchResult) {

          if (gruppeKumulierteStimmen.getGebiet().equals(gebiet) && gruppeKumulierteStimmen.getGruppe().equals(gruppe)) {

            for (AbstractGruppeKumulierteStimmenColumn columnEntry : darzustellendeWerteGrafisch) {
              BarChartDataset barChartDataset;

              if (isNachPartei) {
                barChartDataset = datasetMap.get(gebiet).get(columnEntry);
              } else {
                barChartDataset = datasetMap.get(gruppe).get(columnEntry);
              }

              Number displayValue = (Number) columnEntry.getDisplayValue(gruppeKumulierteStimmen); // ELECT-9389

              if (isPercent) {
                if (displayValue == null) {
                  barChartDataset.addData(entry, 0d * 100d);
                } else {
                  barChartDataset.addData(entry, (Double) displayValue * 100d);
                }
              } else {
                if (displayValue == null) {
                  barChartDataset.addData(entry, 0L);
                } else {
                  barChartDataset.addData(entry, displayValue);
                }
              }
            }
          }
        }
      }
    }

    return result;
  }

  private String getWertBezeichnung(GruppeNachErfassungsgebietStimmenColumnBuilder gruppeNachErfassungsgebietStimmenColumnBuilder, AbstractGruppeKumulierteStimmenColumn columnEntry) {
    return defaultIfBlank(columnEntry.getHeader(), gruppeNachErfassungsgebietStimmenColumnBuilder.getHeaderForColumnInColumnGroup(columnEntry));
  }

  private String getGruppenBezeichnung(GruppenDarstellung gruppenDarstellung, Gruppe gruppe) {
    return gruppenDarstellung.getBezeichnung(gruppe);
  }

  private String getGebietBezeichnung(GebietDarstellung gebietDarstellung, Gebiet gebiet) {
    return gebietDarstellung.getBezeichnung(gebiet, gebietNummerFormatter);
  }

  private double bestimmeAlphaDiff(int size) {
    if (size == 0) {
      return 0d;
    }

    return 0.7d / (size * 1d);
  }

  private List<Gebiet> bestimmeGebieteImSuchergebnis(List<GruppeKumulierteStimmen> searchResult) {
    Set<Gebiet> bestrachtet = new HashSet<>();
    return searchResult.stream().map(GruppeKumulierteStimmen::getGebiet).filter(bestrachtet::add).collect(toList());
  }

  private List<Gruppe> bestimmeGruppenImSuchergebnis(List<GruppeKumulierteStimmen> searchResult) {
    Set<Gruppe> bestrachtet = new HashSet<>();
    return searchResult.stream().map(GruppeKumulierteStimmen::getGruppe).filter(bestrachtet::add).collect(toList());
  }

}
