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

/* This File is part of "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2).
 *
 * "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2).  If not, see <http://www.gnu.org/licenses/>.
 *
 * Diese Datei ist Teil von "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2).
 *
 * "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2) ist Freie Software: Sie können es unter den Bedingungen
 * der GNU General Public License, wie von der Free Software Foundation,
 * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren
 * veröffentlichten Version, weiter verteilen und/oder modifizieren.
 *
 * "Arbeitszeitkonto" (askanimus.arbeitszeiterfassung2) wird in der Hoffnung, dass es nützlich sein wird, aber
 * OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
 * Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
 * Siehe die GNU General Public License für weitere Details.
 *
 * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
 * Programm erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.
 */
package askanimus.arbeitszeiterfassung2.export;

import android.content.Context;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Locale;

import askanimus.arbeitszeiterfassung2.Datum;
import askanimus.arbeitszeiterfassung2.R;
import askanimus.arbeitszeiterfassung2.StorageHelper;
import askanimus.arbeitszeiterfassung2.Uhrzeit;
import askanimus.arbeitszeiterfassung2.Zeitraum.Zeitraum_Frei;
import askanimus.arbeitszeiterfassung2.abwesenheiten.Abwesenheit;
import askanimus.arbeitszeiterfassung2.arbeitsplatz.Arbeitsplatz;
import askanimus.arbeitszeiterfassung2.arbeitsschicht.Arbeitsschicht;
import askanimus.arbeitszeiterfassung2.arbeitstag.Arbeitstag;
import askanimus.arbeitszeiterfassung2.einsatzort.Einsatzort;
import askanimus.arbeitszeiterfassung2.feiertage.Feiertag;
import askanimus.arbeitszeiterfassung2.setup.ASettings;
import askanimus.arbeitszeiterfassung2.setup.ISettings;
import askanimus.arbeitszeiterfassung2.zusatzeintrag.IZusatzfeld;

/**
 * @author askanimus@gmail.com on 10.01.16.
 */
public class Export_CSV_Einsatzort extends AExportBasis_CSV {
    private Datum mBeginn;
    private Datum mEnde;
    private ArrayList<Spalte> mSpaltenSatz; // Namen und Positionen der einzelnen Spalten
    private BitSet mSpalten;
    private BitSet mOptionen;
    private BitSet mZusatzwerte;
    private boolean isSpalteTagsoll = false;
    private Arbeitsplatz mArbeitsplatz;
    private ArrayList<Arbeitsplatz> mArbeitsplatzListe;
    private ArrayList<Job_EortSchichten> mArbeitsplatzEortListe;
    
    private String sTitel;

    //Formater für Wochentag
    DateFormat fWochentag = new SimpleDateFormat("E", Locale.getDefault());

    // Formater für kurzes Datum
    DateFormat fDatum = new SimpleDateFormat("d.M.yyyy", Locale.getDefault());



