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

import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

import javax.faces.validator.LongRangeValidator;
import javax.faces.validator.Validator;
import javax.inject.Named;

import de.ivu.elect.business.ergebnis.boundary.KumulierteStimmenSearchStimmabgabeartProperty;
import de.ivu.elect.business.ergebnis.boundary.NachErfassungsgebietStimmenSearchGebietsauswahlProperty;
import de.ivu.elect.business.ergebnis.control.AbstractNachErfassungsgebietStimmenSearchParameters;
import org.apache.deltaspike.core.api.scope.ConversationGroup;
import org.apache.deltaspike.core.api.scope.GroupedConversationScoped;

import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.gruppe.entity.ChartGroesse;
import de.ivu.elect.business.gruppe.entity.EntityMitGruppen;
import de.ivu.elect.business.gruppe.entity.GebietDarstellung;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.GruppeNachErfassungsgebietStimmenGruppierung;
import de.ivu.elect.business.gruppe.entity.GruppenDarstellung;
import de.ivu.elect.business.gruppe.entity.SuchergebnisDarstellung;
import de.ivu.elect.business.gruppe.entity.SvgAnzahlEinteilung;
import de.ivu.elect.business.gruppe.entity.SvgArtEinteilung;
import de.ivu.elect.jee.presentation.dynamicform.AbstractEnumProperty;
import de.ivu.elect.jee.presentation.dynamicform.AbstractNumberProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaEnumProperty;
import de.ivu.elect.jee.presentation.dynamicform.Property;
import de.ivu.elect.presentation.gruppe.form.GruppeNachErfassungsgebietStimmenWerteProperty;
import de.ivu.elect.presentation.gruppe.form.GruppenSearchProperty;

@GroupedConversationScoped
@ConversationGroup(GruppeNachErfassungsgebietStimmenSearchParameters.class)
@Named(GruppeNachErfassungsgebietStimmenSearchParameters.COMPONENT_NAME)
public class GruppeNachErfassungsgebietStimmenSearchParameters extends AbstractNachErfassungsgebietStimmenSearchParameters implements EntityMitGruppen<Gruppe> {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "gruppeNachErfassungsgebietStimmenSearchParameters";

  private GruppeNachErfassungsgebietStimmenGruppierung gruppeNachErfassungsgebietStimmenGruppierung;

  private List<Gruppe> gruppen;

  private transient Property gruppenProperty;

  private GruppenDarstellung gruppenDarstellung;

  private transient Property gebietsauswahlProperty;

  private GebietDarstellung gebietDarstellung;

  private transient Property stimmabgabeartProperty;

  private SuchergebnisDarstellung suchergebnisDarstellung;

  private List<String> darzustellendeWerteGrafisch;

  private transient Property darzustellendeWerteGrafischProperty;

  private ChartGroesse chartGroesse;

  private SvgAnzahlEinteilung svgAnzahlEinteilung;

  private transient Property svgAnzahlEinteilungProperty;

  private SvgArtEinteilung svgArtEinteilung;

  private transient Property svgArtEinteilungProperty;

  private int svgLegendePositionHorizontal;

  private transient Property svgLegendePositionHorizontalProperty;

  private int svgLegendePositionVertikal;

  private transient Property svgLegendePositionVertikalProperty;

  public GruppeNachErfassungsgebietStimmenSearchParameters() {
    super();

    this.gruppeNachErfassungsgebietStimmenGruppierung = GruppeNachErfassungsgebietStimmenGruppierung.GEBIETE_NACH_GRUPPE;

    this.gruppen = new ArrayList<>();
    this.gruppenProperty = null;
    this.gruppenDarstellung = GruppenDarstellung.KURZNAME;

    this.gebietsauswahlProperty = null;
    this.gebietDarstellung = GebietDarstellung.NAME;

    this.stimmabgabeartProperty = null;

    this.suchergebnisDarstellung = SuchergebnisDarstellung.TABELLE;

    this.darzustellendeWerteGrafisch = new ArrayList<>();
    this.darzustellendeWerteGrafischProperty = null;

    this.chartGroesse = ChartGroesse.AUTO;

    this.svgAnzahlEinteilung = SvgAnzahlEinteilung.FEST_6;
    this.svgAnzahlEinteilungProperty = null;
    this.svgArtEinteilung = SvgArtEinteilung.AEQUIDISTANT;
    this.svgArtEinteilungProperty = null;
    this.svgLegendePositionHorizontal = 0;
    this.svgLegendePositionHorizontalProperty = null;
    this.svgLegendePositionVertikal = 0;
    this.svgLegendePositionVertikalProperty = null;
  }

  public GruppeNachErfassungsgebietStimmenGruppierung getGruppeNachErfassungsgebietStimmenGruppierung() {
    return gruppeNachErfassungsgebietStimmenGruppierung;
  }

