/**********************************************************************
 * Copyright (c) by Heiner Jostkleigrewe
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,  but WITHOUT ANY WARRANTY; without
 *  even the implied warranty of  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *  the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program.  If not,
 * see <http://www.gnu.org/licenses/>.
 *
 * heiner@jverein.de
 * www.jverein.de
 **********************************************************************/
package de.jost_net.JVerein.gui.control;

import java.rmi.RemoteException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

import de.jost_net.JVerein.Einstellungen;
import de.jost_net.JVerein.Einstellungen.Property;
import de.jost_net.JVerein.gui.action.BuchungAction;
import de.jost_net.JVerein.gui.action.EditAction;
import de.jost_net.JVerein.gui.dialogs.TabelleSpaltenAuswahlDialog;
import de.jost_net.JVerein.gui.input.FormularInput;
import de.jost_net.JVerein.gui.input.MitgliedInput;
import de.jost_net.JVerein.gui.menu.BuchungPartAnzeigenMenu;
import de.jost_net.JVerein.gui.menu.SpendenbescheinigungMenu;
import de.jost_net.JVerein.gui.parts.BetragSummaryTablePart;
import de.jost_net.JVerein.gui.parts.BuchungListPart;
import de.jost_net.JVerein.gui.parts.ButtonRtoL;
import de.jost_net.JVerein.gui.parts.JVereinTablePart;
import de.jost_net.JVerein.gui.view.SpendenbescheinigungDetailView;
import de.jost_net.JVerein.gui.view.SpendenbescheinigungMailView;
import de.jost_net.JVerein.io.SpendenbescheinigungAusgabe;
import de.jost_net.JVerein.keys.Adressblatt;
import de.jost_net.JVerein.keys.Ausgabeart;
import de.jost_net.JVerein.keys.Filter;
import de.jost_net.JVerein.keys.FormularArt;
import de.jost_net.JVerein.keys.HerkunftSpende;
import de.jost_net.JVerein.keys.MailAuswahl;
import de.jost_net.JVerein.keys.Spendenart;
import de.jost_net.JVerein.keys.SuchSpendenart;
import de.jost_net.JVerein.keys.SuchVersand;
import de.jost_net.JVerein.keys.VorlageTyp;
import de.jost_net.JVerein.rmi.Buchung;
import de.jost_net.JVerein.rmi.Formular;
import de.jost_net.JVerein.rmi.JVereinDBObject;
import de.jost_net.JVerein.rmi.Mitglied;
import de.jost_net.JVerein.rmi.Spendenbescheinigung;
import de.jost_net.JVerein.util.Datum;
import de.jost_net.JVerein.util.JVDateFormatTTMMJJJJ;
import de.jost_net.JVerein.util.SpbAdressaufbereitung;
import de.jost_net.JVerein.util.VorlageUtil;
import de.willuhn.datasource.BeanUtil;
import de.willuhn.datasource.pseudo.PseudoIterator;
import de.willuhn.datasource.rmi.DBIterator;
import de.willuhn.datasource.rmi.DBService;
import de.willuhn.datasource.rmi.ResultSetExtractor;
import de.willuhn.jameica.gui.AbstractView;
import de.willuhn.jameica.gui.Action;
import de.willuhn.jameica.gui.GUI;
import de.willuhn.jameica.gui.formatter.CurrencyFormatter;
import de.willuhn.jameica.gui.formatter.DateFormatter;
import de.willuhn.jameica.gui.input.AbstractInput;
import de.willuhn.jameica.gui.input.CheckboxInput;
import de.willuhn.jameica.gui.input.DateInput;
import de.willuhn.jameica.gui.input.DecimalInput;
import de.willuhn.jameica.gui.input.Input;
import de.willuhn.jameica.gui.input.SelectInput;
import de.willuhn.jameica.gui.input.TextAreaInput;
import de.willuhn.jameica.gui.input.TextInput;
import de.willuhn.jameica.gui.parts.Button;
import de.willuhn.jameica.gui.parts.Column;
import de.willuhn.jameica.gui.parts.PanelButton;
import de.willuhn.jameica.system.OperationCanceledException;
import de.willuhn.logging.Logger;
import de.willuhn.util.ApplicationException;