    Export_CSV_Einsatzort(
            Context ctx,
            Arbeitsplatz job,
            Datum beginn,
            Datum ende,
            BitSet optionen,
            BitSet spalten,
            BitSet zusatzwerte,
            StorageHelper storageHelper,
            ArrayList<MultiSelectItem> listeOrte
            //String pfad
    ) throws Exception {
        super(ctx);
        mBeginn = beginn;
        mEnde = ende;
        // Ausgabeoptionen
        mSpalten = spalten;
        // nicht benötigte Spalten ausschalten
        mSpalten.set(SPALTE_EORT, false);
        mSpalten.set(SPALTE_TAGSOLL, false);
        mSpalten.set(SPALTE_TAGSALDO, false);

        mZusatzwerte = zusatzwerte;
        mOptionen = optionen;

        if (mOptionen.get(OPTION_ALL_JOBS)) {
            mArbeitsplatzListe = ASettings.jobListe.getListe();
        } else {
            mArbeitsplatzListe = new ArrayList<>();
            mArbeitsplatzListe.add(job);
        }

        String mDateiname;
        if (mOptionen.get(OPTION_NUR_ORTE)) {
            // Nur die Einsatzorte als Liste ausgeben
            sTitel = ASettings.res.getString(R.string.eort_liste);

            if (mOptionen.get(OPTION_ALL_JOBS)) {
                mDateiname = ctx.getString(
                        R.string.exp_dateiname_eort,
                        ASettings.mPreferenzen.getString(ISettings.KEY_USERNAME, "")
                                .replaceAll(ASettings.REG_EX_PFAD, "_"),
                        ctx.getString(R.string.eort_liste).replaceAll(ASettings.REG_EX_PFAD, "_")
                );
                //mDateiname = ExportActivity.makeExportpfad(mContext, pfad, mDateiname);

            } else {
                mDateiname = ctx.getString(
                        R.string.exp_dateiname_eort,
                        getBasisDateiname(),
                        ctx.getString(R.string.eort_liste).replaceAll(ASettings.REG_EX_PFAD, "_")
                );
                //mDateiname = ExportActivity.makeExportpfad(mContext, pfad, mDateiname);

            }
        } else {
            // Einsatzorte mit Schichten
            mArbeitsplatzEortListe = new ArrayList<>();
            sTitel = mBeginn.getString_Datum_Bereich(
                    mContext,
                    0,
                    mBeginn.tageBis(mEnde),
                    Calendar.DAY_OF_MONTH
            );

            // Daten sammeln
            for (Arbeitsplatz j : mArbeitsplatzListe) {
                Zeitraum_Frei zeitraum = new Zeitraum_Frei(j, mBeginn, mEnde);
                ArrayList<Einsatzort> orte;
                if(listeOrte != null){
                    orte = new ArrayList<Einsatzort>();
                    for (MultiSelectItem ort : listeOrte) {
                        if(ort.getIdArbeitsplatz() == j.getId()){
                            orte.add(j.getEinsatzortListe().getOrt(ort.getIdOrt()));
                        }
                    }
                } else {
                    orte = j.getEinsatzortListe().getAlle();
                }
                //mArbeitsplatzEortListe.add(new Job_EortSchichten(j, mBeginn, mEnde));
                mArbeitsplatzEortListe.add(new Job_EortSchichten(orte, zeitraum.getTage()));
            }

            SimpleDateFormat fDatum = new SimpleDateFormat("yyyy_MM_d", Locale.getDefault());

            mDateiname = ctx.getString(
                    R.string.exp_dateiname_zeitraum,
                    ASettings.mPreferenzen.getString(ISettings.KEY_USERNAME, "")
                            .replaceAll(ASettings.REG_EX_PFAD, "_"),
                    ctx.getString(R.string.eort_liste).replaceAll(ASettings.REG_EX_PFAD, "_"),
                    fDatum.format(mBeginn.getDate()) + "-",
                    fDatum.format(mEnde.getDate())
            );
            //mDateiname = ExportActivity.makeExportpfad(mContext, pfad, mDateiname);
        }

        schreibeTabelle(storageHelper, mDateiname, TYP_CSV);
    }

    @Override
    public ArrayList<ArrayList<Zelle_CSV>> erzeugeTabelle() {
        ArrayList<ArrayList<Zelle_CSV>>  mTabelle = new ArrayList<>();

        for (int i = 0; i < mArbeitsplatzListe.size(); i++) {
            mArbeitsplatz = mArbeitsplatzListe.get(i);
            if(i>0){
                // 4 Leehrzeilen einfügen
                makeLeerzeile(mTabelle,4);
            }

            if (mOptionen.get(OPTION_NUR_ORTE)) {
                // Nur die Einsatzorte der Arbeitsplätze auflisten
                
                // den Seitentitel schreiben
                erzeugeSeitenTitel(mTabelle, mArbeitsplatz, sTitel);
                
                // Liste der Einsatzorte dieses Arbeitsplatzes schreiben
                erzeugeTabelleEort(mTabelle, mArbeitsplatz);
            } else {
                mSpaltenSatz = makeSpaltenSet(mSpalten, mZusatzwerte, mArbeitsplatzListe.get(0));
                // die Einsatzorte samt Schichten auflisten
                // pro einsatzort eine Tabelle
                
                // den Seitentitel schreiben
                erzeugeSeitenTitel(mTabelle, mArbeitsplatz, sTitel);

                // für jeden Einsatzort eine Tabell
                for (Job_EortSchichten job: mArbeitsplatzEortListe) {
                    // Datenzeilen Tage/Schichten
                    for (EortSchichten  eorte: job.EinsatzortSchichten) {
                        // Tabellentitel (Einsatzortname) schreiben
                        makeTabellenName(mTabelle, eorte.Eort.getName());
                        makeLeerzeile(mTabelle,1);
                        // die Kopfzeile der Tabelle
                        makeTabellenKopf(mTabelle);
                        // die einzelnen Schgichten
                        makeSchichtenTabelle(mTabelle, eorte.Schichten);
                        makeLeerzeile(mTabelle,2);
                    }
                }
            }
        }

        return mTabelle;
    }

