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


import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.text.format.DateFormat;

import androidx.preference.PreferenceManager;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;

import askanimus.arbeitszeiterfassung2.Datum;
import askanimus.arbeitszeiterfassung2.R;
import askanimus.arbeitszeiterfassung2.arbeitsplatz.Arbeitsplatz;
import askanimus.arbeitszeiterfassung2.arbeitsplatz.ArbeitsplatzListe;
import askanimus.arbeitszeiterfassung2.datenbank.DatenbankHelper;
import askanimus.arbeitszeiterfassung2.feiertage.FeiertageListe;

/**
 * @author askanimus@gmail.com on 22.03.14.
 */
public abstract class ASettings implements ISettings {
    public static int zustand = INIT_ZUSTAND_UNGELADEN;
    private static List<Runnable> ListePosthandler;

    // die Version der App
    public static int appVersionCode;
    public static String appVersionName;
    /*
     * Vom Arbeitsplatz und anderen Einstellungen abhängige Werte
     */
    // die gespeicherten Einstellungen
    public static SharedPreferences mPreferenzen;
    // die Resourcen
    public static Resources res;
    // datenbank Handler wird von der Activity gesetzt
    public static DatenbankHelper stundenDBHelper;
    public static SQLiteDatabase mDatenbank;

    //Aktuelles Datum und letzter angezeigter Tag des aktuellen Arbeitsplatzes
    public static Datum aktDatum;
    public static Datum letzterAnzeigeTag;
    //eingestelltes Zeitformat
    public static SimpleDateFormat zeitformat;
    //public static boolean is24Stunden;
    public static DecimalFormat zahlenformat;
    public static DecimalFormat waehrungformat;
    public static DecimalFormat streckeformat;
    public static DecimalFormat tageformat;
    public static DecimalFormat stundenformat;
    public static String sWaehrung; // Das aktuelle Wahrungssymbol
    //public static String dTrenner;  // Der aktuelle Dezimaltrenner
    public static String sEntfernung; // Die aktuelle Entfernungseinheit

    // aktueller arbeitsplatz
    public static Arbeitsplatz aktJob = null;
    public static ArbeitsplatzListe jobListe;

    // System Thema
    public static boolean isThemaDunkel;

    // einige immer wieder benötigte Farben
    public static int cNegativText;
    public static int cPositivText;
    public static int cManuellText;
    public static int cHintergrundSa;
    public static int cHintergrundSo;
    public static int cHintergrundUrlaub;
    public static int cHintergrundArbeitszeit;
    public static int cHintergrundSonstiges;
    // Themen
    public static int thMain;
    public static int thOhneToolbar;
    public static int themePicker;
    // Regulärer Ausdruck zum ausfiltern von unerlaubten Zeichen im Dateipfad
    public static String REG_EX_PFAD = "[\\s/\"+<>:*\"?\\\\|]";
    // Anzeigeoptionen für Sonstige Werte und Verdienst
    public static Boolean isEinsatzort;
    public static Boolean isVerdienst;
    public static Boolean isZusatzfelder;

    public static FeiertageListe feiertage;

    // Initialisierung in den Hintergrund verschieben
    // und alle Aufrufer in die Posthandler Liste aufnehemen
    // Nach abarbeietn von Init(), alle Aufrufer zurückrufen
    // Dadurch wird init() nur einmal aufgerufen
    public static void init(Context context, Runnable postHandler) {
        if (zustand == INIT_ZUSTAND_UNGELADEN) {
            // der erste Aufruf von init(..)
            zustand = INIT_ZUSTAND_LAEDT;
            ListePosthandler = new ArrayList<>();
            ListePosthandler.add(postHandler);
            Handler mHandler = new Handler();
            new Thread(() -> {
                // Aufruf der Initroutine im neuen Thread
                init(context);
                zustand = INIT_ZUSTAND_GELADEN;
                // aufruf aller Handler, die während des Initlaufs registriert wurden
                //und auf das Ende des Init Laufs warten
                for (Runnable r : ListePosthandler) {
                    mHandler.post(r);
                }
                ListePosthandler.clear();
            }).start();
        } else if(zustand == INIT_ZUSTAND_LAEDT){
            // ein weiterer Aufruf von init(..) den Posthandler ab auf den Stapel
            ListePosthandler.add(postHandler);
        } else {
            // der init(.) Prozess ist schon beendet, gleich die Posthandler Routine aufrufen
            postHandler.run();
        }
    }

