/* Copyright 2014-2023 askanimus@gmail.com */

/* This File is part of "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2).
 *
 * "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2) 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.
 *
 * "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2) 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 "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2).  If not, see <http://www.gnu.org/licenses/>.
 *
 * Diese Datei ist Teil von "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2).
 *
 * "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2) ist Freie Software: Sie können es unter den Bedingungen
 * der GNU General Public License, wie von der Free Software Foundation,
 * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren
 * veröffentlichten Version, weiter verteilen und/oder modifizieren.
 *
 * "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2) wird in der Hoffnung, dass es nützlich sein wird, aber
 * OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
 * Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
 * Siehe die GNU General Public License für weitere Details.
 *
 * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
 * Programm erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.
 */
package askanimus.arbeitszeiterfassung2.export;

import android.content.Context;

import androidx.annotation.NonNull;

import com.pdfjet.A3;
import com.pdfjet.A4;
import com.pdfjet.Align;
import com.pdfjet.Cell;
import com.pdfjet.CoreFont;
import com.pdfjet.Font;
import com.pdfjet.Image;
import com.pdfjet.ImageType;
import com.pdfjet.PDF;
import com.pdfjet.Page;
import com.pdfjet.Point;
import com.pdfjet.Table;
import com.pdfjet.TextBox;
import com.pdfjet.TextLine;

import java.io.File;
import java.io.FileInputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

import askanimus.arbeitszeiterfassung2.R;
import askanimus.arbeitszeiterfassung2.Uhrzeit;
import askanimus.arbeitszeiterfassung2.arbeitsplatz.Arbeitsplatz;
import askanimus.arbeitszeiterfassung2.setup.ASettings;
import askanimus.arbeitszeiterfassung2.setup.ISettings;
import askanimus.arbeitszeiterfassung2.zusatzeintrag.IZusatzfeld;

abstract class AExportBasis_PDF extends AExportBasis implements IExport_Basis_PDF {
    PDF PDFfile;

    Arbeitsplatz aktuellerArbeitsplatz;
    private  Page aktuelleSeite;
    private ArrayList<Page> seitenListe;

    private int mTabellenTyp;
    private BitSet mOptionen;
    //private boolean isMehrspaltig;
    private String mTitelEortListe;
    private String mSeitenNotiz;
    private String mSeitenTitel;


    private float mLinks;
    private float mOben;
    private float mRechts;
    private float mUnten;
    private float mSpaltenrand;
    private float mInhaltBeginn;
    private float mInhaltRandUnten;
    private float mRandUnten;
    private float mRandRechts;
    private float mMaxBreite;


    private Boolean isTitel;
    private TextLine tLinks_1 = null;
    private TextLine tLinks_2 = null;
    private TextLine tMitte_1 = null;
    private TextLine tMitte_2 = null;
    private TextLine tRechts_1 = null;
    private TextLine tRechts_2 = null;
    private TextBox tNotiz = null;


    protected static Font fTitel;
    protected static Font fKopf;
    protected static Font fWert;

    protected static int cGrau;
    protected static int cPositiv;
    protected static int cNegativ;
    protected static int cLeer;
    protected static int cSumme;
    protected static int cSamstag;
    protected static int cSonntag;

    private float fSizeTitel = 12f;
    private float fSizeWert = 8f;

    Image unterschrift = null;

    AExportBasis_PDF(Context context) {
        super(context);
    }

    void setupSeite(
            int tabellenTyp,
            BitSet optionen,
            //boolean mehrspaltig,
            String titelSeite,
            String titelEortListe,
            String notiz,
            float schriftGroesseWerte
    ) {
        mTabellenTyp = tabellenTyp;
        mOptionen = optionen;
        //isMehrspaltig = mehrspaltig;
        mTitelEortListe = titelEortListe!=null ? titelEortListe : "";
        mSeitenTitel = titelSeite!=null ? titelSeite : "";
        mSeitenNotiz = notiz!=null ? notiz : "";
        isTitel = (notiz != null);

        fSizeWert = schriftGroesseWerte;
        fSizeTitel = fSizeWert + (fSizeWert / 2);

        //Farben festlegen
        cGrau = ASettings.res.getColor(R.color.hellgrau);
        cNegativ = ASettings.res.getColor(R.color.negativ);
        cPositiv = ASettings.res.getColor(R.color.positiv);
        cLeer = 0xFEFEFE;
        cSumme = ASettings.res.getColor(R.color.summe);
        cSamstag = ASettings.res.getColor(R.color.samstag_pdf);
        cSonntag = ASettings.res.getColor(R.color.sonntag_pdf);
    }

    @Override
    public void oeffneAusgabe() throws Exception {
        PDFfile = new PDF(mDatei);

        // Schriften festlegen
        fTitel = new Font(PDFfile, CoreFont.HELVETICA_BOLD);
        fTitel.setSize(fSizeTitel);

        fKopf = new Font(PDFfile, CoreFont.HELVETICA_BOLD);
        fKopf.setSize(fSizeWert);

        fWert = new Font(PDFfile, CoreFont.HELVETICA);
        fWert.setSize(fSizeWert);

        neueSeite();
        aktuelleSeite = null;
        seitenListe.clear();
    }