  public void setGruppeNachErfassungsgebietStimmenGruppierung(GruppeNachErfassungsgebietStimmenGruppierung gruppeNachErfassungsgebietStimmenGruppierung) {
    this.gruppeNachErfassungsgebietStimmenGruppierung = gruppeNachErfassungsgebietStimmenGruppierung;
  }

  public Property getGruppeNachErfassungsgebietStimmenGruppierungProperty() {
    return new LambdaEnumProperty<>(getMessage("GruppeNachErfassungsgebietStimmenGruppierung.label"), true, this::setGruppeNachErfassungsgebietStimmenGruppierung,
        this::getGruppeNachErfassungsgebietStimmenGruppierung, GruppeNachErfassungsgebietStimmenGruppierung.class, asList(GruppeNachErfassungsgebietStimmenGruppierung.values()));
  }

  @Override
  public List<Gruppe> getGruppen() {
    return gruppen;
  }

  @Override
  public void setGruppen(List<Gruppe> gruppen) {
    this.gruppen = gruppen;
  }

  public Property getGruppenProperty(List<Gruppe> gruppen) {
    if (gruppenProperty == null) {
      gruppenProperty = new GruppenSearchProperty<Gruppe>(this, gruppen) {

        @Override
        public boolean getDefaultRequired() {
          return true;
        }
      };
    }

    return gruppenProperty;
  }

  public void resetGruppenProperty() {
    gruppenProperty = null;
  }

  public GruppenDarstellung getGruppenDarstellung() {
    return gruppenDarstellung;
  }

  public void setGruppenDarstellung(GruppenDarstellung gruppenDarstellung) {
    this.gruppenDarstellung = gruppenDarstellung;
  }

  public Property getGruppenDarstellungProperty() {
    return new LambdaEnumProperty<>(getMessage("GruppenDarstellung.label"), true, this::setGruppenDarstellung, this::getGruppenDarstellung, GruppenDarstellung.class,
        asList(GruppenDarstellung.values()));
  }

  public Property getGebietsauswahlProperty() {
    if (gebietsauswahlProperty == null) {
      gebietsauswahlProperty = new NachErfassungsgebietStimmenSearchGebietsauswahlProperty(this);
    }

    return gebietsauswahlProperty;
  }

  public GebietDarstellung getGebietDarstellung() {
    return gebietDarstellung;
  }

  public void setGebietDarstellung(GebietDarstellung gebietDarstellung) {
    this.gebietDarstellung = gebietDarstellung;
  }

  public Property getGebietDarstellungProperty() {
    return new LambdaEnumProperty<>(getMessage("GebietDarstellung.label"), true, this::setGebietDarstellung, this::getGebietDarstellung, GebietDarstellung.class, asList(GebietDarstellung.values()));
  }

  public Property getStimmabgabeartProperty() {
    if (stimmabgabeartProperty == null) {
      stimmabgabeartProperty = new KumulierteStimmenSearchStimmabgabeartProperty(this);
    }

    return stimmabgabeartProperty;
  }

  public Property getErgebnisartFuerDifferenzProperty(List<Ergebnisart> values) {
    return new LambdaEnumProperty<>(getMessage("ergebnis.ergebnisart.filter"), true, this::setErgebnisartFuerDifferenz, this::getErgebnisartFuerDifferenz, Ergebnisart.class, values);
  }

  public SuchergebnisDarstellung getSuchergebnisDarstellung() {
    return suchergebnisDarstellung;
  }

  public void setSuchergebnisDarstellung(SuchergebnisDarstellung suchergebnisDarstellung) {
    this.suchergebnisDarstellung = suchergebnisDarstellung;
  }

  public Property getSuchergebnisDarstellungProperty() {
    return createSuchergebnisDarstellungProperty(value -> true);
  }

  public Property getSuchergebnisDarstellungTabellarischProperty() {
    return createSuchergebnisDarstellungProperty(SuchergebnisDarstellung::isTabellarisch);
  }

  public Property getSuchergebnisDarstellungGrafischProperty() {
    return createSuchergebnisDarstellungProperty(SuchergebnisDarstellung::isGrafisch);
  }

  private Property createSuchergebnisDarstellungProperty(Predicate< ? super SuchergebnisDarstellung> valuesPredicate) {
    return new LambdaEnumProperty<>(getMessage("SuchergebnisDarstellung.label"), true, this::setSuchergebnisDarstellung, this::getSuchergebnisDarstellung, SuchergebnisDarstellung.class,
        stream(SuchergebnisDarstellung.values()).filter(valuesPredicate).collect(toList()));
  }

  public List<String> getDarzustellendeWerteGrafisch() {
    return darzustellendeWerteGrafisch;
  }

