/* 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.arbeitsmonat;

import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.database.Cursor;
import android.util.Log;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;

import askanimus.arbeitszeiterfassung2.Datum;
import askanimus.arbeitszeiterfassung2.Uhrzeit;
import askanimus.arbeitszeiterfassung2.abwesenheiten.Abwesenheit;
import askanimus.arbeitszeiterfassung2.arbeitsplatz.Arbeitsplatz;
import askanimus.arbeitszeiterfassung2.arbeitsschicht.Arbeitsschicht;
import askanimus.arbeitszeiterfassung2.arbeitstag.Arbeitstag;
import askanimus.arbeitszeiterfassung2.datenbank.DatenbankHelper;
import askanimus.arbeitszeiterfassung2.setup.ASettings;
import askanimus.arbeitszeiterfassung2.zusatzeintrag.IZusatzfeld;
import askanimus.arbeitszeiterfassung2.zusatzeintrag.ZusatzWertListe;

/**
 * @author askanimus@gmail.com on 17.04.14.
 */
public class Arbeitsmonat {
    // SQL Befehle
    // Monate lesen
    private final static String SQL_READ_MONAT =
            "select * from " +
                    DatenbankHelper.DB_T_MONAT +
                    " where " +
                    DatenbankHelper.DB_F_JOB +
                    " = ? AND " +
                    DatenbankHelper.DB_F_JAHR +
                    " = ? AND " +
                    DatenbankHelper.DB_F_MONAT +
                    " = ? LIMIT 1";

    private final static String SQL_READ_SALDO_VORMONAT =
            "select " +
                    DatenbankHelper.DB_F_SALDO_H +
                    " from " +
                    DatenbankHelper.DB_T_MONAT +
                    " where " +
                    DatenbankHelper.DB_F_JOB +
                    " = ? AND " +
                    DatenbankHelper.DB_F_JAHR +
                    " = ? AND " +
                    DatenbankHelper.DB_F_MONAT +
                    " = ? LIMIT 1";

    // Variablen des Monats
    private long MonatId = -1;      // ID des Monats in der datenbank
    //private long mArbeitsplatzId = 1;         // die ID des Arbeitsplatzes zu der dieser Monat gehört
    private final Arbeitsplatz mArbeitsplatz;
    private final int mJahr;
    private final int mMonat;
    private int mSollNetto = 0;            // Monatliche Sollstunden - entschuldigte Abwesenheit(Urlaub, Krank, Militär usw.) in Minuten
    private int mIstNetto = 0;             // Arbeitstunden, Schule und Bezahlte Freistellung in MInuten
    private int mSaldo = 0;           // Ist-Soll+Saldo_Vormonat-ausbezahlte Überstunden (Wird so in der datenbank gespeichert) in Minuten
    //private int Saldo_Aktuell = 0;   // der Saldo am aktuellen Tag, wird nicht mitgespeichert
    private int mAuszahlung = -1;      // Ausgezahlte Überstunden in Minuten -1 = noch kein Wert festgelegt
    private int mAuszahlung_auto = 0;   // Menge autom. ausbezahlter Überstunden
    private int Soll_Manuell = -1;   // für jeden Monat vergebbare Sollzeit, überschreibt die Arbeitsplatzeinstellungen

    private int mSaldoVormonat = 0;  // Saldo des Vormonats, wird nicht mit gespeichert, dient nur zur Berechnung in Minuten

    private final Datum mStartDatum;     // Datum des ersten Tages im Abrechnungsmonat
    private final Datum datEnde;      // Datum des letzten Tages des Abrechnungsmonats

    // Die Arbeitstage des Monats
    // wenn Monatsbegin >1 dann Monatsübergreifend
    private final ArrayList<Arbeitstag> mArbeitstage = new ArrayList<>();

    // zeigt die Position eines Tages im Abrechnungsmonat, d.h. Monatstag 1 des Folgemonats ist erster Eintrag in der Liste
    private final int[] mIndexListeTag = new int[32];

    private final boolean mUpdateFolgemonate;

    /**
     * Erzeugt ein Arbeitsmonat Objekt und füllt es mit den Daten des übergebenen Moants aus der Datenbank
     * oder legt einen neuen Monat an
     *
     * @param arbeitsplatz      der Arbeitsplatz zu dem dieser Monat gehört
     * @param jahr              das Jahr
     * @param monat             der Monat
     * @param leseTage          sollen die Tage mit gelesen/erzeugt werden oder nur der Rumpfmonat
     * @param updateFolgemonate sollen nach Änderungen des Monatssaldos die Folgemonate angepasst werden?
     */
    public Arbeitsmonat(Arbeitsplatz arbeitsplatz, int jahr, int monat, boolean leseTage, boolean updateFolgemonate) {
        mJahr = jahr;
        mMonat = monat;
        mArbeitsplatz = arbeitsplatz;
        mUpdateFolgemonate = updateFolgemonate;

        // Start- und Enddatum des Monats ermitteln
        mStartDatum = new Datum(jahr, monat, mArbeitsplatz.getMonatsbeginn(), mArbeitsplatz.getWochenbeginn());
        datEnde = new Datum(mStartDatum.getTimeInMillis(), mArbeitsplatz.getWochenbeginn());
        datEnde.add(Calendar.DAY_OF_MONTH, mStartDatum.getAktuellMaximum(Calendar.DAY_OF_MONTH) - 1);

        // Starttag im ersten Monat der Aufzeichnungen verschieben
        if (mStartDatum.liegtVor(mArbeitsplatz.getStartDatum()))
            mStartDatum.set(mArbeitsplatz.getStartDatum().getDate());

        // den Endtag im letzten Monat verschieben
        if (datEnde.liegtNach(ASettings.letzterAnzeigeTag)) {
            datEnde.set(ASettings.letzterAnzeigeTag.getCalendar());
        }

        // Stammdaten des Monats einlesen oder anlegen
        if (!datEnde.liegtVor(mStartDatum)) {
            leseMonat();

            // zugehörige Tage lesen oder leere anlegen
            if (leseTage) {
                leseTage();
            }

            // Saldo auf den neuesten Stand bringen
            updateSaldo(leseTage);


        } else {
            if (leseTage) {
                Arrays.fill(mIndexListeTag, -1);
            }
        }
    }