    // eine Tabelle ins PDF Format übertragen
    public void schreibeSeiten() throws Exception {
        ArrayList<TabellenArbeitsplatz> mTabellen = erzeugeTabellen();

        for (TabellenArbeitsplatz tabellen : mTabellen) {
            aktuellerArbeitsplatz = tabellen.arbeitsplatz;
            // erste bzw. neue leere Seite erzeugen
            neueSeite();

            // den Seitentitel und die Seitennotiz für den Arbeitsplatz erzeugen
            erzeugeSeitenNotiz(
                    mSeitenNotiz,
                    erzeugeSeitenTitel(
                            mSeitenTitel
                    ));

            // die Tabellen erzeugen
            switch (mTabellenTyp) {
                case TABTYP_ZEITRAUM:
                    if (tabellen.zusammenfassung != null) {
                        // die Haupttabelle mit der Zusammenfassung schreiben
                        schreibeTabelleMitZusammenfassung(
                                tabellen.einzeltabelle,
                                tabellen.zusammenfassung);
                    } else {
                        // nur die Haupttabelle schreiben
                        schreibeTabelleMehrspaltig(tabellen.einzeltabelle);
                    }
                    // die Zusatztabelle mit den Zeiten eines Zeitraums nach Einsatzorten sortiert
                    if (tabellen.multitabelle != null) {
                        neueSeite();
                        erzeugeSeitenNotiz(mSeitenNotiz, erzeugeSeitenTitel(mTitelEortListe));
                        schreibeTabellenMehrspaltig(tabellen.multitabelle);
                    }
                    break;
                case TABTYP_JAHR:
                    schreibeTabellenMehrspaltig(tabellen.multitabelle);
                    break;
                case TABTYP_EORT:
                    if (tabellen.einzeltabelle != null) {
                        // nur die Liste aller Einsatzorte
                        schreibeTabelleMehrspaltig(tabellen.einzeltabelle);
                    }
                    if (tabellen.multitabelle != null && tabellen.multitabelle.size() > 0) {
                        // die Einsatzorte mit den zugehörigen Schichten
                        if (mOptionen.get(IExport_Basis.OPTION_TABELLE_JE_ORT)) {
                            for (int t = 0; t < tabellen.multitabelle.size(); t++) {
                                if(t > 0) {
                                    neueSeite();
                                }
                                Table tabelle = tabellen.multitabelle.get(t);
                                schreibeTabelleMehrspaltig(tabelle);
                            }
                        } else {
                            schreibeTabellenMehrspaltig(tabellen.multitabelle);
                        }
                    }
                    if (tabellen.zusammenfassung != null) {
                        // die Zusammenfassung
                        neueSeite();
                        erzeugeSeitenNotiz(
                                mSeitenNotiz,
                                erzeugeSeitenTitel(
                                        (ASettings.res.getString(R.string.summe_schichten) +
                                                "\n" + mSeitenTitel
                                        )));
                        schreibeTabelleMehrspaltig(tabellen.zusammenfassung);
                    }
                    break;
            }
            // die Seitennummern und Unterschriftenfelder aller erzeuigten Seiten schreiben
            schreibeFusszeilen();
        }
    }

    private Point schreibeTabelleMitZusammenfassung(Table haupt, Table neben) throws Exception {
        Point p;
        float maxFrei = getMaxBreite() - (getBeginnLinks() + haupt.getWidth() + getRandRechts() + 16f);

        neben.setPosition(getBeginnLinks() + haupt.getWidth() + 16f, mInhaltBeginn);
        neben.setBottomMargin(mInhaltRandUnten);
        int nebenSeiten = neben.getNumberOfPages(aktuelleSeite);
        int hauptSeiten = haupt.getNumberOfPages(aktuelleSeite);

        if (neben.getWidth() <= maxFrei && nebenSeiten <= hauptSeiten) {
            p= schreibeTabellenMehrseitig(haupt, neben);
        } else {
            schreibeTabelleMehrspaltig(neben);
            neueSeite();
            p = schreibeTabelleMehrspaltig(haupt);
        }
        return p;
    }

    @Override
    public void schliesseAusgabe() throws Exception {
        if(seitenListe != null) {
            for (Page p : seitenListe) {
                PDFfile.addPage(p);
            }
            PDFfile.flush();
        }
    }