  public void setDarzustellendeWerteGrafisch(List<String> darzustellendeWerteGrafisch) {
    this.darzustellendeWerteGrafisch = darzustellendeWerteGrafisch;
  }

  public Property getDarzustellendeWerteGrafischProperty(List<String> values) {
    if (darzustellendeWerteGrafischProperty == null) {
      darzustellendeWerteGrafischProperty = new GruppeNachErfassungsgebietStimmenWerteProperty(values, this::setDarzustellendeWerteGrafisch, this::getDarzustellendeWerteGrafisch);
    }

    return darzustellendeWerteGrafischProperty;
  }

  public ChartGroesse getChartGroesse() {
    return chartGroesse;
  }

  public void setChartGroesse(ChartGroesse chartGroesse) {
    this.chartGroesse = chartGroesse;
  }

  public Property getChartGroesseProperty() {
    return new LambdaEnumProperty<>(getMessage("ChartGroesse.label"), true, this::setChartGroesse, this::getChartGroesse, ChartGroesse.class, asList(ChartGroesse.values()));
  }

  public SvgAnzahlEinteilung getSvgAnzahlEinteilung() {
    return svgAnzahlEinteilung;
  }

  public void setSvgAnzahlEinteilung(SvgAnzahlEinteilung svgAnzahlEinteilung) {
    this.svgAnzahlEinteilung = svgAnzahlEinteilung;
  }

  public Property getSvgAnzahlEinteilungProperty() {
    if (svgAnzahlEinteilungProperty == null) {
      svgAnzahlEinteilungProperty = new SvgAnzahlEinteilungProperty(this);
    }

    return svgAnzahlEinteilungProperty;
  }

  public SvgArtEinteilung getSvgArtEinteilung() {
    return svgArtEinteilung;
  }

  public void setSvgArtEinteilung(SvgArtEinteilung svgArtEinteilung) {
    this.svgArtEinteilung = svgArtEinteilung;
  }

  public Property getSvgArtEinteilungProperty() {
    if (svgArtEinteilungProperty == null) {
      svgArtEinteilungProperty = new SvgArtEinteilungProperty(this);
    }

    return svgArtEinteilungProperty;
  }

  public int getSvgLegendePositionHorizontal() {
    return svgLegendePositionHorizontal;
  }

  public void setSvgLegendePositionHorizontal(int svgLegendePositionHorizontal) {
    this.svgLegendePositionHorizontal = svgLegendePositionHorizontal;
  }

  public Property getSvgLegendePositionHorizontalPropertyProperty() {
    if (svgLegendePositionHorizontalProperty == null) {
      svgLegendePositionHorizontalProperty = new SvgLegendePositionHorizontalProperty(this);
    }

    return svgLegendePositionHorizontalProperty;
  }

  public int getSvgLegendePositionVertikal() {
    return svgLegendePositionVertikal;
  }

  public void setSvgLegendePositionVertikal(int svgLegendePositionVertikal) {
    this.svgLegendePositionVertikal = svgLegendePositionVertikal;
  }

  public Property getSvgLegendePositionVertikalPropertyProperty() {
    if (svgLegendePositionVertikalProperty == null) {
      svgLegendePositionVertikalProperty = new SvgLegendePositionVertikalProperty(this);
    }

    return svgLegendePositionVertikalProperty;
  }

  @Override
  public void reset() {
    super.reset();

    gruppeNachErfassungsgebietStimmenGruppierung = GruppeNachErfassungsgebietStimmenGruppierung.GEBIETE_NACH_GRUPPE;

    gruppen = new ArrayList<>();
    gruppenProperty = null;
    gruppenDarstellung = GruppenDarstellung.KURZNAME;

    gebietsauswahlProperty = null;
    gebietDarstellung = GebietDarstellung.NAME;

    stimmabgabeartProperty = null;

    suchergebnisDarstellung = SuchergebnisDarstellung.TABELLE;

    darzustellendeWerteGrafisch = new ArrayList<>();
    darzustellendeWerteGrafischProperty = null;

    chartGroesse = ChartGroesse.AUTO;

    svgAnzahlEinteilung = SvgAnzahlEinteilung.FEST_6;
    svgAnzahlEinteilungProperty = null;
    svgArtEinteilung = SvgArtEinteilung.AEQUIDISTANT;
    svgArtEinteilungProperty = null;
    svgLegendePositionHorizontal = 0;
    svgLegendePositionHorizontalProperty = null;
    svgLegendePositionVertikal = 0;
    svgLegendePositionVertikalProperty = null;
  }

  public static final class SvgAnzahlEinteilungProperty extends AbstractEnumProperty<SvgAnzahlEinteilung> {

    private final GruppeNachErfassungsgebietStimmenSearchParameters searchParameters;