    private void makeTabellenName(ArrayList<ArrayList<Zelle_CSV>> tabelle, String name){
        ArrayList<Zelle_CSV> row = new ArrayList<>();
        row.add(new Zelle_CSV(""));
        row.add(new Zelle_CSV(""));
        row.add(new Zelle_CSV(name));
        tabelle.add(row);
    }

    public void makeTabellenKopf(ArrayList<ArrayList<Zelle_CSV>> tabelle){
        ArrayList<Zelle_CSV> row = new ArrayList<>();
        for (AExportBasis.Spalte spalte: mSpaltenSatz) {
            if(spalte.mSpalte == SPALTE_DATUM){
                row.add(makeZelleString(ASettings.res.getString(R.string.wochentag)));
            }
            row.add(makeZelleString(spalte.mName));
            if(spalte.mSpalte == SPALTE_TAGSOLL){
                isSpalteTagsoll = true;
            }
        }
        tabelle.add(row);
    }

    public void makeSchichtenTabelle(ArrayList<ArrayList<Zelle_CSV>> tabelle, ArrayList<Schicht> schichten) {
        Arbeitsschicht aSchicht;
        Uhrzeit mZeit = new Uhrzeit(0);
        ArrayList<Zelle_CSV> row;
        int gesamtBrutto = 0;
        int gesamtPause = 0;
        float gesamtVerdienst = 0;
        ArrayList<Object> gesamtZusatzwerte = new ArrayList<>(mArbeitsplatz.getZusatzfeldListe().size());


        for (Schicht s : schichten) {
            aSchicht = s.Schicht;
            row = new ArrayList<>();

            for (Spalte spalte : mSpaltenSatz) {
                switch (spalte.mSpalte) {
                    case SPALTE_DATUM:
                        row.add(new Zelle_CSV(fWochentag.format(s.Datum)));
                        row.add(new Zelle_CSV(fDatum.format(s.Datum)));
                        break;
                    case SPALTE_SCHICHTNAME:
                        int kategorie = aSchicht.getAbwesenheit().getKategorie();
                        if (kategorie == Abwesenheit.KAT_ARBEITSZEIT) {
                            row.add(new Zelle_CSV(aSchicht.getName()));
                        } else {
                            if (kategorie == Abwesenheit.KAT_FEIERTAG) {
                                Feiertag feiertag = ASettings.feiertage.getFeiertag(new Datum(s.Datum, mArbeitsplatz.getWochenbeginn()));
                                if (feiertag != null) {
                                    row.add(makeZelleString(feiertag.getName()));
                                } else {
                                    row.add(makeZelleString(aSchicht.getAbwesenheit().getName()));
                                }
                            } else {
                                row.add(new Zelle_CSV(aSchicht.getAbwesenheit().getName()));
                            }
                        }
                        break;
                    case SPALTE_VON:
                        if (aSchicht.getAbwesenheit().getWirkung() == Abwesenheit.WIRKUNG_IST_PLUS_EFFEKTIV) {
                            row.add(new Zelle_CSV(aSchicht.getVon(), true));
                        } else {
                            row.add(new Zelle_CSV(""));
                        }
                        break;
                    case SPALTE_BIS:
                        if (aSchicht.getAbwesenheit().getWirkung() == Abwesenheit.WIRKUNG_IST_PLUS_EFFEKTIV) {
                            row.add(new Zelle_CSV(aSchicht.getBis(), true));
                        } else {
                            row.add(new Zelle_CSV(""));
                        }
                        break;
                    case SPALTE_BRUTTO:
                        row.add(new Zelle_CSV(aSchicht.getBrutto(), false));
                        gesamtBrutto += aSchicht.getBrutto();
                        break;
                    case SPALTE_PAUSE:
                        row.add(new Zelle_CSV(aSchicht.getPause(), false));
                        gesamtPause += aSchicht.getPause();
                        break;
                    case SPALTE_NETTO:
                        row.add(new Zelle_CSV(aSchicht.getNetto(), false));
                        break;
                    case SPALTE_VERDIENST:
                        mZeit.set(aSchicht.getNetto());
                        float v = mZeit.getAlsDezimalZeit() * mArbeitsplatz.getStundenlohn();
                        row.add(new Zelle_CSV(v));
                        gesamtVerdienst += v;
                        break;
                    default:
                        if (spalte.mSpalte >= SPALTE_ZUSATZ) {
                            int zusatzSpalte = spalte.mSpalte - SPALTE_ZUSATZ;
                            IZusatzfeld zfeld = aSchicht.getZusatzwert(zusatzSpalte);
                            switch (zfeld.getDatenTyp()) {
                                case IZusatzfeld.TYP_BEREICH_ZAHL:
                                case IZusatzfeld.TYP_ZAHL:
                                    row.add(new Zelle_CSV((float) zfeld.getWert()));
                                    gesamtZusatzwerte.set(
                                            zusatzSpalte,
                                            (float)gesamtZusatzwerte.get(zusatzSpalte) + (float) zfeld.getWert()
                                            );
                                    break;
                                case IZusatzfeld.TYP_BEREICH_ZEIT:
                                case IZusatzfeld.TYP_ZEIT:
                                    row.add(new Zelle_CSV((int) zfeld.getWert(), false));
                                    gesamtZusatzwerte.set(
                                            zusatzSpalte,
                                            (int)gesamtZusatzwerte.get(zusatzSpalte) + (int) zfeld.getWert()
                                            );
                                    break;
                                default:
                                    row.add(new Zelle_CSV(zfeld.getString(false)));
                            }
                        } else {
                            row.add(new Zelle_CSV(""));
                        }
                }
            }
            tabelle.add(row);
        }


        // die Zsammenfassung
        row = new ArrayList<>();
        row.add(new Zelle_CSV(ASettings.res.getString(R.string.summe)));

        for (Spalte spalte : mSpaltenSatz) {
            switch (spalte.mSpalte) {
                case SPALTE_BRUTTO:
                    row.add(new Zelle_CSV(gesamtBrutto, false));
                    break;
                case SPALTE_PAUSE:
                    row.add(new Zelle_CSV(gesamtPause, false));
                    break;
                case SPALTE_NETTO:
                    row.add(new Zelle_CSV(gesamtBrutto - gesamtPause, false));
                    break;
                case SPALTE_VERDIENST:
                    mZeit.set(gesamtVerdienst);
                    row.add(new Zelle_CSV(mZeit.getAlsDezimalZeit() * mArbeitsplatz.getStundenlohn()));
                    break;
                default:
                    if (spalte.mSpalte >= SPALTE_ZUSATZ) {
                        int zusatzSpalte = spalte.mSpalte - SPALTE_ZUSATZ;
                        switch (mArbeitsplatz.getZusatzfeldListe().get(zusatzSpalte).getTyp()) {
                            case IZusatzfeld.TYP_BEREICH_ZAHL:
                            case IZusatzfeld.TYP_ZAHL:
                                row.add(new Zelle_CSV((float) gesamtZusatzwerte.get(zusatzSpalte)));
                                break;
                            case IZusatzfeld.TYP_BEREICH_ZEIT:
                            case IZusatzfeld.TYP_ZEIT:
                                row.add(new Zelle_CSV((int) gesamtZusatzwerte.get(zusatzSpalte), false));
                                break;
                            default:
                                row.add(new Zelle_CSV(""));

                        }
                    } else {
                        row.add(new Zelle_CSV(""));
                    }
            }
        }
        tabelle.add(row);
    }