    private static void init(Context context) {
        // die Einstellungen öffnen
        mPreferenzen = PreferenceManager.getDefaultSharedPreferences(context);

        // die Ressourcen der App
        res = context.getResources();

        // Datenbank erzeugen, falls noch nicht geschehen und öffnen
        if (stundenDBHelper == null) {
            stundenDBHelper = new DatenbankHelper(context);
        }

        if(mDatenbank == null || !mDatenbank.isOpen()){
           mDatenbank =  stundenDBHelper.getWritableDatabase();
        }

        // das ISO-Währungskürzel entweder aus den Einstellungen oder dem System
        try {
            sWaehrung = mPreferenzen.getString(ISettings.KEY_ANZEIGE_W_KUERZEL, "");
            if (sWaehrung.length() <= 0)
                sWaehrung = Objects.requireNonNull(NumberFormat.getInstance().getCurrency()).getSymbol() /*+ " "*/;
        } catch (NullPointerException e) {
            sWaehrung = Objects.requireNonNull(NumberFormat.getInstance().getCurrency()).getSymbol(Locale.getDefault())/* + " "*/;
        }

        // das Zahlenformat Komma oder Punkt als Dezimaltrenner - wird nicht mehr gebraucht
       /*dTrenner = mPreferenzen.getString(ISetup.KEY_ANZEIGE_W_TRENNER, "");
        if (dTrenner.length() <= 0) {
            dTrenner = String.valueOf(new DecimalFormat().getDecimalFormatSymbols().getDecimalSeparator());
        }*/
        // das Währungsformat
        waehrungformat = new DecimalFormat("###,##0.00 '" + sWaehrung + "'");
        // das im System hinterlegte Zeitformat
        // leider ist ein alter Fehler immernoch vereinzelt in neueren Androidversionen nicht behoben
        // dieser verhindert eine autom. formatierte Anzeige des 24h Formats
        if (DateFormat.is24HourFormat(context.getApplicationContext())) {
            zeitformat = new SimpleDateFormat("HH:mm", Locale.getDefault());
        } else {
            zeitformat = new SimpleDateFormat("h:mm a", Locale.getDefault());
        }
        //zeitformat = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault());
        // Dezimalformat ohne Einheit
        zahlenformat = new DecimalFormat("###,##0.00");
        // Dezimalformat für Tage
        tageformat = new DecimalFormat("###,##0.00 d");
        // Dezimalformat für Stunden
        stundenformat = new DecimalFormat("###,##0.00 '" + res.getString(R.string.k_stunde) + "'");

        // die Einheit für Entfernung entweder Einstellungen oder dem System lesen
        sEntfernung = mPreferenzen.getString(ISettings.KEY_ANZEIGE_E_KUERZEL, "km");

        streckeformat = new DecimalFormat("###,##0.00 '" + sEntfernung + "'");


        // ermitteln ob das dunkle Farbschema angezigt werden soll
        isThemaDunkel = mPreferenzen.getBoolean(ISettings.KEY_THEMA_DUNKEL, false);

        if (mPreferenzen.contains(ISettings.KEY_INIT_FINISH)) {
            // den letzten geöffneten Job lesen oder den ersten in der Tabelle
            aktJob = new Arbeitsplatz(mPreferenzen.getLong(ISettings.KEY_JOBID, 0));
        } else {
            // ersten Arbeitsplatz lesen oder anlegen
            aktJob = new Arbeitsplatz(mPreferenzen.getLong(ISettings.KEY_JOBID,0));
            mPreferenzen.edit().putLong(ISettings.KEY_EDIT_JOB, aktJob.getId()).apply();
            mPreferenzen.edit().putLong(ISettings.KEY_JOBID, aktJob.getId()).apply();
        }

        // die Arbeitsplatzliste anlegen
        jobListe = new ArbeitsplatzListe(aktJob);

        /*
         * Ab Version 2.01.00 wird, bei Wahl der Urlaubsabrechnung in Stunden,
         * der Urlaubsanspruch und der Resturlaub in Minuten gespeichert
         * beim ersten Start der App nach dem Update bzw. nach dem Wiederherstellen
         * einer alten Datensicherung werden die Werte einmalig umgerechnet
         */
        if(!mPreferenzen.getBoolean(ISettings.KEY_URLAUB_ALS_H_UMGERECHENET, false)) {
            for (Arbeitsplatz job : jobListe.getListe()) {
                if (job.isOptionSet(Arbeitsplatz.OPT_URLAUB_ALS_STUNDEN)) {
                    job.setSoll_Urlaub(job.getSoll_Urlaub() * job.getSollstundenTagPauschal());
                    job.setStart_Urlaub(job.getStart_Urlaub() * job.getSollstundenTagPauschal());
                    job.schreibeJob();
                }
            }
            mPreferenzen.edit().putBoolean(ISettings.KEY_URLAUB_ALS_H_UMGERECHENET, true).apply();
        }

        // Aktuelles Datum setzen und Zeitwerte auf 0 setzen
        aktDatum = new Datum(new Date(), aktJob.getWochenbeginn());

        // den letzten anzuzeigenden Tag festlegen
        letzterAnzeigeTag = new Datum(getLetzterAnzeigeTag(aktJob));

        // Anzeigeoptionen für Sonstigewerte und Verdienst ermitteln
        setAnzeigeOptionen();

        // Einige Farben je nach App Theme vorbelegen
        setFarben();

        // Die Liste gespeicherter Feiertage anlegen
        feiertage = new FeiertageListe(mDatenbank);
    }