    private float erzeugeSeitenTitel(String titel_mitte) {
        float hoehe_kopf = 0;
        int mUmbruch;

        String mTitelLinks = aktuellerArbeitsplatz.getName()+"\n"+ aktuellerArbeitsplatz.getAnschrift();

        String mTitelRechts =
                ASettings.mPreferenzen.getString(ISettings.KEY_USERNAME, "") + "\n" +
                        ASettings.mPreferenzen.getString(ISettings.KEY_USERANSCHRIFT, "");

        isTitel = false;

        // linke Seite Adresse des Arbeitgebers
        if(mTitelLinks.length() > 1) {
            isTitel = true;
            mUmbruch = mTitelLinks.indexOf("\n");
            tLinks_1 = new TextLine(fKopf);
            tLinks_1.setPosition(mLinks, mOben);

            if (mUmbruch < 0) {
                tLinks_1.setText(mTitelLinks);
                tLinks_2 = null;

                hoehe_kopf = tLinks_1.getHeight();
            } else {
                tLinks_1.setText(mTitelLinks.substring(0, mUmbruch));

                tLinks_2 = new TextLine(fWert);
                tLinks_2.setText(mTitelLinks.substring(mUmbruch + 1).replace("\n", "; "));
                tLinks_2.setPosition(mLinks, mOben + tLinks_1.getHeight());

                hoehe_kopf = tLinks_1.getHeight() + tLinks_2.getHeight();
            }
        }

        // rechte Seite Adresse des Arbeitnehmers
        if(mTitelRechts.length() > 1) {
            isTitel = true;
            mUmbruch = mTitelRechts.indexOf("\n");
            tRechts_1 = new TextLine(fKopf);

            if (mUmbruch < 0) {
                tRechts_1.setText(mTitelRechts);
                tRechts_1.setLocation(mRechts - tRechts_1.getWidth(), mOben);
                tRechts_2 = null;

                hoehe_kopf = Math.max(hoehe_kopf, tRechts_1.getHeight());
            } else {
                tRechts_1.setText(mTitelRechts.substring(0, mUmbruch));
                tRechts_1.setLocation(mRechts - tRechts_1.getWidth(), mOben);

                tRechts_2 = new TextLine(fWert);
                tRechts_2.setText(mTitelRechts.substring(mUmbruch + 1).replace("\n", "; "));
                tRechts_2.setPosition(mRechts - tRechts_2.getWidth(), mOben + tRechts_1.getHeight());

                hoehe_kopf = Math.max(hoehe_kopf, (tRechts_1.getHeight() + tRechts_2.getHeight()));
            }
        }

        // Mitte Berichtstitel + Datumsbereich als zweite Zeile
        if(!titel_mitte.isEmpty()) {
            isTitel = true;
            mUmbruch = titel_mitte.indexOf("\n");
            tMitte_1 = new TextLine(fTitel);

            hoehe_kopf += fTitel.getHeight();

            if (mUmbruch < 0) {
                tMitte_1.setText(titel_mitte);
                tMitte_1.setPosition((mRechts / 2) - (tMitte_1.getWidth() / 2), mOben + hoehe_kopf);
                tMitte_2 = null;

                hoehe_kopf += tMitte_1.getHeight();
            } else {
                tMitte_1.setText(titel_mitte.substring(0, mUmbruch));
                tMitte_1.setPosition((mRechts / 2) - (tMitte_1.getWidth() / 2), mOben + hoehe_kopf);
                hoehe_kopf += tMitte_1.getHeight();

                tMitte_2 = new TextLine(fKopf);
                tMitte_2.setText(titel_mitte.substring(mUmbruch + 1));
                tMitte_2.setPosition((mRechts / 2) - (tMitte_2.getWidth() / 2), mOben + hoehe_kopf);
                hoehe_kopf += tMitte_2.getHeight();//+ fWert.getHeight();
            }
        }


        // Beschreibbaren Seitenbeginn verkleinern
        mInhaltBeginn = mOben + hoehe_kopf;

        return mInhaltBeginn;
    }

    // erzeugt einen Notizzeile die unter den Seitentitel gedruckt wird
    private void erzeugeSeitenNotiz(String notiz, float position) throws Exception {
        if(!notiz.isEmpty()){
            tNotiz = new TextBox(fWert);
            tNotiz.setLocation(mLinks, position);
            tNotiz.setWidth(mMaxBreite);
            tNotiz.setNoBorders();
            tNotiz.setText(notiz);
            tNotiz.drawOn(aktuelleSeite, false);

            mInhaltBeginn = position + tNotiz.getHeight();
        } else {
            tNotiz = null;
        }
    }

    // Überschrift drucken
    private void schreibeSeitenTitel() throws Exception {
        if(isTitel) {
            if(tLinks_1 != null)
                tLinks_1.drawOn(aktuelleSeite);
            if(tLinks_2 != null)
                tLinks_2.drawOn(aktuelleSeite);
            if(tMitte_1 != null)
                tMitte_1.drawOn(aktuelleSeite);
            if(tMitte_2 != null)
                tMitte_2.drawOn(aktuelleSeite);
            if(tRechts_1 != null)
                tRechts_1.drawOn(aktuelleSeite);
            if(tRechts_2 != null)
                tRechts_2.drawOn(aktuelleSeite);
        }
    }