    // die Liste aller Einsatzorte eines Arbeitsplatzes
    private void erzeugeTabelleEort(ArrayList<ArrayList<Zelle_CSV>> tabelle, Arbeitsplatz job){
        ArrayList<Zelle_CSV> row;
        int aZeilen = job.getEinsatzortListe().getMenge(ISettings.STATUS_AKTIV);
        int pZeilen = job.getEinsatzortListe().getMenge(ISettings.STATUS_INAKTIV);
        int maxZeilen = Math.max(aZeilen, pZeilen);

        // Tabellenkopf
        row = new ArrayList<>();
        row.add(new Zelle_CSV(ASettings.res.getString(R.string.eort_sichtbar)));
        row.add(new Zelle_CSV(""));
        row.add(new Zelle_CSV(ASettings.res.getString(R.string.eort_ausgeblendet)));
        tabelle.add(row);

        // Datenzeilen
        for (int i = 0; i < maxZeilen; i++) {
            row = new ArrayList<>();
            if(i < aZeilen) {
                row.add(new Zelle_CSV(job.getEinsatzortListe().getAktive().get(i).getName()));
                row.add(new Zelle_CSV(""));
            } else {
                row.add(new Zelle_CSV(""));
                row.add(new Zelle_CSV(""));
            }
            if(i < pZeilen)
               row.add(new Zelle_CSV(job.getEinsatzortListe().getPassive().get(i).getName()));

            tabelle.add(row);
        }

    }