    public SvgAnzahlEinteilungProperty(GruppeNachErfassungsgebietStimmenSearchParameters searchParameters) {
      super();

      this.searchParameters = searchParameters;
    }

    @Override
    public SvgAnzahlEinteilung getValue() {
      return searchParameters.getSvgAnzahlEinteilung();
    }

    @Override
    public void setValue(SvgAnzahlEinteilung value) {
      searchParameters.setSvgAnzahlEinteilung(value);
    }

    @Override
    public List<SvgAnzahlEinteilung> getValues() {
      return asList(SvgAnzahlEinteilung.values());
    }

    @Override
    public String getDefaultLabel() {
      return getMessage("gruppe.nach.gebiet.svg.anzahl.einteilung");
    }

    @Override
    public boolean getDefaultRequired() {
      return true;
    }

    @Override
    protected Class<SvgAnzahlEinteilung> getEnumClass() {
      return SvgAnzahlEinteilung.class;
    }
  }

  public static final class SvgArtEinteilungProperty extends AbstractEnumProperty<SvgArtEinteilung> {

    private final GruppeNachErfassungsgebietStimmenSearchParameters searchParameters;

    public SvgArtEinteilungProperty(GruppeNachErfassungsgebietStimmenSearchParameters searchParameters) {
      super();

      this.searchParameters = searchParameters;
    }

    @Override
    public SvgArtEinteilung getValue() {
      return searchParameters.getSvgArtEinteilung();
    }

    @Override
    public void setValue(SvgArtEinteilung value) {
      searchParameters.setSvgArtEinteilung(value);
    }

    @Override
    public List<SvgArtEinteilung> getValues() {
      return asList(SvgArtEinteilung.values());
    }

    @Override
    public String getDefaultLabel() {
      return getMessage("gruppe.nach.gebiet.svg.art.einteilung");
    }

    @Override
    public boolean getDefaultRequired() {
      return true;
    }

    @Override
    protected Class<SvgArtEinteilung> getEnumClass() {
      return SvgArtEinteilung.class;
    }
  }

  private static final class SvgLegendePositionHorizontalProperty extends AbstractNumberProperty<Integer> {

    private final GruppeNachErfassungsgebietStimmenSearchParameters searchParameters;

    public SvgLegendePositionHorizontalProperty(GruppeNachErfassungsgebietStimmenSearchParameters searchParameters) {
      super();

      this.searchParameters = searchParameters;
    }

    @Override
    public Integer getValue() {
      return searchParameters.getSvgLegendePositionHorizontal();
    }

    @Override
    public void setValue(Integer integer) {
      if (integer == null) {
        searchParameters.setSvgLegendePositionHorizontal(0);
      } else {
        searchParameters.setSvgLegendePositionHorizontal(integer);
      }
    }

    @Override
    public long getMinValue() {
      return 0;
    }

    @Override
    public long getMaxValue() {
      return 100;
    }

    @Override
    public int getDefaultMaxLength() {
      return 3;
    }

    @Override
    public String getDefaultLabel() {
      return getMessage("gruppe.nach.gebiet.svg.legende.relative.position.horizontal");
    }

    @Override
    public boolean getDefaultRequired() {
      return false;
    }

    @Override
    public Collection<Validator> getValidators() {
      ArrayList<Validator> result = new ArrayList<>(super.getValidators());
      result.add(new LongRangeValidator(100, 0));

      return result;
    }
  }

  private static final class SvgLegendePositionVertikalProperty extends AbstractNumberProperty<Integer> {

    private final GruppeNachErfassungsgebietStimmenSearchParameters searchParameters;

    public SvgLegendePositionVertikalProperty(GruppeNachErfassungsgebietStimmenSearchParameters searchParameters) {
      super();

      this.searchParameters = searchParameters;
    }

    @Override
    public Integer getValue() {
      return searchParameters.getSvgLegendePositionVertikal();
    }

    @Override
    public void setValue(Integer integer) {
      if (integer == null) {
        searchParameters.setSvgLegendePositionVertikal(0);
      } else {
        searchParameters.setSvgLegendePositionVertikal(integer);
      }
    }

    @Override
    public long getMinValue() {
      return 0;
    }

    @Override
    public long getMaxValue() {
      return 100;
    }

    @Override
    public int getDefaultMaxLength() {
      return 3;
    }

    @Override
    public String getDefaultLabel() {
      return getMessage("gruppe.nach.gebiet.svg.legende.relative.position.vertikal");
    }

    @Override
    public boolean getDefaultRequired() {
      return false;
    }

    @Override
    public Collection<Validator> getValidators() {
      ArrayList<Validator> result = new ArrayList<>(super.getValidators());
      result.add(new LongRangeValidator(100, 0));

      return result;
    }
  }
}