    // Seitennotiz drucken
    private void schreibeSeitenNotiz() throws Exception {
        if(tNotiz != null)
            tNotiz.drawOn(aktuelleSeite);
    }


    // Unterschriftenfelder am unteren Ende der Seite anfügen
    private void schreibeUnterschrift(boolean anRechts) throws Exception {
        if (mOptionen.get(OPTION_ZEILE_UNTERSCHRIFT)) {
            float mTextStart;
            TextLine mText = new TextLine(fKopf);
            float xAN = mRechts - fKopf.stringWidth(UNTERSCHRIFT_LINIE);
            float xAG = mLinks;
            float y = mUnten;

            // Unterschrift Untertitel  Arbeitnehmer zeichnen (rechts)
            mText.setText(aktuellerArbeitsplatz.getUnterschrift_AN());
            mText.setPosition(xAN, y);
            mText.drawOn(aktuelleSeite);

            // Unterschrift Untertitel  Arbeitgeber zeichnen (links)
            mText.setText(aktuellerArbeitsplatz.getUnterschrift_AG());
            mText.setPosition(xAG, y);
            mText.drawOn(aktuelleSeite);

            // Unterschrift Linie rechts zeichnen
            y -= fKopf.getHeight();
            mText.setText(UNTERSCHRIFT_LINIE);
            //mTextStart = mRechts - mText.getWidth();
            mText.setPosition(xAN/*mTextStart*/, y);
            mText.drawOn(aktuelleSeite);

            // Unterschrift Linie links zeichnen
            mText.setPosition(xAG/*mTextStart*/, y);
            mText.drawOn(aktuelleSeite);

            // Datum des Erstellens auf der Arbeitnehmerseite eintragen
            float datumLang;
            if(mOptionen.get(IExport_Basis.OPTION_DATUM)) {
                TextLine mDatum = new TextLine(fWert);
                mDatum.setText(ASettings.aktDatum.getString_Datum(mContext));
                mDatum.setPosition(xAN, y - 4f);
                mDatum.drawOn(aktuelleSeite);
                datumLang = fKopf.stringWidth(mDatum.getText()+"  ");
            } else {
                datumLang = 0;
            }

            // Bild der Unterschrift des Arbeitnehmers einsetzen
            if(unterschrift != null){
                unterschrift.setPosition(xAN + datumLang, y - unterschrift.getHeight());
                unterschrift.drawOn(aktuelleSeite);
            }
        }
    }



    // Seitennummer und Anzahl Seiten in Form xx von xx
    private void schreibeSeitenzahl(int seite, int von) throws Exception {
        TextLine numPag = new TextLine(fWert, String.format(ASettings.res.getString(R.string.seiten_nummer), seite, von));
        numPag.setPosition((mRechts / 2) - (numPag.getWidth() / 2), aktuelleSeite.getHeight() - 16.0f);
        numPag.drawOn(aktuelleSeite);
    }

    private void schreibeKopfzeilen() throws Exception{
        schreibeSeitenTitel();
        schreibeSeitenNotiz();
    }

    private void schreibeFusszeilen() throws Exception{
        int maxSeiten = seitenListe.size();
        for (int s = 0; s < maxSeiten; s++) {
            aktuelleSeite = seitenListe.get(s);
            schreibeUnterschrift(false);
            schreibeSeitenzahl(s+1,maxSeiten);
        }
    }

