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

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

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

import askanimus.arbeitszeiterfassung2.Datum;
import askanimus.arbeitszeiterfassung2.abwesenheiten.Abwesenheit;
import askanimus.arbeitszeiterfassung2.arbeitsmonat.Arbeitsmonat;
import askanimus.arbeitszeiterfassung2.arbeitsplatz.Arbeitsplatz;
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 31.12.15.
 */
public class Arbeitsjahr {
    private static final String SQL_READ_JAHR =
            "select * from " +
                    DatenbankHelper.DB_T_JAHR +
                    " where " +
                    DatenbankHelper.DB_F_JOB +
                    " = ? AND " +
                    DatenbankHelper.DB_F_JAHR +
                    " = ? LIMIT 1 ";

    protected Arbeitsplatz mArbeitsplatz;
    private long Id = -1;
    public int Jahr;

    private float sollUrlaub = 0;
    private boolean isManuell = false;
    private float istUrlaub = 0;
    public float[] istUrlaubMonat;
    public float[] bezogenUrlaub;
    private float geplanterUrlaub;
    public float geplanterUrlaubAktMonat;

    private float restUrlaub = 0;
    private float restUrlaubIst = 0;
    public float[] bezogenRest;
    public float[] restUrlaubIstMonat;
    int verfallMonat = 3;
    int verfallTag = 31;

    private final boolean urlaubIsStunden;

    int MonatBeginn;
    private int MonatEnde;
    private final Boolean Berechnung_Urlaub;

    // die Monate des Jahres
    public ArrayList<Arbeitsmonat> listMonate;

    // Urlaubsliste
    //public Urlaub mUrlaub;

    // die Summen der einzelnen Monate
    private int hVorjahr = 0;
    int hSoll = 0;
    int hIst = 0;
    int hAusbezahlt = 0;
    /*float sSpesen = 0;
    float sStrecke = 0;
    int sZeit = 0;*/
    float sVerdienst = 0;
    public ZusatzWertListe mZusatzwerteJahresSumme;
    public ArrayList<ZusatzWertListe> mZusazwerteMonatsSummen;

    private int Saldo_aktuell = 0;