    /**
     * die Daten des Monats aus datenbank lesen
     */
    @SuppressLint("Range")
    private void leseMonat() {
        // SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getReadableDatabase();

        // die Werte des gesuchten Monats aus der Datenbank lesen
        Cursor result = ASettings.mDatenbank.rawQuery(SQL_READ_MONAT, new String[]{
                Long.toString(mArbeitsplatz.getId()),
                Integer.toString(mJahr),
                Integer.toString(mMonat)
        });

        // Resultat der Anfrage auswerten
        if (result.moveToFirst()) {
            // Daten des Monats übernehmen
            MonatId = result.getLong(result.getColumnIndex(DatenbankHelper.DB_F_ID));
            mSollNetto = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_SOLL_H));
            Soll_Manuell = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_SOLL_MANUELL));
            mIstNetto = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_IST_H));
            mSaldo = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_SALDO_H));
            mAuszahlung = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_AUSZAHLUNG));
        } else {
            // der Monat ist noch nicht in der Datenbank gespeichert, also neu anlegen
            mSollNetto = berechne_Sollstunden(false);
            // updateSaldo(false);
        }
        result.close();

        // Saldo des Vormonats ermitteln und einsetzen
        if (mArbeitsplatz.isIgnoreSaldoAll()) {
            mSaldoVormonat = 0;
        } else {
            if (mStartDatum.istGleich(mArbeitsplatz.getStartDatum())) {
                // es ist der erste Monat der Aufzeichnungen
                setSaldoVormonat(mArbeitsplatz.getStartsaldo());
            } else {
                // es ist Monat 2...n der Aufzeichnungen
                Datum dVormonat = new Datum(mStartDatum.getTimeInMillis(), mArbeitsplatz.getWochenbeginn());
                dVormonat.add(Calendar.MONTH, -1);

                Cursor resultVormonat = ASettings.mDatenbank.rawQuery(SQL_READ_SALDO_VORMONAT, new String[]{
                        Long.toString(mArbeitsplatz.getId()),
                        Integer.toString(dVormonat.get(Calendar.YEAR)),
                        Integer.toString(dVormonat.get(Calendar.MONTH))
                });

                if (resultVormonat.getCount() > 0) {
                    resultVormonat.moveToFirst();
                    int saldoVormonat  = resultVormonat.getInt(resultVormonat.getColumnIndex(DatenbankHelper.DB_F_SALDO_H));
                    setSaldoVormonat(saldoVormonat);
                    //mSaldoVormonat = resultVormonat.getInt(resultVormonat.getColumnIndex(DatenbankHelper.DB_F_SALDO_H));
                } else {
                    // kein Vormonat gefunden, also den Startsaldo-Wert aus den
                    // Arbeitsplatzeinstellungen nehmen.
                    // Ist redundant, aber sicher ist sicher
                    setSaldoVormonat(mArbeitsplatz.getStartsaldo());
                    // mSaldoVormonat = mArbeitsplatz.getStartsaldo();
                }
                resultVormonat.close();
            }
        }
    }

    /**
     * die Daten der Arbeitstage einlesen und nicht gespeicherte leere Tage erzeugen
     */
    private void leseTage() {
        int mTag = 0;
        Datum mTagDatum = new Datum(mStartDatum);
        int soll_TagPauschal = mArbeitsplatz.getSollstundenTagPauschal(mJahr, mMonat);

        Arrays.fill(mIndexListeTag, -1);
        // alle vorhandenen Tage anlegen
        do {
            mArbeitstage.add(new Arbeitstag(
                    mTagDatum.getCalendar(),
                    mArbeitsplatz,
                    mArbeitsplatz.getSollstundenTag(mTagDatum),
                    soll_TagPauschal
            ));
            mIndexListeTag[mTagDatum.get(Calendar.DAY_OF_MONTH)] = mTag;
            mTagDatum.add(Calendar.DAY_OF_MONTH, 1);
            mTag++;
        } while (!mTagDatum.liegtNach(datEnde));
    }

    /**
     * Den Monat in die Datenbank schreiben, Die Arbeitstage speichern sich bei Änderung automatisch
     */
    private void speichern() {
        // SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getWritableDatabase();
        ContentValues werte = new ContentValues();

        werte.put(DatenbankHelper.DB_F_JOB, mArbeitsplatz.getId());
        werte.put(DatenbankHelper.DB_F_JAHR, mJahr);
        werte.put(DatenbankHelper.DB_F_MONAT, mMonat);
        werte.put(DatenbankHelper.DB_F_IST_H, mIstNetto);
        werte.put(DatenbankHelper.DB_F_SOLL_H, mSollNetto);
        werte.put(DatenbankHelper.DB_F_SALDO_H, mSaldo);
        werte.put(DatenbankHelper.DB_F_AUSZAHLUNG, mAuszahlung);
        werte.put(DatenbankHelper.DB_F_SOLL_MANUELL, Soll_Manuell);

        if (MonatId < 0)
            MonatId = ASettings.mDatenbank.insert(DatenbankHelper.DB_T_MONAT, null, werte);
        else
            // Bestehenden updaten
            ASettings.mDatenbank.update(
                    DatenbankHelper.DB_T_MONAT,
                    werte,
                    DatenbankHelper.DB_F_ID + " = ?",
                    new String[]{Long.toString(MonatId)}
            );
        // mDatenbank.close();
    }

    /**
     * diesen Monat aus der Datenbank löschen
     */
    public void loeschen() {
        // alle Schichten des Monats löschen
        for (int i = 0; i < mArbeitstage.size(); i++) {
            if (mArbeitstage.get(i).getTagId() > 0)
                mArbeitstage.get(i).loeschen();
        }
        // dann den Monat

        if (MonatId > 0) {
            // SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getWritableDatabase();
            ASettings.mDatenbank.delete(DatenbankHelper.DB_T_MONAT, DatenbankHelper.DB_F_ID + " = ?", new String[]{Long.toString(MonatId)});

            // mDatenbank.close();
        }
    }

    /**
     * neuen Saldo vom Vormonat setzen
     *
     * @param saldoVormonat neuer Saldo des Vormonats
     */
    public void setSaldoVormonat(int saldoVormonat) {
        // wenn der positive oder negative Saldo des Vormonats ignoriert werden soll (Einstellungen)
        if(mArbeitsplatz.isIgnoreSaldo() && saldoVormonat != 0){
            Log.d("#################", "all");
            if(mArbeitsplatz.isIgnoreSaldoMinus() && saldoVormonat < 0){
            Log.d("#################", "minus");
                saldoVormonat = 0;
            } else if (mArbeitsplatz.isIgnoreSaldoPlus() && saldoVormonat > 0){
            Log.d("#################", "plus");
                saldoVormonat = 0;
            }
        }
        mSaldoVormonat = saldoVormonat;
    }

    /**
     * setzen und löschen der manuellen Sollstunden
     *
     * @param s_manuell neue manuell gesetzte Sollstunden (-1 = manuelle Sollstunden löschen)
     * @return true, wenn sich durch diese Aktion der Saldo des Monats verändert hat
     */
    boolean setSollstundenManuell(int s_manuell) {
        boolean geaendert = false;
        if (s_manuell != Soll_Manuell) {
            if (s_manuell < 0) {
                // den manuellen Soll zurück setzen
                Soll_Manuell = s_manuell;

                geaendert = true;
            } else {
                // das manuelle Soll wurde gerade festgelegt
                if (s_manuell != getSollBrutto()) {
                    // den Bruttowert speichern
                    Soll_Manuell = s_manuell;

                    geaendert = true;
                }
            }

            // Die Liste der Faktoren aktuellisieren, die bei manuellen Saldo zur Berechnung der Teilsaldi (Tag, Woche)
            // herangezogen wird
            mArbeitsplatz.updateSollStundenFaktor(mStartDatum.get(Calendar.YEAR), mStartDatum.get(Calendar.MONTH), Soll_Manuell);

            // die Sollstunden der einzelnen Tage anpassen
            updateSollTage();

            // Sollstunden berechnen
            mSollNetto = berechne_Sollstunden(true);
            //Saldo berechnen
            updateSaldo(true);
            //return true;
        }
        return geaendert;
    }


    //
    // Die Überstundenauszahlung anpassen
    //

    /**
     * Ausbezahlte Überstunden setzen
     *
     * @param ausbezahlt die bezahlten Überstunden in Minuten
     * @return true wenn der neue Wert ungleich des alten Werten ist
     */
    boolean setAuszahlung(int ausbezahlt) {
        if (getAuszahlung() != ausbezahlt) {
            int sAlt = mSaldo + getAuszahlung();

            mAuszahlung = ausbezahlt;

            //Saldo neu berechnen
            mSaldo = sAlt - getAuszahlung();

            speichern();

            // den Saldo des folgenden Monats anpassen und evtl. aller weiteren Monate
            new Thread(this::updateSaldoFolgemonat).start();
            //updateSaldoFolgemonate();
            return true;
        }
        return false;
    }

    /**
     * Neuberechnung der Sollstunden unter Berücksichtigung der manuell gesetzten Sollstunden und
     * der eingetragenen Tage mit Abwesenheiten, die die Sollstunden reuzieren
     */
    public void updateSollStunden(){
        mSollNetto = berechne_Sollstunden(true);
    }

    /**
     * Berechnet den Saldo des Monats neu
     *
     * @param changeTag = wurde ein Tag verändert oder nicht
     * @return true wenn sich der Saldo geändert hat
     */
    public boolean updateSaldo(boolean changeTag) {
        int saldoAlt = mSaldo;

        if(changeTag) {
            mIstNetto = 0;
            for (int i = 0; i < mArbeitstage.size(); i++) {
                mIstNetto += getTagArbeitNetto(i);
            }
        }

        // Brutto Saldo
        mSaldo = getDifferenz() + mSaldoVormonat;

        // autom Auszahlung berechnen
        if(mArbeitsplatz.isAutoAuszahlung() && mAuszahlung < 0) {
            mAuszahlung_auto = Math.max(0, mSaldo - mArbeitsplatz.getAutoAuzahlungAb());
        } else {
            mAuszahlung_auto = 0;
        }

        // Netto Saldo
        mSaldo -= getAuszahlung();

        speichern();
        if(mSaldo != saldoAlt){
            // den Saldo des folgenden Monats anpassen und evtl. aller weiteren Monate
            new Thread(this::updateSaldoFolgemonat).start();
            return true;
        }
        return false;
    }

    /**
     * Anpassen der Tagessollstunden wenn sich die Eisntellungen geändert haben oder
     * die monatlichen Sollstunden manuell überschrieben wurden
     */
    private void updateSollTage(){
        // die pauschale Sollstundenanzahl eines Arbeitstages, wird nicht mit gespeichert, dient nur zur Berechnung
        int soll_TagPauschal = mArbeitsplatz.getSollstundenTagPauschal(mJahr, mMonat);
        for (Arbeitstag arbeitstag : mArbeitstage) {
            arbeitstag.updateSollTag(soll_TagPauschal);
        }
    }

    /**
     * Neuberechnen des Saldos der nachfolgenden Monate wenn sich der Saldo in diesen Monat geändert hat
     * dies läuft in einem eigen Hintergrundtask, um die App nicht zu verlangsamen
     */
    /*private void updateSaldoFolgemonate() {
        if(mUpdateFolgemonate) {
            Datum mKalender = new Datum(mStartDatum);
            mKalender.add(Calendar.MONTH, 1);

            new Thread(() -> {
                int sVormonat = getSaldo();
                do {*/
                    /*
                     * In dieser Schleife werden alle Folgemeonate einmal aufgerufen.
                     * Während des öffnens des Moants wird auch der Saldo des Vormonats gelesen,
                     * der Saldo angepasst und der Monat gespeichert wenn sich der Saldo geändert hat.
                     *
                     */
                    /*Arbeitsmonat m = new Arbeitsmonat(
                            mArbeitsplatz,
                            mKalender.get(Calendar.YEAR),
                            mKalender.get(Calendar.MONTH),
                            true,
                            false);
                    m.setSaldoVormonat(sVormonat);
                    m.updateSaldo(false);
                    sVormonat = m.getSaldo();
                    mKalender.add(Calendar.MONTH, 1);
                } while (!mKalender.liegtNach(ASettings.getLetzterAnzeigeTag(mArbeitsplatz)));
            }).start();
        }
    }*/

    private void updateSaldoFolgemonat() {
        if(mUpdateFolgemonate) {
            Datum mKalender = new Datum(mStartDatum);
            mKalender.add(Calendar.MONTH, 1);

            // wenn der nächste Monat nicht ausserhalb der Anzeigeperiode liegt
            // dessen SaldoVormonat anpassen
            if (!mKalender.liegtNach(ASettings.getLetzterAnzeigeTag(mArbeitsplatz))) {
                int sVormonat = getSaldo();
                Arbeitsmonat m = new Arbeitsmonat(
                        mArbeitsplatz,
                        mKalender.get(Calendar.YEAR),
                        mKalender.get(Calendar.MONTH),
                        true,
                        true);
                // ändert sich dadurch der Saldo des Monats,
                // wird diese Routine für den folgenden Monat aufgerufen
                m.setSaldoVormonat(sVormonat);
                m.updateSaldo(false);
            }
        }
    }


    /**
     * Die Sollstunden eines Monats berechnen Brutto oder Netto
     * Monat wird beschnitten je nachdem ob er teilweise vor dem Aifzeichnungsbeginn
     * oder teilweise nach dem Aufzeichnungsende liegt
     *
     * @param abzug true wenn die Bruttosollstunden verlangt werden
     *              false wenn die Abwesenheitstage die die Sollstunden reduzieren berücksichtigt werden sollen (Netto)
     */
    private int berechne_Sollstunden(boolean abzug) {
        int mSoll;
        float mAbzugTage = 0;
        int mAbzugTageInMinuten = 0;
        int mAbzugAbwesenheitMinuten = 0;

        /*
         * Sollstunden Berechnen
         */
        if (Soll_Manuell < 0) {
            //vom Soll die Anzahl Minuten aus den Schichten mit "Abzug von Sollstunden..." abziehen
            //wenn gewünscht
            mAbzugAbwesenheitMinuten = abzug ? getSummeAbwesenheitMinuten() : 0;

            // Die Sollstunden an Hand des Arbeitszeitmodells berechnen
            switch (mArbeitsplatz.getModell()) {
                case Arbeitsplatz.Soll_Woche_rollend:
                case Arbeitsplatz.Soll_Monat_pauschal:
                    // sollen die Tage die von Solltagen abgezogen werden berücksichtigt werden?
                    mAbzugTage = abzug ? getSummeAbwesenheitsTage() : 0;
                    break;
                default:
                    // sollen die Tage die von Solltagen abgezogen werden berücksichtigt werden?
                    mAbzugTageInMinuten = abzug ? getSummeAbwesenheitsTageInMinuten() : 0;
            }
            mSoll = mArbeitsplatz.getSollstundenMonat(
                    mStartDatum,
                    mAbzugTage,
                    mAbzugTageInMinuten,
                    mAbzugAbwesenheitMinuten);
        } else {
            // den manuell eingestellten Soll übertragen
            if (abzug) {
                mAbzugTageInMinuten = getSummeAbwesenheitsTageInMinuten();
                mAbzugAbwesenheitMinuten = getSummeAbwesenheitMinuten();
            }
            mSoll = Soll_Manuell - mAbzugAbwesenheitMinuten - mAbzugTageInMinuten;
        }

        return Math.max(mSoll, 0);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////// Ausgabefunktionen //////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Gibt ein Arbeitszplatz Objekt zurück
     *
     * @return der Arbeitszplatz
     */
    public Arbeitsplatz getArbeitsplatz(){
        return mArbeitsplatz;
    }

    /**
     * Gibt das Datums Objekt des ersten Tages des Monats zurück
     *
     * @return Datum des ersten Tages des Monats
     */
    public Datum getDatumErsterTag(){
        return mStartDatum;
    }

    /**
     * Gibt die Nummer des Kalendermoants zurück, beginnend mit 1 für Januar
     *
     * @return der Kalndermonat
     */
    public int getMonat() {
        return mMonat;
    }

    /**
     * gibt das Kalenderjahr zurück
     *
     * @return das Kalenderjahr
     */
    public int getJahr() {
        return mJahr;
    }

    /**
     * Gibt das Datums Objekt des letzten Tages des Monats zurück
     *
     * @return Datum des letzten Tages des Monats
     */
    public Datum getDatumLetzterTag(){
        return datEnde;
    }

    /**
     * gibt die Brutto Arbeitszeit des Monats zurück, inkl. der Pausenzeiten
     *
     * @return Brutto Arbeitszeit in Minuten
     */
    public int getIstBrutto(){
        int wert = 0;
        for (Arbeitstag tag: mArbeitstage) {
            wert += tag.getTagBrutto();
        }
        return wert;
    }

    /**
     * gibt die Summe aller Arbeitstunden minus Pausenzeiten des Monats zurück
     *
     * @return Summe aller Arbeitstunden minus Pausenzeite in Minuten
     */
    public int getIstNetto() {
        return mIstNetto;
    }

    /**
     * gibt die Summe aller Arbeitstunden minus Pausenzeiten des Monats zurück
     * Der Überstundenanteil dieses Wertes wird um die pauschal vergüteten Überstunden verringert
     *
     * @return Summe aller Arbeitstunden minus Pausenzeite in Minuten
     */
    public int getIstNettoMinusUeberstundenpauschale() {
        // wenn Überstunden pauschal vergütet werden und Überstunden angefallen sind
        // werden die pauschal bezahlten Überstunden von den Iststunden abgezogen
        int ueberPauschal = mArbeitsplatz.getUeberstundenPauschal();
        if (mIstNetto > mSollNetto && ueberPauschal > 0) {
            return Math.max(mSollNetto, mIstNetto - ueberPauschal);
        } else {
            return mIstNetto;
        }
    }

    /**
     * gibt die summierten Pausenstunden aller Arbeitstage des Monats zurück
     *
      * @return Pausenstunden in Minuten
     */
    public int getPause(){
        int wert = 0;
        for (Arbeitstag tag: mArbeitstage) {
            wert += tag.getTagPause();
        }
        return wert;
    }

    /**
     * gibt den Betrag der ausbezahlten Überstunden zurück
     *
     * @return ausbezahlte Überstunden in Minuten
     */
    public int getAuszahlung() {
        if ( mAuszahlung < 0) {
            return mAuszahlung_auto;
        } else {
            return mAuszahlung;
        }
    }

    /**
     * gibt die ID unter der dieser Monat in der Datenbank gespeichert wurde zurück
     *
     * @return ID des Monats
     */
    public long getId() {
        return MonatId;
    }

    /**
     * gibt die Netto Sollstunden des Monats zurück
     *
     * @return Netto Sollstunden als Minuten
     */
    public int getSollNetto() {
        return (mSollNetto);
    }

    /**
     * gibt die Brutto Sollstunden des Monats zurück
     *
     * @return Brutto Sollstunden als Minuten
     */
    int getSollBrutto(){
        int mSoll;

        if (Soll_Manuell < 0) {
            // Brutto Sollstunden berechnen
            mSoll = berechne_Sollstunden(false);
        } else
            mSoll = Soll_Manuell;

        return  mSoll;
    }

    /**
     * Gibt den Saldo des Monats zurück
     * Saldo + Saldo des Vormonats - ausbezahlte Überstunden
     *
     * @return Saldo des Monats in Minuten
     */
    public int getSaldo() {
        return (mSaldo);
    }

    // der Saldo nur diesen Monats, ohne Saldo Vormonat

    /**
     * gibt die Differenz von Iststunden zu Sollstunden des Monats aus
     *
     * @return Ist - Soll Differenz in Minuten
     */
    public int getDifferenz() {
        return (getIstNettoMinusUeberstundenpauschale() - mSollNetto);
    }

    /**
     * Der Tagesaktuelle Saldo eines noch nicht beendeten Monats
     *
     * @param tag der aktuelle Tag im Monat
     * @return Saldo in Minuten
     */
    public int getSaldo_Aktuell(int tag){
        int mSaldo = 0;
        int mTag = mIndexListeTag[tag];

        if (mTag <= mStartDatum.getAktuellMaximum(Calendar.DAY_OF_MONTH)) {
            for (int i = 0; i <= mTag; i++) {
                mSaldo += mArbeitstage.get(i).getTagSaldo();
            }
        } else {
            mSaldo = this.mSaldo;
        }
        return mSaldo + mSaldoVormonat - getAuszahlung();
    }

    /**
     * gibt den Saldo des Vormontas aus
     *
     * @return Saldo des Vomonats in Minuten
     */
    public int getSaldoVormonat() {
        return (mSaldoVormonat);
    }

    /**
     * gibt die Anzahl Tage des (unvollständigen)Monats zurück
     *
     * @return Anzahl der Tage die in diesen Monat gespeichert sind
     */
    public int getTagZahl() {
        return mArbeitstage.size();
    }

    /**
     * gibt die Anzahl der Soll Arbeitstage des Monats zurück
     *
     * @param netto false wenn alle Arbeitstage des Monats benötigt werden
     *              - true wenn die Abwesenheitstage herrausgerechnet werden sollen
     * @return Anzahl Arbeitstage des Monats
     */
    float getSollArbeitstageGesamterMonat(boolean netto){
        float solltage;
        float mAbzugTage = netto ? getSummeAbwesenheitsTage() : 0;
        // den Monat verkürzen wenn er vor dem Aufzeichnungsbeginn beginnt
        // oder nach dem Auzeichnungsende endet
        Datum mMonat = new Datum(
                mJahr,
                this.mMonat,
                mArbeitsplatz.getMonatsbeginn(), mArbeitsplatz.getWochenbeginn()
        );

        Datum mEnde = new Datum(mMonat);
        mEnde.add(Calendar.DAY_OF_MONTH, mMonat.getAktuellMaximum(Calendar.DAY_OF_MONTH) - 1);

        // den Monatsbeginn verschieben, wenn Aufzeichnungsbeginn später
        if (mArbeitsplatz.getStartDatum().liegtNach(mMonat)) {
            mMonat.set(mArbeitsplatz.getStartDatum().getDate());
        }

        // das Monatsende verschieben, wenn Aufzeichnungsende früher
        if (mArbeitsplatz.isSetEnde()) {
            if (mArbeitsplatz.getEndDatum().liegtVor(mEnde)) {
                mEnde.set(mArbeitsplatz.getEndDatum().getDate());
            }
        }
        mEnde.add(Calendar.DAY_OF_MONTH, 1);

        switch (mArbeitsplatz.getModell()) {
                case Arbeitsplatz.Soll_Woche_rollend:
                    solltage = mMonat.tageBis(mEnde);
                    solltage /= 7;
                    solltage *= 5;
                    break;
                case Arbeitsplatz.Soll_Monat_pauschal:
                    solltage = mMonat.tageBis(mEnde);
                    if (solltage < mMonat.getAktuellMaximum(Calendar.DAY_OF_MONTH)) {
                        if (solltage > 0){
                           solltage /= 30.42f / mArbeitsplatz.getArbeitstage_Monat();
                        } else {
                            solltage = 0;
                        }
                    } else {
                        solltage = mArbeitsplatz.getArbeitstage_Monat();
                    }
                    break;
                default:
                    solltage = 0;
                    do {
                        solltage += mArbeitsplatz.getArbeitstag(mMonat.get(Calendar.DAY_OF_WEEK));
                        mMonat.add(Calendar.DAY_OF_MONTH, 1);
                    } while (mEnde.liegtNach(mMonat));
            }

        return Math.max(0, solltage - mAbzugTage );
    }

    /**
     * gibt die Summe der Arbeitstage des angezeigten Monats aus
     * entweder alle Arbeitstage laut Kalender (false)
     * oder unter Berücksichtigung der eingetragenen Abwesenheitstage (Urlaub etc.) (true)
     *
     * @param netto  false wenn alle Arbeitstage des Monats benötigt werden
     *               - true wenn die Abwesenheitstage herrausgerechnet werden sollen
     * @return Anzahl Arbeitstage
     */
    public float getSollArbeitsTage(Boolean netto) {
        float mArbeitstageSumme;
        float mAbzugTage = netto ? getSummeAbwesenheitsTage() : 0;

        switch (mArbeitsplatz.getModell()) {
            case Arbeitsplatz.Soll_Woche_rollend:
                mArbeitstageSumme = getTagZahl();
                mArbeitstageSumme /= 7;
                mArbeitstageSumme *= 5;
                break;
            case Arbeitsplatz.Soll_Monat_pauschal:
                if (getTagZahl() < mArbeitsplatz.getAbrechnungsmonat(mStartDatum)
                        .getAktuellMaximum(Calendar.DAY_OF_MONTH)
                ) {
                    mArbeitstageSumme = getTagZahl();
                    if (mArbeitstageSumme > 0) {
                        mArbeitstageSumme /= 30.42f / mArbeitsplatz.getArbeitstage_Monat();
                    } else {
                        mArbeitstageSumme = 0;
                    }
                } else {
                    mArbeitstageSumme = mArbeitsplatz.getArbeitstage_Monat();
                }
                break;
            default:
                mArbeitstageSumme = 0;
                for (Arbeitstag aTag : mArbeitstage) {
                    mArbeitstageSumme += mArbeitsplatz.getArbeitstag(
                            aTag.getKalender().get(Calendar.DAY_OF_WEEK));
                }

        }
        /*if (mArbeitsplatz.getModell() == Arbeitsplatz.Soll_Woche_rollend) {
            mArbeitstageSumme = getTagZahl();
            mArbeitstageSumme /= 7;
            mArbeitstageSumme *= 5;
        } else if (mArbeitsplatz.getModell() == Arbeitsplatz.Soll_Monat_pauschal) {
            if ( getTagZahl()
                    < mArbeitsplatz.getAbrechnungsmonat(mStartDatum).getAktuellMaximum(Calendar.DAY_OF_MONTH)) {
                mArbeitstageSumme = getTagZahl();
                if ( mArbeitstageSumme > 0) {
                    mArbeitstageSumme /= 30.42f / mArbeitsplatz.getArbeitstage_Monat();
                } else {
                   mArbeitstageSumme = 0;
                }
            } else {
                mArbeitstageSumme = mArbeitsplatz.getArbeitstage_Monat();

            }
        } else {
            mArbeitstageSumme = 0;
            for (Arbeitstag aTag : mArbeitstage) {
                mArbeitstageSumme += mArbeitsplatz.getArbeitstag(
                        aTag.getKalender().get(Calendar.DAY_OF_WEEK));
            }
        }*/

        mArbeitstageSumme -= mAbzugTage;

        return Math.max(0, mArbeitstageSumme);
    }


    /**
     * gibt den Verdienst für diesen Monat zurück
     *
     * @return der Verdienst
     */
    public float getVerdienst() {
        Uhrzeit mZeit;
        int soll = getSollBrutto();
        float verdienst = 0;

        if (soll > 0) {
            // Verdienstminuten und Korrekturwerte sammeln
            float[] verdienstWerte;
            float verdienstMinuten = 0;
            float verdienstKorrektur = 0;
            for (Arbeitstag tag : mArbeitstage) {
               verdienstWerte = tag.getTagVerdienstWerte();
               verdienstMinuten += verdienstWerte[0];
               verdienstKorrektur += verdienstWerte[1];
            }

            // Anzahl Minuten auf Sollminuten reduzieren
            // also Überminuten von der Verdienstberechnung ausnehmen
            verdienstMinuten = Math.min(verdienstMinuten, soll);

            // Minuten in Stunden umrechnen
            float stunden = verdienstMinuten / 60;

            // verdienst berechnen
            verdienst = stunden * mArbeitsplatz.getStundenlohn();
            verdienst += verdienstKorrektur;

            // Ausbezahlte Überstunden ermitteln
            mZeit = new Uhrzeit(getAuszahlung());

            // den Verdienst um die ausgezahlten Überstunden erhöhen
            verdienst += mZeit.getAlsDezimalZeit() * mArbeitsplatz.getStundenlohn();
        } else {
            // Es wurden keine Sollstunden vorgegeben, dadurch werden nur die
            // als ausbezahlte Stunden eingetragenen Stunden verrechnet
            mZeit = new Uhrzeit(getAuszahlung());

            // den Verdienst für die ausbezahlten Stunden berechnen
            verdienst += mZeit.getAlsDezimalZeit() * mArbeitsplatz.getStundenlohn();

            // Verdienst um die Summe der eingetragenen Zusatzwerte,
            // die den Verdienst beeinflussen, korrigieren
            for (Arbeitstag tag : mArbeitstage) {
                verdienst += tag.getTagVerdienstKorrektur((float) getAuszahlung());
            }
        }
        // Verdienst auf zwei Nachkommastellen runden und zurück geben
        return Math.round(verdienst * 100.0f) / 100.0f;
    }


    /*
     * gibt den Verdienst für diesen Monat zurück
     *  ---- ALTE VERSION -----------------
     *
     * @return der Verdienst
     */
    /*public float getVerdienst_Alt() {
        Uhrzeit mZeit;
        int soll = getSollBrutto();

        if (soll > 0) {
            mZeit = new Uhrzeit(soll + getSaldo());

            if (mZeit.getAlsMinuten() > soll) {
                // geleistete Arbeitszeit übersteigt Sollzeit (Überstunden
                // der Verdienst wird auf die Anzahl der Sollstunden gedeckelt
                mZeit.set(soll);
            }

            // die ausbezahlten Überstunden werden addiert
            mZeit.add(getAuszahlung());

            // ist der Verdienst negativ, dann ist er 0.--
            if (mZeit.getAlsMinuten() < 0) {
                mZeit.set(0);
            }
        } else {
            // Es wurden keine Sollstunden vorgegeben, dadurch werden nur die
            // als ausbezahlte Stunden eingetragenen Stunden verrechnet
            mZeit = new Uhrzeit(getAuszahlung());
        }
        return mZeit.getAlsDezimalZeit() * mArbeitsplatz.getStundenlohn();
    }*/

    /*
     * gibt einen bestimmten Zusatzeintrag eines bestimmten Tages zurück
     *
     * @param tag der gewünschte Tag im Monat
     * @param indexWert der Index des Zusatzwertes in der Zusatzwerteliste
     * @return der gefundene Zusdatzwert
     */
    /*public IZusatzfeld getTagZusatzwert(int tag, int indexWert){
        if(mArbeitstage.size() > tag)
            return mArbeitstage.get(tag).getTagZusatzwert(indexWert);
        else
            return null;
    }*/

    /**
     * gibt ein Arbeitstag Objekt zurück welches zu dem Kalendertag gehört
     *
     * @param tag der Tag im Monat
     * @return Arbeitstag Objekt des Tages
     */
    public Arbeitstag getTagimMonat(int tag) {
        tag = mIndexListeTag[tag];
        if(tag > -1) {
            return mArbeitstage.get(tag);
        } else {
            return null;
        }
    }

    /**
     * gibt ein Arbeitstag Objekt zurück welches in der Tagesliste an besgter Positon liegt
     * wenn der Abrechnungsmonat nicht mit dem 1. des Monats beginnt, ist der erste Tag in der Liste
     * der erste Tag des Abrechnungsmonats und nicht der erste Tag des Kalendermonats
     *
     * @param position des Tages in der Liste der Tage
     * @return Arbeitstag Objekt des Tages
     */
    public Arbeitstag getTagInListe(int position) {
        if(mArbeitstage.size() > position)
            return mArbeitstage.get(position);
        else
            return null;
    }

    /**
     * gibt die Position des Kalendertages in der Liste der Tage aus
     * wenn der Abrechnungsmonat nicht mit dem 1. des Monats beginnt, ist der erste Tag in der Liste
     * der erste Tag des Abrechnungsmonats und nicht der erste Tag des Kalendermonats
     *
     * @param tagImMonat Kalendertag des Monats
     * @return Position in der Liste der Tage
     */
    protected int getTagPosition(int tagImMonat){
        return mIndexListeTag[tagImMonat];
    }

    /**
     * gibt die Netto Arbeitsstunden eines Tages in Minuten zurück
     *
     * @param tag der gesuchte Tag
     * @return Netto Stunden des Tages in Minuten
     */
    private int getTagArbeitNetto(int tag) {
        if (mArbeitstage.size() > tag)
            return mArbeitstage.get(tag).getTagNetto();
        else
            return 0;
    }

    /**
     * gibt die Summe der Werte eines Zusatzfeldes als Zusatzfeld Objekt zurück
     *
     * @param index die Nummer des Zusatzfeldes in der Liste der Zusatzfelder, mit 0 beginnend
     * @return das Zusatzfeldobjekt mit der Summme des Zusatzfeldes
     */
    public IZusatzfeld getSummeZusatzeintrag(int index) {
        IZusatzfeld eintrag = mArbeitsplatz.getZusatzDefinition(index).makeNewZusatzfeld();
        if (eintrag != null) {
            for (Arbeitstag tag : mArbeitstage) {
                eintrag.add(tag.getTagZusatzwert(index));
            }
        }
        return eintrag;
    }

    /**
     * gibt eine Liste aller Zusatzfelder, ausser Textfelder, zurück
     * die Liste enthält die Monatssummen aller Zusatzfelder
     *
     * @return summierte Zusatzfeldliste
     */
    public ZusatzWertListe getZusatzeintragSummenListe(){
        ZusatzWertListe mListe = new ZusatzWertListe(mArbeitsplatz.getZusatzfeldListe(), false);
        if(mListe.size() > 0) {
            for (Arbeitstag tag : getTagListe()) {
                mListe.addListenWerte(tag.getTagZusatzwerte(IZusatzfeld.TEXT_NO));
            }
        }

        return mListe;
    }

    /**
     * gibt die Summe an Tagen in denen eine Alternative zur Arbeitszeit eingetragen wurde(z.B.: Urlaubstage)
     *
     * @return Summe der Tage
     */
    public ArrayList<Float> getSummeAlternativTage(){
        ArrayList<Float> werte = new ArrayList<>();
        int mengeAbwesenheiten = mArbeitsplatz.getAbwesenheiten().size();
        boolean isUrlaubStunden = mArbeitsplatz.isOptionSet(Arbeitsplatz.OPT_URLAUB_ALS_STUNDEN);
        Abwesenheit abw;
        float mSollstundenPauschal = mArbeitsplatz.getSollstundenTagPauschal(mJahr, mMonat);

        for (int i = 0; i < mArbeitstage.size(); i++) {
            for (int a = 0; a < mengeAbwesenheiten; a++) {
                abw = mArbeitsplatz.getAbwesenheiten().get(a);
                if(i == 0) {
                    if( abw.getKategorie() == Abwesenheit.KAT_URLAUB && isUrlaubStunden) {
                        float m = mArbeitstage.get(i).getAlternativMinuten(abw.getID());
                        //m /= 60;
                        m /= mSollstundenPauschal;
                        werte.add(m);
                    } else {
                        werte.add(mArbeitstage.get(i).getAlternativTag(abw.getID()));
                    }
                } else {
                    if( abw.getKategorie() == Abwesenheit.KAT_URLAUB && isUrlaubStunden) {
                        float m = mArbeitstage.get(i).getAlternativMinuten(abw.getID());
                        //m /= 60;
                        Log.d("###########", "Stunden: "+m);
                        m /= mSollstundenPauschal;
                        werte.set(a, werte.get(a) + m);
                        Log.d("###########", "Tqge: "+m);
                    }
                    else
                        werte.set(a, werte.get(a) + mArbeitstage.get(i).getAlternativTag(abw.getID()));
                }
            }
        }
        return werte;
    }

    /**
     * gibt die Summe an Tagen aus in denen eine bestimmte Abwesenheit eingetragen wurde
     *
     * @param abwesenheit gesuchte Abwesenheit
     * @return Summe der Tage mit dieser Abwesenheit
     */
    public float getSummeAlternativTage(long abwesenheit) {
        float u = 0;

        for (Arbeitstag aTag : mArbeitstage ) {
            u += aTag.getAlternativTag(abwesenheit);
        }
        return u;
    }

    /**
     * gibt die Summe an Stunden (in Minuten) aus in denen eine bestimmte Abwesenheit eingetragen wurde
     *
     * @param abwesenheit  gesuchte Abwesenheit
     * @return Summe der Stunden in MInuten mit dieser Abwesenheit
     */
    public int getSummeAlternativMinuten(long abwesenheit) {
        int m = 0;
        for (Arbeitstag aTag : mArbeitstage ) {
            for (Arbeitsschicht aSchicht : aTag.getSchichten()) {
                if( aSchicht.getAbwesenheit().getID() == abwesenheit ){
                   m += aSchicht.getNetto();
                }
            }
        }
        return m;
    }

    // Summe von Tage einer bestimmten Kategorie

    /**
     * Summe an Tagen in die eine Abwesenheit einer bestimmter Kategorie eingetragen wurde
     *
     * @param kategorie gesuchte Kategorie
     * @return Anzahl Tage
     */
    public float getSummeKategorieTage(int kategorie){
        float tage = 0;

        for ( Arbeitstag aTag : mArbeitstage ) {
            tage += aTag.getKategorieTag(kategorie);
        }

        return tage;
    }

    /**
     * gibt die Summe an Tagen aus in denen eine Abwesenheit mit der Wirkung "Abzug von Solltagen"
     * eingetragen wurde
     *
     * @return Anzahl der Tage
     */
    public float getSummeAbwesenheitsTage() {
        float tage = 0;

        for ( Arbeitstag aTag : mArbeitstage ) {
            tage += aTag.getAbzugTag();
        }
        return tage;
    }

    /**
     * gibt die Summe an Stunden(in Minuten) aus in denen eine Abwesenheit mit der Wirkung "Abzug von Solltagen"
     * eingetragen wurde
     *
     * @return Summe der Stunden in Minuten
     */
    private int getSummeAbwesenheitsTageInMinuten() {
        int minuten = 0;

        for ( Arbeitstag aTag : mArbeitstage ) {
            minuten += aTag.getAbzugTag() * aTag.getTagSollBrutto();
        }
        return minuten;
    }


    // gibt die Summe von Minuten nach Art der Abwesenheit (bezahlt(Feiertage)
    // oder Sollstundereduzierend (Urlaub, Krank. usw.)

    /**
     * gibt die Summe an Stunden(in Minuten) aus in denen eine Abwesenheit
     * mit der Wirkung "Abzug von Sollstunden" eingetragen wurde
     *
     * @return Summe der Stunden in Minuten
     */
    private int getSummeAbwesenheitMinuten() {
        int minuten = 0;

        for ( Arbeitstag aTag : mArbeitstage ) {
            minuten += aTag.getAbzugMinuten();
        }

        return minuten;
    }

    public ArrayList<Arbeitstag> getTagListe() {
        return mArbeitstage;
    }

    boolean isSollManuell(){
        return (Soll_Manuell >= 0);
    }

    public int getSummeEinsatzortMinuten(long id_eort){
        int summe = 0;
         //if(mArbeitstage != null ) {
             for (int i = 0; i < mArbeitstage.size(); i++) {
                 for (int s = 0; s < mArbeitstage.get(i).getSchichtAnzahl(); s++) {
                     if (mArbeitstage.get(i).getSchicht(s).getIdEinsatzort() == id_eort)
                         summe += mArbeitstage.get(i).getSchicht(s).getNetto();
                 }
             }
        // }
        return summe;
    }

    public ArrayList<Arbeitsschicht> getSchichtListe() {
        ArrayList<Arbeitsschicht> mSchichten = new ArrayList<>();
         //if(mArbeitstage != null ) {
             for (int i = 0; i < mArbeitstage.size(); i++) {
                 mSchichten.addAll(mArbeitstage.get(i).getSchichten());
                 /*for (int s = 0; s < Arbeitstage.get(i).getSchichtzahl(); s++) {
                     mSchichten.add(Arbeitstage.get(i).getSchicht(s));
                 }*/
             }
         //}

        return mSchichten;
    }

///////////////////// Ende der Wertrückgaben //////////////////////////////////////////////////
}