    // Eine Tabelle aus den Tabellendaten erzeugen
    // dabei vorhandene Notizspalten auf die komplette Seitenbreite aufteilen, wenn gewünscht
    protected Table makeTabelle(
            List<List<Cell>> tableData,
            int Kopfzeilen,
            boolean trimText,
            ArrayList<Spalte> spaltensatz) throws Exception {
        class textSpalte {
            int spalteNummer;
            float minBreiteText;
            float maxBreiteText;
        }

        ArrayList<textSpalte> mTextSpalten = new ArrayList<>();
        Table mTabelle = new Table();
        appendMissingCells(tableData, fWert);
        mTabelle.setData(tableData, Kopfzeilen);
        mTabelle.setCellBordersWidth(0.2f);
        mTabelle.rightAlignNumbers();
        mTabelle.autoAdjustColumnWidths();

        // Spaltenbreiten leicht erweitern
        int colZahl = mTabelle.getRow(0).size();
        for (int c = 0; c < colZahl; c++) {
            mTabelle.setColumnWidth(c, mTabelle.getColumnWidth(c) + 4f);
        }

        // Textspalten finden und maximieren
        if(trimText) {
            // Anzahl der Textspalten ermitteln
            // und den maximal zur Verfügung stehenden Platz für die Notizspalten auf der Seite errechnen

            // die Tabellenbreite kann zu diesen Zeitpunkt breiter als die Seitenbreite sein
            // dadurch wird mMaxBreite negativ
            float mMaxBreite = getMaxBreite() - mTabelle.getWidth();// - getBeginnLinks();
            if (mMaxBreite < 0) {
                int s = 0;
                for (Spalte spalte : spaltensatz) {
                    if (spalte.isText) {
                        textSpalte ts = new textSpalte();
                        ts.spalteNummer = s;
                        ts.maxBreiteText = mTabelle.getColumnWidth(s);
                        if (spalte.mSpalte == SPALTE_DATUM) {
                            ts.minBreiteText = fWert.stringWidth("00.00.2000");
                        } else if (spalte.mSpalte == SPALTE_ANGELEGT || spalte.mSpalte == SPALTE_AENDERUNG) {
                            ts.minBreiteText = fWert.stringWidth("00.00.00");
                        } else {
                            ts.minBreiteText = -1;
                        }
                        mTextSpalten.add(ts);
                        mMaxBreite += ts.maxBreiteText;
                    }
                    s++;
                }
                // Breite für jede Textspalte berrechnen und alle Textspalten auf den vorhandnen Platz
                // aufteilen
                float mTeilBreite = mMaxBreite / mTextSpalten.size();
                // die Breite, die jeder Spalte bleibt anpassen
                for (textSpalte ts : mTextSpalten) {
                    if (ts.minBreiteText > 0) {
                        if (ts.minBreiteText != mTeilBreite) {
                            if(ts.maxBreiteText > mTeilBreite){
                                ts.maxBreiteText = ts.minBreiteText;
                            }
                            mTeilBreite += (mTeilBreite - ts.minBreiteText) / (mTextSpalten.size() );
                        }
                    } else if (ts.maxBreiteText < mTeilBreite) {
                        mTeilBreite += (mTeilBreite - ts.maxBreiteText) / (mTextSpalten.size() );
                    }
                }
                // die Spalten verbreitern, kürzen
                for (textSpalte ts : mTextSpalten) {
                    mTabelle.setColumnWidth(
                            ts.spalteNummer,
                            (ts.maxBreiteText < mTeilBreite) ? ts.maxBreiteText + 4f : mTeilBreite);
                }
            }
        }

        // zu lange Notizen umbrechen
        mTabelle.wrapAroundCellText();

        return mTabelle;
    }

     // Spalten am Zeilenende auffüllen
    public void appendMissingCells(List<List<Cell>> tableData, Font f2) {
        //if(tableData.size() > 0) {
            List<Cell> firstRow = tableData.get(0);
            int numOfColumns = firstRow.size();
            for (int i = 0; i < tableData.size(); i++) {
                List<Cell> dataRow = tableData.get(i);
                int dataRowColumns = dataRow.size();
                if (dataRowColumns < numOfColumns) {
                    for (int j = 0; j < (numOfColumns - dataRowColumns); j++) {
                        dataRow.add(new Cell(f2, " "));
                    }
                    //dataRow.get(dataRowColumns - 1).setColSpan((numOfColumns - dataRowColumns) + 1);
                }
            }
        //}
    }


    Point schreibeTabelleMehrseitig(Table tabelle) throws Exception{
        Point neachstePosition;
        tabelle.setPosition(mLinks, mInhaltBeginn);
        tabelle.setBottomMargin(mInhaltRandUnten);

        while (true) {

            // Kopzeile drucken
            schreibeKopfzeilen();

            // Tabelle zeichnen oder der Teil der auf die Seite passt
            neachstePosition = tabelle.drawOn(aktuelleSeite);

            if (!tabelle.hasMoreData()) {
                // Tabelle erlauben sich später zu zeichnen
                tabelle.resetRenderedPagesCount();
                break;
            }
        }
    return neachstePosition;
    }


    private Point schreibeTabellenMehrseitig(Table tabelle, Table zusammen) throws Exception{
        Point neachstePosition = new Point();
        tabelle.setPosition(mLinks, mInhaltBeginn);
        tabelle.setBottomMargin(mInhaltRandUnten);

        zusammen.setPosition(getBeginnLinks() + tabelle.getWidth() + 16f, mInhaltBeginn);
        zusammen.setBottomMargin(mInhaltRandUnten);

        neachstePosition.setPosition(getBeginnLinks() + tabelle.getWidth() + 16f, mInhaltBeginn);

        int seitenTabelle = tabelle.getNumberOfPages(aktuelleSeite);
        int seitenZusammenfassung = zusammen.getNumberOfPages(aktuelleSeite);
        int numOfPages = Math.max(seitenTabelle, seitenZusammenfassung);
        int currentPage = 1;

        while (currentPage <= numOfPages) {

            // Kopzeile drucken
            schreibeKopfzeilen();

            // Tabelle zeichnen oder der Teil der auf die Seite passt
           if (tabelle.hasMoreData())
                neachstePosition = tabelle.drawOn(aktuelleSeite);
            if (zusammen.hasMoreData())
                neachstePosition = zusammen.drawOn(aktuelleSeite);

            //Zeile mit der Seiten Nummer
            if(currentPage < numOfPages)
                neueSeite();

            currentPage++;
        }
        tabelle.resetRenderedPagesCount();
        zusammen.resetRenderedPagesCount();

        return neachstePosition;
    }