    @SuppressLint("Range")
    Arbeitsjahr(int jahr, Arbeitsplatz arbeitsplatz, Boolean berechne_urlaub) {
        // SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getReadableDatabase();
        listMonate = new ArrayList<>();
        mArbeitsplatz = arbeitsplatz;
        mZusatzwerteJahresSumme = new ZusatzWertListe(mArbeitsplatz.getZusatzfeldListe(), false);
        Jahr = jahr;
        urlaubIsStunden = mArbeitsplatz.isOptionSet(Arbeitsplatz.OPT_URLAUB_ALS_STUNDEN);
        Berechnung_Urlaub = berechne_urlaub;

        if (jahr > mArbeitsplatz.getStartDatum().get(Calendar.YEAR)) {
            MonatBeginn = 1;
        } else if (jahr == mArbeitsplatz.getStartDatum().get(Calendar.YEAR)) {
            MonatBeginn = mArbeitsplatz.getStartDatum().get(Calendar.MONTH);
            if (mArbeitsplatz.getMonatsbeginn() > 1 &&
                    mArbeitsplatz.getStartDatum().get(Calendar.DAY_OF_MONTH) < mArbeitsplatz.getMonatsbeginn()) {
                if (MonatBeginn > Datum.JANUAR)
                    MonatBeginn--;
            }
        } else if (jahr == mArbeitsplatz.getStartDatum().get(Calendar.YEAR) - 1 &&
                mArbeitsplatz.getStartDatum().get(Calendar.MONTH) == Datum.JANUAR &&
                mArbeitsplatz.getMonatsbeginn() > 1 &&
                mArbeitsplatz.getStartDatum().get(Calendar.DAY_OF_MONTH) < mArbeitsplatz.getMonatsbeginn()) {
            MonatBeginn = 12;
        } else {
            MonatBeginn = 13;
        }

        Datum letzterAnzeigeTag = ASettings.getLetzterAnzeigeTag(mArbeitsplatz);
        if (jahr == letzterAnzeigeTag.get(Calendar.YEAR)) {
            MonatEnde = letzterAnzeigeTag.get(Calendar.MONTH);
            if (letzterAnzeigeTag.get(Calendar.DAY_OF_MONTH) < mArbeitsplatz.getMonatsbeginn())
                MonatEnde--;
        } else if (jahr < letzterAnzeigeTag.get(Calendar.YEAR))
            MonatEnde = 12;
        else
            MonatEnde = 0;

        if (MonatBeginn <= MonatEnde) {
            Cursor result = ASettings.mDatenbank.rawQuery(SQL_READ_JAHR, new String[]{
                    Long.toString(mArbeitsplatz.getId()),
                    Integer.toString(jahr)
            });

            if (result.getCount() > 0) {
                result.moveToFirst();
                Id = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_ID));
                Jahr = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_JAHR));
                verfallMonat = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_MONAT_VERFALL));
                if (verfallMonat == 0)
                    verfallMonat = 3;
                verfallTag = result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_TAG_VERFALL));
                if (verfallTag == 0)
                    verfallTag = 31;
                sollUrlaub = result.getFloat(result.getColumnIndex(DatenbankHelper.DB_F_SOLL_URLAUB));
                isManuell = (result.getInt(result.getColumnIndex(DatenbankHelper.DB_F_SOLL_MANUELL)) == 1);
                istUrlaub = result.getFloat(result.getColumnIndex(DatenbankHelper.DB_F_IST_URLAUB));
                restUrlaubIst = result.getFloat(result.getColumnIndex(DatenbankHelper.DB_F_REST_URLAUB));
            } else {
                setSollUrlaub(-1);
            }
            result.close();
            // ASettings.mDatenbank.close();

            // Resturlaub anpassen
            if (Jahr == mArbeitsplatz.getStartDatum().get(Calendar.YEAR)) {
                restUrlaub = mArbeitsplatz.getStart_Urlaub();
                if(mArbeitsplatz.isOptionSet(Arbeitsplatz.OPT_URLAUB_ALS_STUNDEN)){
                    //restUrlaub *= mArbeitsplatz.getTagSollPauschal();
                    restUrlaub /= 60;
                }
            } else {
                Arbeitsjahr mVojahr = new Arbeitsjahr(Jahr - 1, mArbeitsplatz, false);
                restUrlaub = mVojahr.getUrlaubSaldo();
                if (!mVojahr.isUrlaubVerfallen(12))
                    restUrlaub += (mVojahr.getResturlaub() - mVojahr.getResturlaubIst());
            }

            if (restUrlaub < 0)
                restUrlaub = 0;

            if (!isManuell)
                setSollUrlaub(-1);

            // Monate lesen und Jahressummen erzeugen
            if (Berechnung_Urlaub) {
                // Monatsliste erzeugen
                Arbeitsmonat mMonat;

                for (int i = MonatBeginn; i <= MonatEnde; i++) {
                    // Monat lesen
                    mMonat = new Arbeitsmonat(mArbeitsplatz, Jahr, i, true, false);
                    // in die Liste einfügen
                    listMonate.add(mMonat);

                    // den Saldo am aktuellen Tag bestimmen
                    if (Jahr == ASettings.aktDatum.get(Calendar.YEAR) &&
                            mMonat.getMonat() == ASettings.aktDatum.get(Calendar.MONTH)) {
                        Saldo_aktuell = mMonat.getSaldo_Aktuell(ASettings.aktDatum.get(Calendar.DAY_OF_MONTH));
                    }
                }

                // Urlaub berechnen
                if (mArbeitsplatz.getSoll_Urlaub() > 0)
                    berechneUrlaub();
            }
            // den Saldo des Vormonats lesen
            if (listMonate.size() > 0) {
                hVorjahr = listMonate.get(0).getSaldoVormonat();
            }
        }

    }


   private void berechneUrlaub() {
       float restUrlaubIst_alt = restUrlaubIst;
       float istUrlaub_alt = istUrlaub;
       restUrlaubIst = 0;
       istUrlaub = 0;
       geplanterUrlaub = 0;
       geplanterUrlaubAktMonat = 0;

       bezogenRest = new float[13];
       restUrlaubIstMonat = new float[13];
       istUrlaubMonat = new float[13];
       bezogenUrlaub = new float[13];
       float mBezogen;

       Datum mAktuell = mArbeitsplatz.getAbrechnungsmonat(ASettings.aktDatum);
       int aktuellerMonat = mAktuell.get(Calendar.YEAR) * 12 + mAktuell.get(Calendar.MONTH);
       int j = getJahr() * 12;

       for (Arbeitsmonat mMonat : listMonate) {
           int m = mMonat.getMonat();
           boolean isAktuellerMonat = (j + m) == aktuellerMonat;

           bezogenUrlaub[m] = 0;
           bezogenRest[m] = 0;
           for (Arbeitstag mTag : mMonat.getTagListe()) {
               if (urlaubIsStunden) {
                   mBezogen = mTag.getKategorieMinuten(Abwesenheit.KAT_URLAUB);
                   mBezogen /= 60;
               } else {
                   mBezogen = mTag.getKategorieTag(Abwesenheit.KAT_URLAUB);
               }

               if (mTag.getKalender().liegtNach(ASettings.aktDatum)) {
                   if(isAktuellerMonat){
                       geplanterUrlaubAktMonat += mBezogen;
                   }
                   geplanterUrlaub += mBezogen;
               } else {
                   if ((restUrlaub - restUrlaubIst) <= 0 || isUrlaubVerfallen(mMonat.getMonat())) {
                       //if (mBezogen > 0) {
                       bezogenUrlaub[m] += mBezogen;
                       istUrlaub += mBezogen;
                       // }
                   } else {
                       if (mBezogen > 0) {
                           restUrlaubIst += mBezogen;
                           if (restUrlaubIst > restUrlaub) {
                               istUrlaub = restUrlaubIst - restUrlaub;
                               restUrlaubIst = restUrlaub;
                               bezogenUrlaub[m] += istUrlaub;
                               bezogenRest[m] += (mBezogen - istUrlaub);
                           } else {
                               bezogenRest[m] += mBezogen;
                           }
                       }
                   }
               }
           }

           restUrlaubIstMonat[m] = restUrlaubIst;
           istUrlaubMonat[m] = istUrlaub;
       }

       // haben sich die Urlaubswerte geändert, das Jahr speichern
       if (istUrlaub_alt != istUrlaub || restUrlaubIst_alt != restUrlaubIst)
           speichern();
   }

    protected void speichern(){
        // SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getWritableDatabase();
        ContentValues werte = new ContentValues();
        werte.put(DatenbankHelper.DB_F_JAHR, Jahr);
        werte.put(DatenbankHelper.DB_F_JOB, mArbeitsplatz.getId());
        werte.put(DatenbankHelper.DB_F_MONAT_VERFALL, verfallMonat);
        werte.put(DatenbankHelper.DB_F_TAG_VERFALL, verfallTag);
        werte.put(DatenbankHelper.DB_F_SOLL_URLAUB, sollUrlaub);
        werte.put(DatenbankHelper.DB_F_SOLL_MANUELL, isManuell?1:0);
        werte.put(DatenbankHelper.DB_F_IST_URLAUB, istUrlaub);
        werte.put(DatenbankHelper.DB_F_REST_URLAUB, restUrlaubIst);

        if(Id <= 0)
            Id = ASettings.mDatenbank.insert(DatenbankHelper.DB_T_JAHR, null, werte);
        else
            ASettings.mDatenbank.update(DatenbankHelper.DB_T_JAHR, werte, DatenbankHelper.DB_F_ID+"=?", new String[] {Long.toString(Id)});

        // mDatenbank.close();
    }


    ///////////////////////////////////////////////////////////////////////////////////////////////
    void setSollUrlaub(float soll){
        float sollAlt = sollUrlaub;

        if(soll < 0) {
            isManuell = false;
            float sollMonatUrlaub = mArbeitsplatz.getSoll_Urlaub() /12;

            if(Jahr == (mArbeitsplatz.getStartDatum().get(Calendar.YEAR)-1) && MonatBeginn == 12){
                sollUrlaub = 0;
            } else if (MonatBeginn > 1) {
                sollUrlaub = sollMonatUrlaub * ((12 - MonatBeginn) + 1);
            } else
                sollUrlaub = mArbeitsplatz.getSoll_Urlaub();

            if(mArbeitsplatz.isSetEnde()){
                if(Jahr > mArbeitsplatz.getEndDatum().get(Calendar.YEAR))
                    sollUrlaub = 0;
                else if(Jahr == mArbeitsplatz.getEndDatum().get(Calendar.YEAR)){
                    sollUrlaub = sollMonatUrlaub * ((mArbeitsplatz.getEndDatum().get(Calendar.MONTH) - MonatBeginn) + 1);
                }
            }

        } else {
            sollUrlaub = soll;
            isManuell = true;
        }

        if(urlaubIsStunden) {
            //sollUrlaub *= mArbeitsplatz.getTagSollPauschal();
            sollUrlaub /= 60;
        }

        if(sollAlt != sollUrlaub)
            speichern();
    }

    void setVerfall(int monat, int tag){
        verfallMonat = monat;
        verfallTag = tag;

        if(Berechnung_Urlaub)
            berechneUrlaub();

        speichern();
    }


    ///////////////////////////////////////////////////////////////////////////////////////////////
    public Arbeitsplatz getArbeitsplatz(){
        return mArbeitsplatz;
    }

    public int getJahr(){
        return Jahr;
    }

    public int getMonatBeginn(){
        return MonatBeginn;
    }

    public int getMonatEnde(){
        return MonatEnde;
    }

    public Datum getDatumBeginn(){
        return listMonate.get(0).getDatumErsterTag();
    }

    public Datum getDatumEnde(){
        return listMonate.get(listMonate.size()-1).getDatumLetzterTag();
    }


    public float getIstUrlaub(){
        return istUrlaub;
    }

    public float getUrlaubSoll(){
        return sollUrlaub;
    }

    boolean isUrlaubManuell(){
        return isManuell;
    }

    public float getResturlaubIst(){
        return restUrlaubIst;
    }

    public float getUrlaubSaldo(){
        //float s = sollUrlaub - istUrlaub;
        /*if(s<0)
            s = 0;*/

        return sollUrlaub - istUrlaub;
    }

    public float getResturlaub(){
        return restUrlaub;
    }

    public float getUrlaubGeplant(){
        return geplanterUrlaub;
    }

    public boolean isUrlaubVerfallen(int monat){
        boolean mVerfallen = false;

        if(!mArbeitsplatz.isOptionSet(Arbeitsplatz.OPT_RESTURLAUB_NO_VERFALL)) {
            if (monat > verfallMonat)
                mVerfallen = true;
        }

        return mVerfallen;
    }


    public int getSaldoVorjahr(){
        return hVorjahr;
    }

    public int getSoll(){
        return hSoll;
    }

    public int getIst() {
        return hIst;
    }

    public int getDifferenz(){
        return hIst - hSoll;
    }

    public int getAusgezahlt(){
        return hAusbezahlt;
    }

    public int getSaldo(){
        return hIst - hSoll - hAusbezahlt + hVorjahr;
    }

    int getSaldo_aktuell(){
        return Saldo_aktuell;
    }

    /*protected float getSpesen(){
        return sSpesen;
    }

    protected float getStrecke(){
        return sStrecke;
    }

    int getZeit(){
        return sZeit;
    }*/

    public float getVerdienst(){
        return sVerdienst;
    }

    public float getArbeitstage(){
        float t = 0;
        for (Arbeitsmonat m : listMonate) {
            t += m.getSollArbeitsTage(false);
        }
        return t;
    }

    public float getAbwesenheitstage() {
        float t = 0;
        for (Arbeitsmonat m : listMonate) {
            t += m.getSummeAbwesenheitsTage();
        }
        return t;
    }

    public float getSummeAlternativTage(long idAbwesenheit) {
        float t = 0;
        for (Arbeitsmonat m : listMonate) {
            t += m.getSummeAlternativTage(idAbwesenheit);
        }
        return t;
    }

    public int getSummeAlternativMinuten(long idAbwesenheit) {
        int minuten = 0;
        for (Arbeitsmonat m : listMonate) {
            minuten += m.getSummeAlternativMinuten(idAbwesenheit);
        }
        return minuten;
    }

    public ArrayList<Arbeitstag> getTage(){
        ArrayList<Arbeitstag> tage = new ArrayList<>();
        for (Arbeitsmonat m : listMonate) {
            tage.addAll(m.getTagListe());
        }
        return tage;
    }

    /*public ZusatzWertListe getZusatzeintragSummenListe() {
        ZusatzWertListe mListe = new ZusatzWertListe(getArbeitsplatz().getZusatzfeldListe(), false);
        if(mListe.size() > 0) {
            for (Arbeitstag tag : getTage()) {
                mListe.addListenWerte(tag.getTagZusatzwerte(IZusatzfeld.TEXT_NO));
            }
        }
        return mListe;
       //return mZusatzwerteSumme;
    }*/


    protected IZusatzfeld getZusatzwertSumme(int index){
        if(index < mZusatzwerteJahresSumme.size())
            return mZusatzwerteJahresSumme.get(index);
        return null;
    }
}