    // Einstellungen der optionalen Werte
    public static void setAnzeigeOptionen() {
        isEinsatzort = aktJob.isOptionSet(Arbeitsplatz.OPT_WERT_EORT);
        isVerdienst = aktJob.getStundenlohn() > 0;
        isZusatzfelder = aktJob.getZusatzfeldListe().size() > 0;
    }

    // Einige Farben an Hand des gewählten Themas erzeugen
    public static void setFarben() {
        if (isThemaDunkel) {
            thMain = R.style.MyAppTheme;
            themePicker = R.style.BetterPickersDialogFragment;
            thOhneToolbar = R.style.MyFullscreenTheme;

            cNegativText = res.getColor(android.R.color.holo_red_light);
            cPositivText = res.getColor(android.R.color.holo_green_light);
            cManuellText = res.getColor(android.R.color.holo_purple);
            cHintergrundSa = res.getColor(R.color.samstag_light);
            cHintergrundSo = res.getColor(R.color.sonntag_light);
            cHintergrundArbeitszeit = res.getColor(R.color.box_rot_light);
            cHintergrundUrlaub = res.getColor(R.color.box_gruen_light);
            cHintergrundSonstiges = res.getColor(R.color.box_blau_light);
        } else {
            thMain = R.style.MyAppTheme_Light;
            thOhneToolbar = R.style.MyFullscreenTheme_Light;
            themePicker = R.style.BetterPickersDialogFragment_Light;

            cNegativText = res.getColor(android.R.color.holo_red_dark);
            cPositivText = res.getColor(android.R.color.holo_green_dark);
            cManuellText = res.getColor(android.R.color.holo_purple);
            cHintergrundSa = res.getColor(R.color.samstag);
            cHintergrundSo = res.getColor(R.color.sonntag);
            cHintergrundArbeitszeit = res.getColor(R.color.box_rot);
            cHintergrundUrlaub = res.getColor(R.color.box_hgruen);
            cHintergrundSonstiges = res.getColor(R.color.box_blau);
        }
    }

    /*
     * Wertrückgaben
     */

    // Ausgabe des letzten angezeigten Tages eines bestimmten Arbeitsplatzes
    public static Datum getLetzterAnzeigeTag(Arbeitsplatz job) {
        // den letzten Tag auf den aktuellen Tag setzen
        Datum mLetzterTag = new Datum(getAktDatum());

        // den letzten Tag der Anzeige berechnen
        if (job.isAnzeigeZukunft()) {
            mLetzterTag.add(Calendar.MONTH, job.getMonate_Zukunft());

            if (job.getMonatsbeginn() > 1) {
                if (job.getMonatsbeginn() <= aktDatum.get(Calendar.DAY_OF_MONTH)) {
                    mLetzterTag.setTag(mLetzterTag.getAktuellMaximum(Calendar.DAY_OF_MONTH));
                    mLetzterTag.add(Calendar.DAY_OF_MONTH, job.getMonatsbeginn() - 1);
                } else {
                    mLetzterTag.setTag(job.getMonatsbeginn() - 1);
                }
            } else {
                mLetzterTag.setTag(mLetzterTag.getAktuellMaximum(Calendar.DAY_OF_MONTH));
            }
        }


        if (job.isEndeAufzeichnung(mLetzterTag)) {
            mLetzterTag.set(job.getEndDatum().getDate());
        }

        return (mLetzterTag);
    }

    // den aktuellen Arbeitsplatz wechseln
    public static Arbeitsplatz setAktivJob(long jobID){
        aktJob = jobListe.newAktivJob(jobID);
        SharedPreferences.Editor mEdit = mPreferenzen.edit();
        mEdit.putLong(KEY_JOBID, jobID).apply();
        letzterAnzeigeTag.set(getLetzterAnzeigeTag(aktJob).getCalendar());
        setAnzeigeOptionen();
        setFarben();
        return aktJob;
    }

    // einen Job aus der Jobliste lesen
    public static Arbeitsplatz getArbeitsplatz(long jobID){
        return jobListe.getVonID(jobID);
    }

    // Das aktuelle Datum
    public static Datum getAktDatum(){
        if(aktDatum == null){
          aktDatum = new Datum(new Date(), aktJob.getWochenbeginn());
        }
        return aktDatum;
    }

    /**
     *
     * Datenbank löschen und neue leere Datenbank anlegen
     *
     * @param context der Applikationscontext
     */
    public static void resetDatenbank(Context context) {
        if(stundenDBHelper != null) {
            stundenDBHelper.close();
        }
        context.deleteDatabase(res.getString(R.string.dbName));
        aktJob = null;
        jobListe = null;
        stundenDBHelper = new DatenbankHelper(context);
        mDatenbank = stundenDBHelper.getWritableDatabase();
    }
}