    Point schreibeTabelleMehrspaltig(@NonNull Table tabelle)throws Exception {
        Point neachstePosition;
        tabelle.setBottomMargin(mInhaltRandUnten);
        float columnBreite = tabelle.getWidth() + mSpaltenrand;
        float columnPosLeft = mLinks;

        while (true) {
            tabelle.setPosition(columnPosLeft, mInhaltBeginn);

            // Kopzeile drucken
            schreibeKopfzeilen();

            // Tabelle zeichnen oder der Teil der auf die Seite passt
            neachstePosition = tabelle.drawOn(aktuelleSeite);

            if (!tabelle.hasMoreData()) {
                // Tabelle erlauben sich später zu zeichnen
                tabelle.resetRenderedPagesCount();
                break;
            }

            columnPosLeft += columnBreite;

            if (columnPosLeft + columnBreite > getMaxBreite()) {
                neueSeite();
                columnPosLeft = mLinks;
                //currentPage++;
            }
        }
        return neachstePosition;
    }

    private Point schreibeTabellenMehrspaltig(@NonNull ArrayList<Table> tabellen)throws Exception {
        Point neachstePosition = new Point();
        float columnPosLeft = mLinks;
        float columPosTop = mInhaltBeginn;

        float columnBreite = 0;

        boolean neueSeite = false;

        for (Table tabelle : tabellen) {
            columnBreite = Math.max(tabelle.getWidth(), columnBreite);
        }
        columnBreite += mSpaltenrand;
        neachstePosition.setPosition(columnPosLeft, columPosTop);

        for (Table tabelle : tabellen) {
            if (neueSeite){
                neueSeite();
                neueSeite = false;
            }
            tabelle.setBottomMargin(mInhaltRandUnten);
            while (true) {
                tabelle.setPosition(columnPosLeft, columPosTop);

                // Kopzeile drucken
                schreibeKopfzeilen();

                // Tabelle zeichnen oder der Teil der auf die Seite passt
                neachstePosition = tabelle.drawOn(aktuelleSeite);

                if (tabelle.hasMoreData()) {
                    // neue Spalte
                    columnPosLeft += columnBreite;
                    columPosTop = mInhaltBeginn;

                    if (columnPosLeft + columnBreite > getMaxBreite()) {
                        neueSeite();
                        columnPosLeft = mLinks;
                    }

                } else {
                    columPosTop = neachstePosition.getY() + 8f;
                    if (columPosTop >= aktuelleSeite.getHeight() - mInhaltRandUnten - (4 * fKopf.getHeight())) {
                        // neue Spalte
                        columnPosLeft += columnBreite;
                        columPosTop = mInhaltBeginn;

                        if (columnPosLeft + columnBreite > getMaxBreite()) {
                            neueSeite = true;
                            columnPosLeft = mLinks;
                        }
                    }
                    tabelle.resetRenderedPagesCount();
                    break;
                }
            }
        }
        return neachstePosition;
    }

    // neue Seite
    void neueSeite() throws Exception {
        Page p ;

        if (mOptionen.get(OPTION_LAYOUT_A3)) {
            p = new Page(
                    PDFfile,
                    mOptionen.get(OPTION_LAYOUT_QUEER) ? A3.LANDSCAPE : A3.PORTRAIT,
                    false
            );
        } else {
            p = new Page(
                    PDFfile,
                    mOptionen.get(OPTION_LAYOUT_QUEER) ? A4.LANDSCAPE : A4.PORTRAIT,
                    false
            );
        }

        // wenn die erste Seite angelegt wird
        if (seitenListe == null) {
            // Seitenliste initialisieren
            seitenListe = new ArrayList<>();

            // Ränder und Inhaltefenster festlegen
            mRandRechts = 24.0f;
            mRandUnten = mRandRechts;
            mSpaltenrand = mRandRechts;

            mLinks = 48.0f;
            mOben = 24.0f;
            mRechts = p.getWidth() - mRandRechts;
            mUnten = p.getHeight() - mRandUnten;
            if (mOptionen.get(OPTION_ZEILE_UNTERSCHRIFT)) {
                mInhaltRandUnten = mRandUnten + (fWert.getHeight() * 2);
                File file = new File(mContext.getFilesDir(), "UnterschriftAN.jpg");
                if (file.exists()) {
                    FileInputStream isUnterschrift = mContext.openFileInput("UnterschriftAN.jpg");
                    unterschrift = new Image(PDFfile, isUnterschrift, ImageType.JPG);
                    unterschrift.scaleBy(UNTERSCHRIFT_W / unterschrift.getWidth());
                    mInhaltRandUnten += unterschrift.getHeight();
                    isUnterschrift.close();
                }
            } else {
                mInhaltRandUnten = mRandUnten;
            }
            mMaxBreite = mRechts - mLinks;
        }

        // die erzeugte Seite in die Seitenliste einfügen
        seitenListe.add(p);
        aktuelleSeite = p;
    }

