/*
 * Copyright (c) 2014 - "2023")$today.year.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.datenbank;
import askanimus.arbeitszeiterfassung2.R;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.core.content.res.ResourcesCompat;

import askanimus.arbeitszeiterfassung2.abwesenheiten.Abwesenheit;
import askanimus.arbeitszeiterfassung2.arbeitsplatz.Arbeitsplatz;
import askanimus.arbeitszeiterfassung2.setup.ASettings;

public class Bereinige_Datenbank {
    final String MESSAGE = "msg";
    Activity mContext;
    int startJahr;
    int grenzJahr;

    public Bereinige_Datenbank(Activity context) {
        startJahr = ASettings.aktDatum.getJahr();
        grenzJahr = startJahr + 2;
        mContext = context;
    }

    public void reinigung() {
        //Handler postHandler = new Handler();
        // Fortschritsdialog öffnen
        ProgressDialog dialog = new ProgressDialog(mContext);
        dialog.setIndeterminate(true);
        dialog.setIndeterminateDrawable(
                ResourcesCompat.getDrawable(
                        mContext.getResources(),
                        R.drawable.progress_dialog_anim,
                        mContext.getTheme()));
        dialog.setCancelable(false);
        dialog.setMessage(mContext.getString(R.string.progress_dbreinigen));
        dialog.show();

        final Handler messageHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                Bundle data = msg.getData();
                if (data != null) {
                    //String key = (String) data.keySet().toArray()[0];
                    dialog.setMessage(data.getCharSequence(MESSAGE));
                }
                return false;
            }
        });
        new Thread(() -> {
            boolean status;
            // SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getWritableDatabase();
            // zuerst leere Tage löschen
            status = loescheLeereTage(messageHandler, ASettings.mDatenbank);

            // dann leere Monate löschen
            if (status) {
                status = loescheLeereMonate(messageHandler, ASettings.mDatenbank);
            }

            // leere Jahre löschen
            //dialog.setMessage(context.getString(R.string.progress_dbreinigen_jahr));
            if (status) {
                status = loescheLeereJahre(messageHandler, ASettings.mDatenbank);
            }

            // ASettings.ASettings.mDatenbank.close();

            final boolean statusAll = status;
            messageHandler.post(() -> {
                // Progressdialog ausblenden
                if (dialog.isShowing()) {
                    dialog.dismiss();
                }
                // Erfolgsmeldung
                Toast erfolgToast = Toast.makeText(
                        mContext,
                        statusAll ?
                                mContext.getString(R.string.toast_dbreinigen) :
                                mContext.getString(R.string.toast_dbreinigen_no),
                        Toast.LENGTH_LONG
                );
                erfolgToast.show();
            });
        }).start();

    }

    @SuppressLint("Range")
    private boolean loescheLeereTage(Handler messageHandler, SQLiteDatabase datenbank) {
        boolean status = true;
        Cursor resultTage;
        Cursor resultSchichten;
        long tagID;
        Arbeitsplatz job;
        long abwesenheit;
        int schichtAnzahl;
        int jahr = startJahr;
        int delTage = 0;

        Message msg = messageHandler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putCharSequence(
                MESSAGE,
                mContext.getString(
                        R.string.progress_dbreinigen,
                        delTage,
                        mContext.getString(R.string.tage
                        ))
        );
        msg.setData(bundle);
        messageHandler.sendMessage(msg);

        // die ID's aller Tage lesen
        String leseTageSQL = "select "
                + DatenbankHelper.DB_F_ID + ", "
                + DatenbankHelper.DB_F_JOB
                + " from " + DatenbankHelper.DB_T_TAG
                + " where " + DatenbankHelper.DB_F_JAHR
                + " = ?";

        // die Schichten des Tages lesen
        // sobald eine Schicht für den Tag gespeichert wurde kann dieser Tag nicht gelöscht werden
        String leseSchichtenSQL = "select "
                + DatenbankHelper.DB_F_ID + ", "
                + DatenbankHelper.DB_F_ABWESENHEIT
                + " from " + DatenbankHelper.DB_T_SCHICHT
                + " where " + DatenbankHelper.DB_F_TAG
                + " = ?";

        // alle Tage Jahresweise lesen lesen
        // bei sehr vielen Jahren in der Datenbank ist der Speicher für die Cursordaten zu klein
        //// SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getWritableDatabase();
        resultTage = datenbank.rawQuery(leseTageSQL, new String[]{Integer.toString(jahr)});
        while (resultTage.getCount() > 0) {
            // Schleife durschläuft alle gelesenen Tage des Jahres
            while (resultTage.moveToNext()) {
                tagID = resultTage.getLong(resultTage.getColumnIndex(DatenbankHelper.DB_F_ID));
                //jahr = resultTage.getInt(resultTage.getColumnIndex(Datenbank.DB_F_JAHR));
                job = ASettings.getArbeitsplatz(resultTage.getLong(resultTage.getColumnIndex(DatenbankHelper.DB_F_JOB)));

                // Schichten des Tages lesen
                resultSchichten = datenbank.rawQuery(leseSchichtenSQL, new String[]{Long.toString(tagID)});

                if (job == null) {
                    // dieser Tag gehört zu einem gelöschten Job, kann weg
                    // erst alle Schichten löschen
                    while (resultSchichten.moveToNext()) {
                        datenbank.delete(
                                DatenbankHelper.DB_T_SCHICHT,
                                DatenbankHelper.DB_F_ID + "=?",
                                new String[]{Long.toString(
                                        resultSchichten.getLong(resultSchichten.getColumnIndex(DatenbankHelper.DB_F_ID))
                                )});

                    }
                    // diesen Tag löschen
                    delTage++;
                    datenbank.delete(
                            DatenbankHelper.DB_T_TAG,
                            DatenbankHelper.DB_F_ID + "=?",
                            new String[]{Long.toString(tagID)});
                } else {
                    schichtAnzahl = resultSchichten.getCount();
                    // wenn der Tag weit in der Zukunft liegt (heute plus 2 Jahre)
                    // Tage und Schichten löschen, dann stehen wahrscheinlich nur autom. angelegte freie Tage drin
                    if (jahr >= grenzJahr && schichtAnzahl > 0) {
                        while (resultSchichten.moveToNext()) {
                            abwesenheit = resultSchichten.getLong(resultSchichten.getColumnIndex(DatenbankHelper.DB_F_ABWESENHEIT));
                            Abwesenheit abw = job.getAbwesenheiten().getVonId(abwesenheit);
                            if (abw != null && abw.getWirkung() == Abwesenheit.WIRKUNG_KEINE) {
                                datenbank.delete(
                                        DatenbankHelper.DB_T_SCHICHT,
                                        DatenbankHelper.DB_F_ID + "=?",
                                        new String[]{Long.toString(
                                                resultSchichten.getLong(resultSchichten.getColumnIndex(DatenbankHelper.DB_F_ID))
                                        )});
                                schichtAnzahl--;
                            }
                        }
                    }
                    // wenn keine Schicht vorhanden diesen Tag löschen
                    if (schichtAnzahl <= 0) {
                        datenbank.delete(
                                DatenbankHelper.DB_T_TAG,
                                DatenbankHelper.DB_F_ID + "=?",
                                new String[]{Long.toString(tagID)});
                    }
                }
                resultSchichten.close();
            }
            resultTage.close();
            // Anzeige der Anzahl bereits gelöschter Tage im Fortschrittsdialog
            msg = messageHandler.obtainMessage();
            bundle.putCharSequence(
                    MESSAGE,
                    mContext.getString(
                            R.string.progress_dbreinigen,
                            delTage,
                            mContext.getString(R.string.tage
                            ))
            );
            msg = messageHandler.obtainMessage();
            msg.setData(bundle);
            messageHandler.sendMessage(msg);

            jahr++;
            resultTage = datenbank.rawQuery(leseTageSQL, new String[]{Integer.toString(jahr)});
        }
        resultTage.close();
        return status;
    }

    @SuppressLint("Range")
    private boolean loescheLeereMonate(Handler messageHandler, SQLiteDatabase datenbank) {
        boolean status = true;
        Cursor resultStartjahr;
        Cursor resultMonate;
        Cursor resultTag;
        long monatID;
        long jobID;
        int jahr = grenzJahr;
        int monat;
        int delMonate = 0;
        int maxMonate = 0;

        Message msg = messageHandler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putCharSequence(
                MESSAGE,
                mContext.getString(
                        R.string.progress_dbreinigen,
                        delMonate,
                        mContext.getString(R.string.monate
                        ))
        );
        msg.setData(bundle);
        messageHandler.sendMessage(msg);

        // SQL Anweisung zum lesen der Anzahl gespeicherter Monate
        String leseAnzahlMonate = "select count (*) from " +
                DatenbankHelper.DB_T_MONAT +
                " where " + DatenbankHelper.DB_F_JAHR +
                " > ?";

        // SQL Anweisung zum lesen des ersten Monats des nächsten Jahres
        String leseStartJahr = "select " +
                DatenbankHelper.DB_F_JAHR +
                " from " +
                DatenbankHelper.DB_T_MONAT
                + " where " + DatenbankHelper.DB_F_JAHR
                + " > ?"
                + " limit 1";

        // SQL Anweisung zum lesen aller Monate eines Jahres
        String leseMonateSQL = "select " +
                DatenbankHelper.DB_F_ID + ", " +
                DatenbankHelper.DB_T_MONAT + ", " +
                /*Datenbank.DB_F_JAHR + ", " +*/
                DatenbankHelper.DB_F_JOB +
                " from " +
                DatenbankHelper.DB_T_MONAT
                + " where " + DatenbankHelper.DB_F_JAHR
                + " = ?";

        // SQL Anweisung zum lesen eines Tages des Monats
        // sollte ein Tag für diesen Monat gespeichert sein, kann dieser nicht gelöscht werden
        String leseTagSQL =
                "select "
                        + DatenbankHelper.DB_F_ID
                        + " from " + DatenbankHelper.DB_T_TAG
                        + " where " + DatenbankHelper.DB_F_JAHR + " = ? AND "
                        + DatenbankHelper.DB_F_MONAT + " = ? AND "
                        + DatenbankHelper.DB_F_JOB + " = ?"
                        + " limit 1";

        //// SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getWritableDatabase();
        Cursor resultCount = datenbank.rawQuery(
                leseAnzahlMonate, new String[]{Integer.toString(jahr)}
        );

        if(resultCount.getCount() > 0){
            resultCount.moveToFirst();
            int anzahl = resultCount.getInt(0);
            maxMonate = anzahl > 0? anzahl/100 : 0;
        }
        resultCount.close();

        if(maxMonate > 0) {
            // alle Monate Jahresweise lesen lesen
            // bei sehr vielen Jahren in der Datenbank ist der Speicher für die Cursordaten zu klein
            // dazu zuerst den ersten gespeicherten Monat des nächsten Jahres suchen
            resultStartjahr = datenbank.rawQuery(leseStartJahr,
                    new String[]{Integer.toString(jahr)});
            if (resultStartjahr.moveToFirst()) {
                // alle Monate des Jahres lesen
                jahr = resultStartjahr.getInt(resultStartjahr.getColumnIndex(DatenbankHelper.DB_F_JAHR));
                resultMonate = datenbank.rawQuery(leseMonateSQL, new String[]{
                        Integer.toString(jahr)
                });
                // gibt es keine Monate in diesen Jahr, sind wir fertig
                while (resultMonate.getCount() > 0) {
                    while (resultMonate.moveToNext()) {
                        monatID = resultMonate.getLong(resultMonate.getColumnIndex(DatenbankHelper.DB_F_ID));
                        jobID = resultMonate.getLong(resultMonate.getColumnIndex(DatenbankHelper.DB_F_JOB));
                        //jahr = resultMonate.getInt(resultMonate.getColumnIndex(Datenbank.DB_F_JAHR));
                        monat = resultMonate.getInt(resultMonate.getColumnIndex(DatenbankHelper.DB_T_MONAT));

                        // einen Tag lesen
                        resultTag = datenbank.rawQuery(leseTagSQL, new String[]{
                                Integer.toString(jahr),
                                Integer.toString(monat),
                                Long.toString(jobID)
                        });
                        // wenn keine Schicht vorhanden diesen Tag löschen
                        if (resultTag.getCount() <= 0) {
                            delMonate++;
                            datenbank.delete(
                                    DatenbankHelper.DB_T_MONAT,
                                    DatenbankHelper.DB_F_ID + "=?",
                                    new String[]{Long.toString(monatID)});
                        }
                        resultTag.close();
                    }
                    resultMonate.close();
                    // Anzahl bereits gelöschter Monate im Fortschrittsdialog anzeigen
                    msg = messageHandler.obtainMessage();
                    bundle.putCharSequence(
                            MESSAGE,
                            mContext.getString(
                                    R.string.progress_dbreinigen,
                                    Math.min(delMonate / maxMonate, 100),
                                    "%"/*mContext.getString(R.string.monate)*/
                            )
                    );
                    msg.setData(bundle);
                    messageHandler.sendMessage(msg);
                    // das nächste Jahr aus der Datenbank lesen
                    jahr++;
                    resultMonate = datenbank.rawQuery(leseMonateSQL, new String[]{
                            Integer.toString(jahr)
                    });
                }
                resultMonate.close();
            }
            resultStartjahr.close();
        }
        return status;
    }

    @SuppressLint("Range")
    private boolean loescheLeereJahre(Handler messageHandler, SQLiteDatabase datenbank) {
        boolean status = true;
        Cursor resultJahre;
        Cursor resultMonat;
        long JahrID;
        long jobID;
        int jahr;
        int delJahre = 0;

        Message msg = messageHandler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putCharSequence(
                MESSAGE,
                mContext.getString(
                        R.string.progress_dbreinigen,
                        delJahre,
                        mContext.getString(R.string.jahre
                        ))
        );
        msg.setData(bundle);
        messageHandler.sendMessage(msg);

        // SQL Anweisung zum lesen aller Jahre
        String leseJahreSQL = "select " +
                DatenbankHelper.DB_F_ID + ", " +
                DatenbankHelper.DB_F_JAHR + ", " +
                DatenbankHelper.DB_F_JOB +
                " from " +
                DatenbankHelper.DB_T_JAHR;

        // SQL Anweisung zum lesen eines Monats des Jahres
        // sollte ein Monat für dieses Jahr gespeichert sein, kann dieses nicht gelöscht werden
        String leseMonatSQL =
                "select "
                        + DatenbankHelper.DB_F_ID
                        + " from " + DatenbankHelper.DB_T_MONAT
                        + " where " + DatenbankHelper.DB_F_JAHR + " = ? AND "
                        + DatenbankHelper.DB_F_JOB + " = ?"
                        + " limit 1";

        // alle Jahre lesen
        //// SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getWritableDatabase();
        resultJahre = datenbank.rawQuery(leseJahreSQL, new String[]{});
        while (resultJahre.moveToNext()) {
            JahrID = resultJahre.getLong(resultJahre.getColumnIndex(DatenbankHelper.DB_F_ID));
            jobID = resultJahre.getLong(resultJahre.getColumnIndex(DatenbankHelper.DB_F_JOB));
            jahr = resultJahre.getInt(resultJahre.getColumnIndex(DatenbankHelper.DB_F_JAHR));

            // einen Monat lesen
            resultMonat = datenbank.rawQuery(leseMonatSQL, new String[]{
                    Integer.toString(jahr),
                    Long.toString(jobID)
            });
            // wenn keine Schicht vorhanden diesen Tag löschen
            if (resultMonat.getCount() <= 0) {
                delJahre++;
                datenbank.delete(
                        DatenbankHelper.DB_T_JAHR,
                        DatenbankHelper.DB_F_ID + "=?",
                        new String[]{Long.toString(JahrID)});
            }
            resultMonat.close();
            // die Anzahl gelöschter Jahre im Fortschrittsdialog anzeigen
            msg = messageHandler.obtainMessage();
            bundle.putCharSequence(
                    MESSAGE,
                    mContext.getString(
                            R.string.progress_dbreinigen,
                            delJahre,
                            mContext.getString(R.string.jahre
                            ))
            );
            msg.setData(bundle);
            messageHandler.sendMessage(msg);
        }

        resultJahre.close();
        return status;
    }

}
