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

import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.documentfile.provider.DocumentFile;

import java.io.File;

import askanimus.arbeitszeiterfassung2.datenbank.DatenbankHelper;
import askanimus.arbeitszeiterfassung2.setup.ASettings;
import askanimus.arbeitszeiterfassung2.setup.ISettings;
import askanimus.arbeitszeiterfassung2.setup.SettingsActivity;

/**
 * Created by gert on 10.12.17.
 */

public class StorageHelper implements ISettings {
    private final static int STATUS_NO_EXIST = -1;
    private final static int STATUS_EXIST = 0;
    private final static int STATUS_READABLE = 1;
    private final static int STATUS_WRITEABLE = 2;

    // Status des Dateipfades
    int status = STATUS_NO_EXIST;

    final Activity mActivity;
    final Context mContext;
    DocumentFile mFile = null;
    String mPfad;
    String mPfadSubtree;
    String keyDatenbank;
    String keyPreferenzen;
    boolean isSchreiben;
    int mRequestCode;
    //private selectFolder mCallback;

    public StorageHelper(@NonNull Activity activity){
        mActivity = activity;
        mContext = mActivity.getApplicationContext();
    }

    public StorageHelper(
            Activity activity,
            String pfad,
            String keyDB,
            String keyPrefs,
            boolean schreiben,
            int reqestCode/*,
            selectFolder callback*/) {
        mActivity = activity;
        mContext = mActivity.getApplicationContext();
        keyDatenbank = keyDB;
        keyPreferenzen = keyPrefs;
        isSchreiben = schreiben;
        mRequestCode = reqestCode;
        //mCallback = callback;

        setPfad(pfad);
    }

    public StorageHelper(
            Context context,
            String pfad,
            String keyDB,
            String keyPrefs,
            boolean schreiben,
            int reqestCode/*,
            selectFolder callback**/) {
        mActivity = null;
        mContext = context;
        keyDatenbank = keyDB;
        keyPreferenzen = keyPrefs;
        isSchreiben = schreiben;
        mRequestCode = reqestCode;
        //mCallback = callback;

        setPfad(pfad);
    }

    public void setUp(
            String pfad,
            String keyDB,
            String keyPrefs,
            boolean schreiben,
            int reqestCode/*,
            selectFolder callback*/){
        keyDatenbank = keyDB;
        keyPreferenzen = keyPrefs;
        isSchreiben = schreiben;
        mRequestCode = reqestCode;
        //mCallback = callback;

        setPfad(pfad);
    }

    public void setPfad(String pfad) {
        mPfad = pfad;
        if (isStorageMounted() && pruefeAllgmeinesDateirecht()) {
            if (mPfad != null && !mPfad.isEmpty()) {
                // es ist schon SFA Pfad
                if (mPfad.contains("content://") && mPfad.contains("%3A")) {
                    mPfadSubtree = mPfad.substring(mPfad.lastIndexOf("%3A") + 3);
                    mPfadSubtree = mPfadSubtree.replace("%2F", File.separator);
                    // das Verzeichnisfile anlegen
                    if (mPfad != null && !mPfad.isEmpty()) {
                        mFile = DocumentFile.fromTreeUri(
                                mContext,
                                Uri.parse(mPfad));

                        // Existiert das Verzeichnis
                        if (mFile != null && mFile.exists() && mFile.isDirectory()) {
                            status = STATUS_EXIST;
                        }
                    }
                } else {
                    // es ist noch ein konvetioneller Pfad
                    File f = new File(mPfad);
                    Uri uri = makeTreeURIfromFile(f);
                    mPfad = uri.toString();
                    mPfadSubtree = mPfad.substring(mPfad.lastIndexOf("%3A") + 3);
                    mPfadSubtree = mPfadSubtree.replace("%2F", File.separator);

                    if(f.exists()){
                        mFile = DocumentFile.fromTreeUri(
                                mContext,
                                Uri.parse(mPfad));
                        status = STATUS_EXIST;
                    }
                }


                if(isExists()) {
                    // die Zugriffsrechte prüfen
                    if (isSchreiben) {
                        pruefePfadBeschreibbar();
                    } else {
                        pruefePfadLesbar();
                    }
                } else {
                    // das Verzeichnis ist nicht vorhanden
                    // ein Dialog wird angezeigt, der die Möglichkeit zum anlegen offeriert
                    if (mActivity != null) {
                        androidx.appcompat.app.AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
                        builder.setMessage(ASettings.res.getString(R.string.info_kein_Ordner, getPfadSubtree()));
                        builder.setPositiveButton(android.R.string.ok,
                                (dialog, id) -> waehlePfad());
                        builder.setNegativeButton(android.R.string.cancel,
                                (dialog, id) -> {
                                    dialog.dismiss();
                                    if (mActivity.getClass() != SettingsActivity.class) {
                                        mActivity.onBackPressed();
                                    }
                                });
                        builder.create().show();
                    }
                }
            }
        }
    }