    /*
     * Datenstrukturen
     */
    private static class Job_EortSchichten{
        Arbeitsplatz Job;
        ArrayList<EortSchichten> EinsatzortSchichten;

        //Job_EortSchichten(Arbeitsplatz job, Datum von, Datum bis){
        Job_EortSchichten(ArrayList<Einsatzort> orte, ArrayList<Arbeitstag> tage){
            /*Datum Von = new Datum(von);
            Job = job;

            // alle Arbeitstage des Arbeitsplatzes des gegebenen Zeitraums lesen
            ArrayList<Arbeitstag> Tage;
            Tage = new ArrayList<>();
            while (!bis.liegtVor(Von)) {
                Tage.add(new Arbeitstag(
                        Von.getCalendar(),
                        Job,
                        Job.getSollstundenTag(Von),
                        Job.getSollstundenTagPauschal(Von.get(Calendar.YEAR), Von.get(Calendar.MONTH))
                ));
                Von.add(Calendar.DAY_OF_MONTH, 1);
            }*/

            // Aus der Tagesliste eine Liste aller Einsatzorte
            // und deren zugehörenden Schichten erstellen
            EinsatzortSchichten = new ArrayList<>();
            for (Einsatzort eort : orte) {
                EinsatzortSchichten.add(new EortSchichten(eort));
            }
            /*for (Einsatzort eort: Job.getEinsatzortListe().getAlle()) {
                EinsatzortSchichten.add(new EortSchichten(eort));
            }*/


            for (Arbeitstag aTag: tage ) {
                for (Arbeitsschicht aSchicht:aTag.getSchichten()) {
                    long idEo = aSchicht.getIdEinsatzort();
                    if(idEo > 0){
                        for (EortSchichten eos: EinsatzortSchichten) {
                            if ( aSchicht.getIdEinsatzort() == eos.Eort.getId()){
                                eos.Schichten.add(
                                        new Schicht(aTag.getKalender().getTimeInMillis(), aSchicht));
                                break;
                            }
                        }
                    }
                }
            }
        }

        ArrayList<Schicht> getSchichtListEort(long eortID){
            ArrayList<Schicht> mListe = new ArrayList<>();
            for (EortSchichten eos : EinsatzortSchichten) {
                if (eos.Eort.getId() == eortID){
                    mListe = eos.Schichten;
                    break;
                }
            }
            return mListe;
        }
    }

    private static class Schicht{
        long Datum;
        Arbeitsschicht Schicht;

        Schicht(long d, Arbeitsschicht s){
            Datum = d;
            Schicht = s;
        }
    }

    private static class EortSchichten{
        Einsatzort Eort;
        ArrayList<Schicht> Schichten;

        EortSchichten(Einsatzort eort){
            Eort = eort;
            Schichten = new ArrayList<>();
        }
    }
}