public class SpendenbescheinigungControl extends DruckMailControl
    implements Savable
{

  // Spendenbescheinigung View
  private BetragSummaryTablePart spbList;

  private SelectInput spendenart;

  private AbstractInput mitglied;

  private TextInput zeile1;

  private TextInput zeile2;

  private TextInput zeile3;

  private TextInput zeile4;

  private TextInput zeile5;

  private TextInput zeile6;

  private TextInput zeile7;

  private DateInput spendedatum;

  private DateInput bescheinigungsdatum;

  private DecimalInput betrag;

  private FormularInput formular;

  private CheckboxInput ersatzaufwendungen;

  private TextAreaInput bezeichnungsachzuwendung;

  private SelectInput herkunftspende;

  private CheckboxInput unterlagenwertermittlung;

  private Spendenbescheinigung spendenbescheinigung;

  private DateInput versanddatum;

  private boolean and = false;

  private String sql = "";

  final static String ExportPDF = ".pdf";

  final static String ExportCSV = ".csv";

  public SpendenbescheinigungControl(AbstractView view)
  {
    super(view);
  }

  private Spendenbescheinigung getSpendenbescheinigung()
  {
    if (spendenbescheinigung != null)
    {
      return spendenbescheinigung;
    }
    spendenbescheinigung = (Spendenbescheinigung) getCurrentObject();
    return spendenbescheinigung;
  }

  public SelectInput getSpendenart() throws RemoteException
  {
    if (spendenart != null)
    {
      return spendenart;
    }
    spendenart = new SelectInput(Spendenart.getArray(),
        new Spendenart(getSpendenbescheinigung().getSpendenart()));
    spendenart.setEnabled(false);
    return spendenart;
  }

  public Input getMitglied() throws RemoteException
  {
    if (mitglied != null)
    {
      return mitglied;
    }
    Mitglied m = getSpendenbescheinigung().getMitglied();
    mitglied = new MitgliedInput().getMitgliedInput(mitglied, m,
        (Integer) Einstellungen.getEinstellung(Property.MITGLIEDAUSWAHL));
    mitglied.addListener(new MitgliedListener());
    if (mitglied instanceof SelectInput)
    {
      ((SelectInput) mitglied).setPleaseChoose("Optional auswählen");
      if (m == null)
      {
        ((SelectInput) mitglied).setPreselected(null);
      }
    }
    if (m != null || !getSpendenbescheinigung().isNewObject())
    {
      mitglied.disable();
    }
    return mitglied;
  }

  public TextInput getZeile1(boolean withFocus) throws RemoteException
  {
    if (zeile1 != null)
    {
      return zeile1;
    }
    zeile1 = new TextInput(getSpendenbescheinigung().getZeile1(), 80);
    if (withFocus)
    {
      zeile1.focus();
    }
    zeile1.setEnabled(getSpendenbescheinigung().isNewObject());
    return zeile1;
  }

  public TextInput getZeile2() throws RemoteException
  {
    if (zeile2 != null)
    {
      return zeile2;
    }
    zeile2 = new TextInput(getSpendenbescheinigung().getZeile2(), 80);
    zeile2.setEnabled(getSpendenbescheinigung().isNewObject());
    return zeile2;
  }

  public TextInput getZeile3() throws RemoteException
  {
    if (zeile3 != null)
    {
      return zeile3;
    }
    zeile3 = new TextInput(getSpendenbescheinigung().getZeile3(), 80);
    zeile3.setEnabled(getSpendenbescheinigung().isNewObject());
    return zeile3;
  }

  public TextInput getZeile4() throws RemoteException
  {
    if (zeile4 != null)
    {
      return zeile4;
    }
    zeile4 = new TextInput(getSpendenbescheinigung().getZeile4(), 80);
    zeile4.setEnabled(getSpendenbescheinigung().isNewObject());
    return zeile4;
  }

  public TextInput getZeile5() throws RemoteException
  {
    if (zeile5 != null)
    {
      return zeile5;
    }
    zeile5 = new TextInput(getSpendenbescheinigung().getZeile5(), 80);
    zeile5.setEnabled(getSpendenbescheinigung().isNewObject());
    return zeile5;
  }

  public TextInput getZeile6() throws RemoteException
  {
    if (zeile6 != null)
    {
      return zeile6;
    }
    zeile6 = new TextInput(getSpendenbescheinigung().getZeile6(), 80);
    zeile6.setEnabled(getSpendenbescheinigung().isNewObject());
    return zeile6;
  }

  public TextInput getZeile7() throws RemoteException
  {
    if (zeile7 != null)
    {
      return zeile7;
    }
    zeile7 = new TextInput(getSpendenbescheinigung().getZeile7(), 80);
    zeile7.setEnabled(getSpendenbescheinigung().isNewObject());
    return zeile7;
  }

  public DateInput getSpendedatum() throws RemoteException
  {
    if (spendedatum != null)
    {
      return spendedatum;
    }
    spendedatum = new DateInput(getSpendenbescheinigung().getSpendedatum());
    spendedatum.setEnabled(false);
    return spendedatum;
  }

  public DateInput getBescheinigungsdatum() throws RemoteException
  {
    if (bescheinigungsdatum != null)
    {
      return bescheinigungsdatum;
    }
    bescheinigungsdatum = new DateInput(
        getSpendenbescheinigung().getBescheinigungsdatum());
    bescheinigungsdatum.setEnabled(getSpendenbescheinigung().isNewObject());
    return bescheinigungsdatum;
  }

  public DecimalInput getBetrag() throws RemoteException
  {
    if (betrag != null)
    {
      return betrag;
    }
    betrag = new DecimalInput(getSpendenbescheinigung().getBetrag(),
        Einstellungen.DECIMALFORMAT);
    betrag.setEnabled(false);
    return betrag;
  }

  public SelectInput getFormular() throws RemoteException
  {
    if (formular != null)
    {
      return formular;
    }
    String def = null;
    if (getSpendenbescheinigung().getFormular() != null)
    {
      def = getSpendenbescheinigung().getFormular().getID();
    }
    if (getSpendenbescheinigung().getSpendenart() == Spendenart.SACHSPENDE)
    {
      formular = new FormularInput(FormularArt.SACHSPENDENBESCHEINIGUNG, def);
      // Wegen kompatibilität zu früher
      if (def != null)
      {
        Formular f = getSpendenbescheinigung().getFormular();
        @SuppressWarnings("unchecked")
        List<Formular> list = formular.getList();

        // Contains geht bei RemoteObject nicht, muss über BeanUtil gemacht
        // werden
        boolean found = false;
        for (Formular fo : list)
        {
          if (BeanUtil.equals(fo, f))
          {
            found = true;
            break;
          }
        }
        if (!found)
        {
          list.add(f);
          formular.setList(list);
        }
      }
    }
    else if (getSpendenbescheinigung().getBuchungen().size() > 1)
    {
      formular = new FormularInput(FormularArt.SAMMELSPENDENBESCHEINIGUNG, def);
    }
    else
    {
      formular = new FormularInput(FormularArt.SPENDENBESCHEINIGUNG, def);
    }
    formular.setPleaseChoose("Standard");
    return formular;
  }

  public CheckboxInput getErsatzAufwendungen() throws RemoteException
  {
    if (ersatzaufwendungen != null)
    {
      return ersatzaufwendungen;
    }
    List<Buchung> buchungen = getSpendenbescheinigung().getBuchungen();
    boolean check = false;
    if (buchungen != null && buchungen.size() == 1
        && getSpendenbescheinigung().getSpendenart() == Spendenart.GELDSPENDE)
    {
      // Verzicht aus Buchung lesen
      check = buchungen.get(0).getVerzicht();
    }
    ersatzaufwendungen = new CheckboxInput(check);
    if (buchungen != null && buchungen.size() > 1
        && getSpendenbescheinigung().getSpendenart() == Spendenart.GELDSPENDE)
    {
      // Sammelspendenbescheinigung
      ersatzaufwendungen.setName("*siehe Buchungsliste");
    }
    ersatzaufwendungen.disable();
    return ersatzaufwendungen;
  }

  public TextAreaInput getBezeichnungSachzuwendung() throws RemoteException
  {
    if (bezeichnungsachzuwendung != null)
    {
      return bezeichnungsachzuwendung;
    }
    bezeichnungsachzuwendung = new TextAreaInput(
        getSpendenbescheinigung().getBezeichnungSachzuwendung(), 1000);
    bezeichnungsachzuwendung.setHeight(50);
    bezeichnungsachzuwendung.disable();
    return bezeichnungsachzuwendung;
  }

  public SelectInput getHerkunftSpende() throws RemoteException
  {
    if (herkunftspende != null)
    {
      return herkunftspende;
    }
    herkunftspende = new SelectInput(HerkunftSpende.getArray(),
        new HerkunftSpende(getSpendenbescheinigung().getHerkunftSpende()));
    herkunftspende.disable();
    return herkunftspende;
  }

  public CheckboxInput getUnterlagenWertermittlung() throws RemoteException
  {
    if (unterlagenwertermittlung != null)
    {
      return unterlagenwertermittlung;
    }
    unterlagenwertermittlung = new CheckboxInput(
        getSpendenbescheinigung().getUnterlagenWertermittlung());
    unterlagenwertermittlung.disable();
    return unterlagenwertermittlung;
  }

  public DateInput getVersanddatum() throws RemoteException
  {
    if (versanddatum != null)
    {
      return versanddatum;
    }
    versanddatum = new DateInput(getSpendenbescheinigung().getVersanddatum());
    versanddatum.setName("Versand Datum");
    versanddatum.disable();
    return versanddatum;
  }

  public JVereinTablePart getBuchungListPart() throws RemoteException
  {
    return new BuchungListPart(getSpendenbescheinigung().getBuchungen(),
        new BuchungAction(false), new BuchungPartAnzeigenMenu());
  }

  @Override
  public JVereinDBObject prepareStore() throws RemoteException
  {
    Spendenbescheinigung spb = getSpendenbescheinigung();
    Spendenart spa = (Spendenart) getSpendenart().getValue();
    spb.setMitglied((Mitglied) getMitglied().getValue());
    spb.setSpendenart(spa.getKey());
    spb.setZeile1((String) getZeile1(false).getValue());
    spb.setZeile2((String) getZeile2().getValue());
    spb.setZeile3((String) getZeile3().getValue());
    spb.setZeile4((String) getZeile4().getValue());
    spb.setZeile5((String) getZeile5().getValue());
    spb.setZeile6((String) getZeile6().getValue());
    spb.setZeile7((String) getZeile7().getValue());
    spb.setSpendedatum((Date) getSpendedatum().getValue());
    spb.setBescheinigungsdatum((Date) getBescheinigungsdatum().getValue());
    spb.setBetrag((Double) getBetrag().getValue());
    spb.setBezeichnungSachzuwendung(
        (String) getBezeichnungSachzuwendung().getValue());
    spb.setFormular((Formular) getFormular().getValue());
    HerkunftSpende hsp = (HerkunftSpende) getHerkunftSpende().getValue();
    spb.setHerkunftSpende(hsp.getKey());
    spb.setUnterlagenWertermittlung(
        (Boolean) getUnterlagenWertermittlung().getValue());
    spb.setVersanddatum((Date) getVersanddatum().getValue());
    return spb;
  }

  /**
   * This method stores the project using the current values.
   * 
   * @throws ApplicationException
   */
  @Override
  public void handleStore() throws ApplicationException
  {
    try
    {
      prepareStore().store();
    }
    catch (RemoteException e)
    {
      String fehler = "Fehler bei Speichern der Spendenbescheinigung";
      Logger.error(fehler, e);
      throw new ApplicationException(fehler, e);
    }
  }

  public ButtonRtoL getDruckUndMailButton()
  {
    ButtonRtoL b = new ButtonRtoL("Druck und Mail", c -> {
      Spendenbescheinigung spb = getSpendenbescheinigung();
      try
      {
        if (spb.isNewObject())
        {
          GUI.getStatusBar()
              .setErrorText("Spendenbescheinigung bitte erst speichern!");
          return;
        }
      }
      catch (RemoteException e)
      {
        Logger.error(e.getMessage());
        throw new ApplicationException(
            "Fehler bei der Aufbereitung der Spendenbescheinigung");
      }
      GUI.startView(SpendenbescheinigungMailView.class,
          new Spendenbescheinigung[] { getSpendenbescheinigung() });
    }, getSpendenbescheinigung(), false, "document-print.png");
    return b;
  }

  @Override
  public JVereinTablePart getTablePart()
      throws RemoteException, ApplicationException
  {
    if (spbList != null)
    {
      return spbList;
    }
    spbList = new BetragSummaryTablePart(getSpendenbescheinigungen(), null);
    spbList.addColumn("Nr", "id-int");
    spbList.addColumn("Versanddatum", "versanddatum",
        new DateFormatter(new JVDateFormatTTMMJJJJ()));
    spbList.addColumn("Spender", "mitglied");
    spbList.addColumn("Spendenart", "spendenart",
        o -> new Spendenart((Integer) o).getText(), false, Column.ALIGN_LEFT);
    spbList.addColumn("Bescheinigungsdatum", "bescheinigungsdatum",
        new DateFormatter(new JVDateFormatTTMMJJJJ()));
    spbList.addColumn("Spendedatum", "spendedatum",
        new DateFormatter(new JVDateFormatTTMMJJJJ()));
    spbList.addColumn("Betrag", "betrag",
        new CurrencyFormatter("", Einstellungen.DECIMALFORMAT));
    spbList.addColumn("Zeile 1", "zeile1");
    spbList.addColumn("Zeile 2", "zeile2");
    spbList.addColumn("Zeile 3", "zeile3");
    spbList.addColumn("Zeile 4", "zeile4");
    spbList.addColumn("Zeile 5", "zeile5");
    spbList.addColumn("Zeile 6", "zeile6");
    spbList.addColumn("Zeile 7", "zeile7");

    spbList.setContextMenu(new SpendenbescheinigungMenu(spbList));
    spbList.setMulti(true);
    spbList.setAction(
        new EditAction(SpendenbescheinigungDetailView.class, spbList));
    VorZurueckControl.setObjektListe(null, null);
    return spbList;
  }

  @Override
  protected void TabRefresh() throws ApplicationException
  {
    if (spbList != null)
    {
      try
      {
        spbList.removeAll();
        for (Spendenbescheinigung spb : getSpendenbescheinigungen())
        {
          spbList.addItem(spb);
        }
        spbList.sort();
      }
      catch (RemoteException e1)
      {
        Logger.error("Fehler beim Refresh der Tabelle", e1);
      }
    }
  }

  @SuppressWarnings("unchecked")
  private ArrayList<Spendenbescheinigung> getSpendenbescheinigungen()
      throws RemoteException, ApplicationException
  {
    SuchSpendenart suchSpendenart = (SuchSpendenart) getFilter()
        .get(Filter.SPENDENART);
    ArrayList<Long> ids = new ArrayList<>();
    ArrayList<Long> queryIds = querySpendenbescheinigungen(suchSpendenart);

    // Bei GELDSPENDE_ECHT liefert das Query auch Splittbuchungen die neben
    // echten Geldbuchungen auch Geldbuchungen mit Verzicht haben.
    // Darum lesen wir nochmal mit ERSTATTUNGSVERZICHT. Wenn eine Id da
    // auch dabei ist dürfen wir die Splittbuchung nicht nehmen
    if (suchSpendenart == SuchSpendenart.GELDSPENDE_ECHT)
    {
      ArrayList<Long> erstattungsIds = querySpendenbescheinigungen(
          SuchSpendenart.ERSTATTUNGSVERZICHT);
      for (Long id : queryIds)
      {
        if (!erstattungsIds.contains(id))
        {
          ids.add(id);
        }
      }
    }
    else
    {
      ids = queryIds;
    }

    if (ids.size() == 0)
      return new ArrayList<Spendenbescheinigung>();

    DBIterator<Spendenbescheinigung> list = Einstellungen.getDBService()
        .createList(Spendenbescheinigung.class);
    list.addFilter("id in (" + StringUtils.join(ids, ",") + ")");
    list.setOrder(" ORDER BY bescheinigungsdatum desc, spendedatum desc ");
    ArrayList<Spendenbescheinigung> spendenbescheinigungen = list != null
        ? (ArrayList<Spendenbescheinigung>) PseudoIterator.asList(list)
        : null;
    return spendenbescheinigungen;
  }

  @SuppressWarnings("unchecked")
  private ArrayList<Long> querySpendenbescheinigungen(
      SuchSpendenart suchSpendenart)
      throws RemoteException, ApplicationException
  {
    final DBService service = Einstellungen.getDBService();
    ArrayList<Object> bedingungen = new ArrayList<>();
    and = false;

    sql = "select DISTINCT spendenbescheinigung.id, bescheinigungsdatum from spendenbescheinigung ";
    if (getFilter().get(Filter.MAIL) != null)
    {
      sql += "left join mitglied on (spendenbescheinigung.mitglied = mitglied.id) ";
    }
    Object spart = getFilter().get(Filter.SPENDENART);
    if (spart != null && spart != SuchSpendenart.GELDSPENDE
        && spart != SuchSpendenart.SACHSPENDE)
    {
      sql += "left join buchung on (spendenbescheinigung.id = buchung.spendenbescheinigung) ";
    }

    for (Entry<Filter, Object> entry : getFilter().entrySet())
    {
      Object value = entry.getValue();
      switch (entry.getKey())
      {
        case MAIL:
          switch ((MailAuswahl) value)
          {
            case OHNE:
              addCondition("(email is null or length(email) = 0) ");
              break;
            case MIT:
              addCondition("(email is not null and length(email) > 0) ");
          }
          break;
        case SPENDENART:
          switch ((SuchSpendenart) value)
          {
            case GELDSPENDE:
              addCondition("spendenart = ?");
              bedingungen.add(Spendenart.GELDSPENDE);
              break;
            case SACHSPENDE:
              addCondition("spendenart = ?");
              bedingungen.add(Spendenart.SACHSPENDE);
              break;
            case ERSTATTUNGSVERZICHT:
              addCondition("buchung.verzicht = 1");
              break;
            case GELDSPENDE_ECHT:
              addCondition(
                  "(buchung.verzicht != 1 or buchung.verzicht is null) AND spendenart = ?");
              bedingungen.add(Spendenart.GELDSPENDE);
              break;
            case SACHSPENDE_ERSTATTUNGSVERZICHT:
              addCondition("(buchung.verzicht = 1 OR spendenart = ?)");
              bedingungen.add(Spendenart.SACHSPENDE);
              break;
            default:
              break;
          }
          break;
        case ZEILE2:
          addCondition("(lower(zeile2) like ?)");
          bedingungen.add("%" + value.toString().toLowerCase() + "%");
          break;
        case DATUM_BESCHEINIGUNG_VON:
          addCondition("bescheinigungsdatum >= ?");
          bedingungen.add(value);
          break;
        case DATUM_BESCHEINIGUNG_BIS:
          addCondition("bescheinigungsdatum <= ?");
          bedingungen.add(value);
          break;
        case DATUM_SPENDE_VON:
          addCondition("spendedatum >= ?");
          bedingungen.add(value);
          break;
        case DATUM_SPENDE_BIS:
          addCondition("spendedatum <= ?");
          bedingungen.add(value);
          break;
        case VERSAND:
          switch ((SuchVersand) value)
          {
            case VERSAND:
              addCondition("versanddatum IS NOT NULL");
              break;
            case NICHT_VERSAND:
              addCondition("versanddatum IS NULL");
              break;
          }
          break;
        default:
          throw new ApplicationException(
              "Filter nicht implementiert: " + entry.getKey().getAnzeigeText());
      }
    }

    ResultSetExtractor rs = new ResultSetExtractor()
    {
      @Override
      public Object extract(ResultSet rs) throws RemoteException, SQLException
      {
        ArrayList<Long> list = new ArrayList<>();
        while (rs.next())
        {
          list.add(rs.getLong(1));
        }
        return list;
      }
    };

    return (ArrayList<Long>) service.execute(sql, bedingungen.toArray(), rs);
  }

  private void addCondition(String condition)
  {
    if (and)
    {
      sql += " AND ";
    }
    else
    {
      sql += "where ";
    }
    and = true;
    sql += condition;
  }

  // Mail/Drucken View
  @Override
  public String getInfoText(Object selection) throws RemoteException
  {
    Spendenbescheinigung[] spbArr = null;
    if (selection instanceof Spendenbescheinigung)
    {
      spbArr = new Spendenbescheinigung[] { (Spendenbescheinigung) selection };
    }
    else if (selection instanceof Spendenbescheinigung[])
    {
      spbArr = (Spendenbescheinigung[]) selection;
    }
    else
    {
      return "";
    }

    String text = "Es wurden " + spbArr.length
        + " Spendenbescheinigungen ausgewählt";
    String fehlen = "";
    String keinMitglied = "";

    for (Spendenbescheinigung spb : spbArr)
    {
      Mitglied m = spb.getMitglied();
      if (m != null && (m.getEmail() == null || m.getEmail().isEmpty()))
      {
        fehlen = fehlen + "\n - " + m.getName() + ", " + m.getVorname();
      }
      if (spb.getMitglied() == null)
      {
        keinMitglied = keinMitglied + "\n - " + spb.getZeile1() + ", "
            + spb.getZeile2() + ", " + spb.getZeile3();
      }
    }
    if (fehlen.length() > 0)
    {
      text += "\nFolgende Mitglieder haben keine Mailadresse:" + fehlen;
    }
    if (keinMitglied.length() > 0)
    {
      text += "\nFür folgende Spendenbescheinigungen existiert kein Mitglied und keine Mailadresse:"
          + keinMitglied;
    }

    return text;
  }

  public Button getStartButton(final Object currentObject)
  {
    return new Button("Starten", new Action()
    {

      @Override
      public void handleAction(Object context)
      {
        try
        {
          saveFilterSettings();
          new SpendenbescheinigungAusgabe((String) mailtext.getValue(),
              (Adressblatt) adressblatt.getValue()).aufbereiten(
                  getDruckMailSpendenbescheinigungen(currentObject),
                  (Ausgabeart) getAusgabeart().getValue(), getBetreffString(),
                  getTxtString(), false,
                  (Boolean) Einstellungen
                      .getEinstellung(Property.UNTERSCHRIFTDRUCKEN),
                  (Boolean) versand.getValue());
        }
        catch (ApplicationException ae)
        {
          GUI.getStatusBar().setErrorText(ae.getMessage());
        }
        catch (Exception e)
        {
          Logger.error("Fehler", e);
          GUI.getStatusBar().setErrorText(e.getMessage());
        }
      }
    }, null, true, "walking.png");
  }

  private class MitgliedListener implements Listener
  {
    @Override
    public void handleEvent(Event event)
    {
      try
      {
        Mitglied selected = (Mitglied) getMitglied().getValue();
        if (selected != null)
        {
          SpbAdressaufbereitung.adressaufbereitung(selected,
              spendenbescheinigung);
          zeile1.setValue(spendenbescheinigung.getZeile1());
          zeile2.setValue(spendenbescheinigung.getZeile2());
          zeile3.setValue(spendenbescheinigung.getZeile3());
          zeile4.setValue(spendenbescheinigung.getZeile4());
          zeile5.setValue(spendenbescheinigung.getZeile5());
          zeile6.setValue(spendenbescheinigung.getZeile6());
          zeile7.setValue(spendenbescheinigung.getZeile7());
        }
        else
        {
          spendenbescheinigung.setMitglied((Integer) null);
        }
      }
      catch (Exception e)
      {
        Logger.error("Fehler beim Setzen des Mitglieds:", e);
      }
    }
  }

  private ArrayList<Spendenbescheinigung> getDruckMailSpendenbescheinigungen(
      Object object) throws RemoteException, ApplicationException
  {
    if (object instanceof Spendenbescheinigung)
    {
      object = new Spendenbescheinigung[] { (Spendenbescheinigung) object };
    }
    if (object instanceof Spendenbescheinigung[])
    {
      return new ArrayList<Spendenbescheinigung>(
          Arrays.asList((Spendenbescheinigung[]) object));
    }

    ArrayList<Spendenbescheinigung> spblist = getSpendenbescheinigungen();

    if (spblist == null || spblist.size() == 0)
    {
      throw new ApplicationException(
          "Für die gewählten Filterkriterien wurden keine Spendenbescheinigungen gefunden");
    }
    return spblist;
  }

  @Override
  DruckMailEmpfaenger getDruckMailMitglieder(Object object, String option)
      throws RemoteException, ApplicationException
  {
    List<DruckMailEmpfaengerEntry> liste = new ArrayList<>();
    String text = "";
    int ohneMail = 0;
    int ohneMitglied = 0;
    ArrayList<Spendenbescheinigung> spbs = getDruckMailSpendenbescheinigungen(
        object);
    Mitglied m;
    String dokument = "";
    for (Spendenbescheinigung spb : spbs)
    {
      m = spb.getMitglied();
      if (m != null)
      {
        String mail = m.getEmail();
        if ((mail == null || mail.isEmpty())
            && getAusgabeart().getValue() == Ausgabeart.MAIL)
        {
          ohneMail++;
        }
        dokument = "Spendenbescheinigung von "
            + Datum.formatDate(spb.getBescheinigungsdatum()) + " über "
            + Einstellungen.DECIMALFORMAT.format(spb.getBetrag()) + "€";
        liste.add(new DruckMailEmpfaengerEntry(dokument, mail, m.getName(),
            m.getVorname(), m.getMitgliedstyp()));
      }
      else
      {
        ohneMitglied++;
        dokument = "Spendenbescheinigung von "
            + Datum.formatDate(spb.getBescheinigungsdatum()) + " über "
            + Einstellungen.DECIMALFORMAT.format(spb.getBetrag())
            + "€ und Zeile 2: " + spb.getZeile2();
        liste.add(new DruckMailEmpfaengerEntry(dokument, null, null, null, ""));
      }
    }

    if (ohneMail > 0)
    {
      text = ohneMail + " Mitglied" + (ohneMail > 1 ? "er haben" : " hat")
          + " keine Mail Adresse";
      if (ohneMitglied == 0)
      {
        text += ".";
      }
      else
      {
        text += " und" + ohneMitglied + " Spendenbescheinigung"
            + (ohneMitglied > 1 ? "en haben" : " hat")
            + " kein Mitglied gesetzt.";
      }
    }
    else if (ohneMitglied > 0)
    {
      text = ohneMitglied + " Spendenbescheinigung"
          + (ohneMitglied > 1 ? "en haben" : " hat") + " kein Mitglied gesetzt";
      if (getAusgabeart().getValue() == Ausgabeart.MAIL)
      {
        text += ".";
      }
      else
      {
        text += ", " + (ohneMitglied > 1 ? "werden" : "wird")
            + " aber gedruckt.";
      }

    }
    return new DruckMailEmpfaenger(liste, text);
  }

  @Override
  public Map<Mitglied, Object> getDruckMailList()
      throws RemoteException, ApplicationException
  {
    Map<Mitglied, Object> map = new HashMap<>();
    ArrayList<Spendenbescheinigung> spbs = getDruckMailSpendenbescheinigungen(
        this.view.getCurrentObject());
    for (Spendenbescheinigung spb : spbs)
    {
      if (spb.getMitglied() != null)
      {
        map.put(spb.getMitglied(), spb);
      }
    }
    return map;
  }

  public PanelButton getDetailSpaltenPanelButton()
  {
    return new PanelButton("document-properties.png", context -> {
      try
      {
        new TabelleSpaltenAuswahlDialog(getBuchungListPart()).open();
      }
      catch (OperationCanceledException | ApplicationException e)
      {
        throw e;
      }
      catch (Exception e)
      {
        Logger.error("Fehler beim Spalten-Auswahl-Dialog", e);
        throw new ApplicationException("Fehler beim Spalten-Auswahl-Dialog");
      }
    }, "Spalten auswählen");
  }

  @Override
  protected String getTableTitle()
  {
    return VorlageUtil.getName(VorlageTyp.SPENDENBESCHEINIGUNGEN_TITEL, this);
  }

  @Override
  protected String getTableSubtitle()
  {
    return VorlageUtil.getName(VorlageTyp.SPENDENBESCHEINIGUNGEN_SUBTITEL,
        this);
  }

  @Override
  protected String getTableDateiname()
  {
    return VorlageUtil.getName(VorlageTyp.SPENDENBESCHEINIGUNGEN_DATEINAME,
        this);
  }
}