    /*
     * Prüfen das Lese- /Schreibrecht auf ext. Speicher
     */
    private boolean pruefeAllgmeinesDateirecht(){
        if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
            String recht = isSchreiben
                    ? Manifest.permission.WRITE_EXTERNAL_STORAGE
                    : Manifest.permission.READ_EXTERNAL_STORAGE;
            if (ContextCompat.checkSelfPermission(mContext, recht) == PackageManager.PERMISSION_DENIED) {
                // Dateirechte anfordern
                // Soll ein Hinweis angezeigt werden?
                if (mActivity != null) {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, recht)) {
                        // Hinweis anzeigen der die Notwendigkeit der Gewährung der Schreibrechte
                        // erklärt.
                        AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
                        builder.setMessage(ASettings.res.getString(R.string.info_keine_berechtigung));
                        builder.setPositiveButton(android.R.string.ok,
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int id) {
                                        ActivityCompat.requestPermissions(mActivity,
                                                new String[]{recht},
                                                isSchreiben ? REQ_DEMAND_WRITE : REQ_DEMAND_READ);
                                    }
                                });
                        builder.create().show();
                    } else {
                        // Keine Erklärung notwendig.
                        ActivityCompat.requestPermissions(mActivity,
                                new String[]{recht},
                                isSchreiben ? REQ_DEMAND_WRITE : REQ_DEMAND_READ);
                    }
                }
                return false;
            } else {
                return true;
            }
        }
        return true;
    }

    /*
     * Prüfe ob Pfad lesbar ist
     */
    private void pruefePfadLesbar() {
            if (mFile != null && mFile.canRead()) {
                status = STATUS_READABLE;
                savePfad();
            } else {
                if (mActivity != null) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
                    builder.setMessage(ASettings.res.getString(R.string.info_keine_Sicherungen));
                    builder.setPositiveButton(android.R.string.ok,
                            (dialog, id) -> waehlePfad());
                    builder.setNegativeButton(android.R.string.cancel,
                            (dialog, id) -> {
                                mPfad = "";
                                dialog.dismiss();
                            });
                    builder.create().show();
                }
            }

    }


    /*
     * Prüfe ob Pfad beschreibbar ist
     */
    private void pruefePfadBeschreibbar() {
        if (mFile != null && mFile.canWrite()) {
            // das Verzeichnis ist vorhanden und beschreibbar
            status = STATUS_WRITEABLE;
            savePfad();
        } else {
            // das Verzeichnis ist vorhanden aber die App hat noch kein Schreibrecht
            if (mActivity != null) {
                androidx.appcompat.app.AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
                builder.setMessage(ASettings.res.getString(R.string.info_berechtigung_erneuern, getPfadSubtree()));
                builder.setPositiveButton(android.R.string.ok,
                        (dialog, id) -> waehlePfad());
                builder.setNegativeButton(android.R.string.cancel,
                        (dialog, id) -> {
                            dialog.dismiss();
                            if (mActivity.getClass() != SettingsActivity.class) {
                                mActivity.onBackPressed();
                            }
                        });
                builder.create().show();
            }
        }
    }

    /*
     * den Dialog zur Auswhal bzw. zum erstellen eines neuen Verzeichnisses öffnen
     * das Ergebnis wird an die hinterlegte Activity gesendet
     */
    public void waehlePfad() {
        if (mActivity != null) {
            // das Verzeichnis ist noch nicht vorhanden oder soll neu gewählt werden
            // Die Verzeichnisauswahl öffnen
            Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
            intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
            intent.putExtra("android.content.extra.FANCY", true);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                if (mFile != null) {
                    intent.putExtra(
                            DocumentsContract.EXTRA_INITIAL_URI,
                            mFile.getUri().toString()
                    );
                }
            }

            if (isSchreiben) {
                intent.addFlags(
                        Intent.FLAG_GRANT_READ_URI_PERMISSION
                                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
                );
            } else {
                intent.addFlags(
                        Intent.FLAG_GRANT_READ_URI_PERMISSION
                                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
                );
            }

            try {
                mActivity.startActivityForResult(intent, mRequestCode);
            } catch (ActivityNotFoundException e) {
                Toast.makeText(mActivity, R.string.no_dateimanager, Toast.LENGTH_LONG).show();
            }
        }
    }


    public String getPfad(){
        return mPfad;
    }

    public String getPfadSubtree(){
        return mPfadSubtree;
    }

    public DocumentFile getVerzeichnisFile() {
        return mFile;
    }



    /*
     * Ist der angeforderte Speicher eingebunden?
     */
    public boolean isStorageMounted() {
        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }

    /*
     * ist das Verzeichnis vorhanden
     */
    public boolean isExists(){
        return status >= STATUS_EXIST;
    }

    /*
     * ist das Verzeichnis beschreibbar
     */
    public boolean isWritheable(){
        return status == STATUS_WRITEABLE;
    }

    /*
     * ist das Verzeichnis beschreibbar
     */
    public boolean isReadable(){
        return status >= STATUS_READABLE;
    }


    // Pfad in den Preferenzen sichern
    private void savePfad() {
        if(keyPreferenzen != null) {
            SharedPreferences.Editor mEdit = ASettings.mPreferenzen.edit();
            mEdit.putString(keyPreferenzen, mPfad);
            mEdit.apply();
        }
        // und in die Datenbank verewigen
        if(keyDatenbank != null) {
            // SQLiteDatabase mDatenbank = ASettings.stundenDBHelper.getWritableDatabase();
            ContentValues werte = new ContentValues();
            werte.put(keyDatenbank, mPfad);
            ASettings.mDatenbank.update(
                    DatenbankHelper.DB_T_SETTINGS,
                    werte,
                    DatenbankHelper.DB_F_ID + "=?",
                    new String[]{Long.toString(1)}
            );
            // ASettings.mDatenbank.close();
        }
    }

    public Uri getDateiUri(String dateiname) {
            if(isExists() && dateiname != null) {
                DocumentFile mDatei = mFile.findFile(dateiname);
                if (mDatei != null && mDatei.exists()) {
                    return mDatei.getUri();
                }
            }
        return null;
    }


    private Uri makeTreeURIfromFile(File file) {
        Uri uri;
        String scheme = "content";
        String authority = "com.android.externalstorage.documents";
        String[] ele = file.getPath().split(File.separator);
        String common;

        if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) {
            // Separate each element of the File path
            // File format: "/storage/XXXX-XXXX/sub-folder1/sub-folder2..../filename"
            // (XXXX-XXXX is external removable number
            //  ele[0] = not used (empty)
            //  ele[1] = not used (storage name)
            //  ele[2] = storage number
            //  ele[3 to (n-1)] = folders
            //  ele[n] = file name

            // Construct folders strings using SAF format
            StringBuilder folders = new StringBuilder();
            if (ele.length >= 4) {
                folders.append(ele[3]);
                for (int i = 4; i < ele.length; ++i) folders.append("%2F").append(ele[i]);
            }

            common = ele[2] + "%3A" + folders.toString();
        } else {
            // Separate each element of the File path
            // File format: "/storage/emulated/XXXX/sub-folder1/sub-folder2..../filename"
            // XXXX-XXXX is external removable number
            //  ele[0] = not used (empty)
            //  ele[1] = not used (storage name)
            //  ele[2] = emulated
            //  ele[3] = storage number
            //  ele[4 to (n-1)] = folders
            //  ele[n] = file name

            // Construct folders strings using SAF format
            StringBuilder folders = new StringBuilder();
            if (ele.length >= 5) {
                folders.append(ele[4]);
                for (int i = 5; i < ele.length; ++i) folders.append("%2F").append(ele[i]);
            }

            common = "primary" + "%3A" + folders;
        }

        // Construct TREE Uri
        Uri.Builder builder = new Uri.Builder();
        builder.scheme(scheme);
        builder.authority(authority);
        builder.encodedPath("/tree/" + common);
        uri = builder.build();

        // Construct DOCUMENT Uri
   /* builder = new Uri.Builder();
    builder.scheme(scheme);
    builder.authority(authority);
    if (ele.length > 4) common = common + "%2F";
    builder.encodedPath("/document/" + common + pfad.getName());
    uri[1] = builder.build();*/
        return uri;
    }
}