    // eine Zeile erzeugen die einen, nicht in der Tabelle angezeigten, Summenwert enthält
    public ArrayList<Cell> addSummeZeile(int posTitel, int posMax, String titel, int wert, boolean isDezimal, boolean isSaldo) {
        Uhrzeit mWert = new Uhrzeit(wert);
        int zSpalte = 0;
        ArrayList<Cell> row = new ArrayList<>();

        if(posTitel < 0){
            String s = titel + ": " + mWert.getStundenString(true, isDezimal);
            row.add(makeZelleString(s,false));
            row.get(0).setTextAlignment(Align.RIGHT);
            if(isSaldo) {
                row.get(0).setBgColor(cSumme);
                row.get(0).setUnderline(true);
                if(wert < 0) {
                    row.get(0).setBrushColor(cNegativ);
                } else if(wert > 0) {
                    row.get(0).setBrushColor(cPositiv);
                }
            }
        } else {
            // Titel zeichen und nach rechts verschieben
            row.add(makeZelleKopf(titel));
            row.get(zSpalte).setNoBorders();
            while (zSpalte < posTitel) {
                row.add(makeZelleLeer(true));
                zSpalte++;
            }
            zSpalte++;
            row.get(0).setColSpan(zSpalte);
            row.get(0).setTextAlignment(Align.RIGHT);
            row.get(0).setRightPadding(4f);

            // leere Spalten einfügen
            posTitel++;
            for (int i = zSpalte; i < posTitel; i++) {
                zSpalte++;
                row.add(makeZelleLeer(true));
            }

            // Wert Spalte einfügen
            row.add(makeZelleSummeStunden(mWert.getAlsMinuten(), isDezimal, false, isSaldo));

            // rechts mit unsichtbaren Spalten auffüllen
            for (zSpalte = zSpalte + 1; zSpalte < posMax; zSpalte++) {
                row.add(makeZelleLeer(true));
            }

        }
        return row;
    }


    /*
     * Wertrückgaben
     */
    float getBeginnLinks(){
        return mLinks;
    }

    float getMaxBreite(){
        return mMaxBreite;
    }

    float getBeginnOben(){
        return mInhaltBeginn;
    }

    float getMaxHoch(){
        return aktuelleSeite.getHeight() - mRandUnten;
    }

    float getRandRechts(){
        return mRandRechts;
    }




    @Override
    public Cell makeZelleLeer(boolean noborder) {
        Cell c = new Cell(fWert);
        if(noborder){
            c.setNoBorders();
        }
        return c;
    }

    @Override
    public Cell makeZelleKopf(String text) {
        Cell c = new Cell(fKopf, text);
        c.setTextAlignment(Align.CENTER);
        c.setBgColor(cLeer);
        return c;
    }

    @Override
    public Cell makeZelleBezeichnung(String text){
        Cell c = new Cell(fKopf, text);
        c.setBgColor(cLeer);
        return c;
    }

    @Override
    public Cell makeZelleString(String text, boolean farbeHintergrund) {
        Cell c = new Cell(fWert, text);

        c.setBgColor(farbeHintergrund?cGrau:cLeer);
        c.setTextAlignment(Align.LEFT);
        return c;
    }

    @Override
    public Cell makeZelleWert(float wert, DecimalFormat formater, boolean farbeHintergrund) {
        Cell c = new Cell(fWert, formater.format(wert));

        c.setBgColor(farbeHintergrund?cGrau:cLeer);
        c.setTextAlignment(Align.RIGHT);
        if (wert < 0) {
            c.setBrushColor(cNegativ);
        }
        return c;
    }

    @Override
    public Cell makeZelleStunden(int wert, boolean isDezimal, boolean einheit, boolean farbeHintergrund) {
        Uhrzeit uz = new Uhrzeit(wert);
        Cell c = new Cell(fWert, uz.getStundenString(einheit, isDezimal));

        c.setBgColor(farbeHintergrund?cGrau:cLeer);
        c.setTextAlignment(Align.RIGHT);
        if (wert < 0) {
            c.setBrushColor(cNegativ);
        }
        return c;
    }

    @Override
    public Cell makeZelleUhrzeit(int wert, boolean farbeHintergrund) {
        Uhrzeit uz = new Uhrzeit(wert);
        Cell c = new Cell(fWert, uz.getUhrzeitString());

        c.setBgColor(farbeHintergrund?cGrau:cLeer);
        c.setTextAlignment(Align.RIGHT);
        return c;
    }

    @Override
    public Cell makeZelleSummeWert(float wert, DecimalFormat formater, boolean isSaldo) {
        Cell c = new Cell(fWert, formater.format(wert));
        c.setBgColor(cSumme);
        if(wert < 0){
            c.setBrushColor(cNegativ);
        } else if(wert > 0) {
            c.setBrushColor(cPositiv);
        }
        c.setUnderline(isSaldo);
        c.setTextAlignment(Align.RIGHT);
        return c;
    }

    @Override
    public Cell makeZelleSummeStunden(int wert, boolean isDezimal, boolean einheit, boolean isSaldo) {
        Uhrzeit uz = new Uhrzeit(wert);
        Cell c = new Cell(fWert, uz.getStundenString(einheit, isDezimal));
        c.setBgColor(cSumme);
        if(wert < 0){
            c.setBrushColor(cNegativ);
        } else if(wert > 0){
            c.setBrushColor(cPositiv);
        }
        if (isSaldo) {
            c.setUnderline(true);
        }
        c.setTextAlignment(Align.RIGHT);
        return c;
    }

    @Override
    public ArrayList<Cell> makeZusammenfassungTitel(String text, Boolean isFirst) {
        ArrayList<Cell> row = new ArrayList<>();
        row.add(new Cell(fTitel, text));
        row.add(new Cell(fTitel));
        row.add(new Cell(fTitel));
        row.get(0).setColSpan(3);
        if(!isFirst) {
            row.get(0).setTopPadding(16f);
        }
        row.get(0).setBottomPadding(4f);

        return row;
    }

    @Override
    public Cell makeZelleZusatzwert(IZusatzfeld zfeld, boolean isDezimal, boolean farbeHintergrund, boolean inklNotiz){
        Cell zelle;
        boolean notNull;
        switch (zfeld.getDatenTyp()) {
            case IZusatzfeld.TYP_BEREICH_ZAHL:
            case IZusatzfeld.TYP_ZAHL:
            case IZusatzfeld.TYP_AUSWAHL_ZAHL:
                notNull = (float) zfeld.getWert() > 0;
                zelle = makeZelleWert(
                        (float) zfeld.getWert(),
                        zfeld.getFormater(),
                        farbeHintergrund);
                break;
            case IZusatzfeld.TYP_PROZENT:
            case IZusatzfeld.TYP_AUSWAHL_PROZENT:
                notNull = (float) zfeld.getWert() > 0;
                zelle = makeZelleWert(
                        (float) zfeld.getWert()*100,
                        zfeld.getFormater(),
                        farbeHintergrund);
                break;
            case IZusatzfeld.TYP_BEREICH_ZEIT:
            case IZusatzfeld.TYP_ZEIT:
            case IZusatzfeld.TYP_AUSWAHL_ZEIT:
                notNull = (int) zfeld.getWert() > 0;
                zelle = makeZelleStunden((int) zfeld.getWert(), isDezimal, true, farbeHintergrund);
                break;
            default:
                notNull = false;
                if(inklNotiz) {
                    zelle = makeZelleString(zfeld.getString(false), farbeHintergrund);
                } else {
                    zelle = makeZelleLeer(true);
                }
        }
        if (
                notNull &&
                (
                        zfeld.getWirkung() == IZusatzfeld.SUB_ISTSTUNDEN ||
                        zfeld.getWirkung() == IZusatzfeld.SUB_VERDIENST
                )
        ){
            zelle.setFgColor(cNegativ);
            zelle.setText("-"+zelle.getText());
        }
        return zelle;
    }

    @Override
    public Cell makeZelleSummeZusatzwert(IZusatzfeld zfeld, boolean isDezimal) {
        Cell zelle;

        if (zfeld != null) {
            switch (zfeld.getDatenTyp()) {
                case IZusatzfeld.TYP_BEREICH_ZAHL:
                case IZusatzfeld.TYP_ZAHL:
                case IZusatzfeld.TYP_AUSWAHL_ZAHL:
                    zelle = makeZelleSummeWert(
                            (float) zfeld.getWert(),
                            zfeld.getFormater(),
                            true);
                    break;
                case IZusatzfeld.TYP_BEREICH_ZEIT:
                case IZusatzfeld.TYP_ZEIT:
                case IZusatzfeld.TYP_AUSWAHL_ZEIT:
                    zelle = makeZelleSummeStunden(
                            (int) zfeld.getWert(),
                            isDezimal,
                            true,
                            true);
                    break;
                default:
                    zelle = makeZelleLeer(false);
                    zelle.setBgColor(cSumme);
            }
        } else {
            zelle = makeZelleLeer(false);
            zelle.setBgColor(cSumme);
        }
        return zelle;
    }

    @Override
    public void makeLeerzeile(List<List<Cell>> tabeldata, int spalten, boolean noborder){
        ArrayList<Cell> row = new ArrayList<>();

        while (row.size() < spalten) {
            row.add(makeZelleLeer(noborder));
        }
        tabeldata.add(row);
    }

    @Override
    public void makeTitel(List<List<Cell>> tabeldata, String titel, int spalten){
        ArrayList<Cell> row = new ArrayList<>();

        // Überschrifft
        row.add(makeZelleKopf(titel));
        while (row.size() < spalten) {
            row.add(makeZelleLeer(true));
        }
        row.get(0).setColSpan(row.size());
        row.get(0).setTextAlignment(Align.CENTER);
        row.get(0).setNoBorders();

        tabeldata.add(row);
    }
}
