package nodomain.freeyourgadget.gadgetbridge;

import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Build.VERSION;
import android.preference.PreferenceManager;
import android.util.Log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;

public class GBApplication extends Application {
    // Since this class must not log to slf4j, we use plain android.util.Log
    private static final String TAG = "GBApplication";
    private static GBApplication context;
    private static ActivityDatabaseHandler mActivityDatabaseHandler;
    private static final Lock dbLock = new ReentrantLock();
    private static DeviceService deviceService;
    private static SharedPreferences sharedPrefs;

    public GBApplication() {
        context = this;
        deviceService = createDeviceService();
        // don't do anything here, add it to onCreate instead
    }

    protected DeviceService createDeviceService() {
        return new GBDeviceService(this);
    }

    @Override
    public void onCreate() {
        super.onCreate();

        sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);

        // don't do anything here before we set up logging, otherwise
        // slf4j may be implicitly initialized before we properly configured it.
        setupLogging();
//        For debugging problems with the logback configuration
//        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
//         print logback's internal status
//        StatusPrinter.print(lc);
//        Logger logger = LoggerFactory.getLogger(GBApplication.class);

        GB.environment = GBEnvironment.createDeviceEnvironment();
        mActivityDatabaseHandler = new ActivityDatabaseHandler(context);
        loadBlackList();
// for testing DB stuff
//        SQLiteDatabase db = mActivityDatabaseHandler.getWritableDatabase();
//        db.close();
    }

    public static boolean isFileLoggingEnabled() {
        return sharedPrefs.getBoolean("log_to_file", false);
    }

    private void setupLogging() {
        if (isFileLoggingEnabled()) {
            try {
                File dir = FileUtils.getExternalFilesDir();
                // used by assets/logback.xml since the location cannot be statically determined
                System.setProperty("GB_LOGFILES_DIR", dir.getAbsolutePath());
                getLogger().info("Gadgetbridge version: " + BuildConfig.VERSION_NAME);
            } catch (IOException ex) {
                Log.e("GBApplication", "External files dir not available, cannot log to file, ex");
                System.setProperty("GB_LOGFILES_DIR", "/dev/null");
            }
        } else {
            try {
                ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
                root.detachAppender("FILE");
            } catch (Throwable ex) {
                System.out.println("Error removing logger FILE appender");
                ex.printStackTrace();
            }
        }
    }

    private Logger getLogger() {
        return LoggerFactory.getLogger(GBApplication.class);
    }

    public static Context getContext() {
        return context;
    }

    public static DeviceService deviceService() {
        return deviceService;
    }

    /**
     * Returns the DBHandler instance for reading/writing or throws GBException
     * when that was not successful
     * If acquiring was successful, callers must call #releaseDB when they
     * are done (from the same thread that acquired the lock!
     *
     * @return the DBHandler
     * @throws GBException
     * @see #releaseDB()
     */
    public static DBHandler acquireDB() throws GBException {
        try {
            if (dbLock.tryLock(30, TimeUnit.SECONDS)) {
                return mActivityDatabaseHandler;
            }
        } catch (InterruptedException ex) {
            Log.i(TAG, "Interrupted while waiting for DB lock");
        }
        throw new GBException("Unable to access the database.");
    }

    /**
     * Releases the database lock.
     *
     * @throws IllegalMonitorStateException if the current thread is not owning the lock
     * @see #acquireDB()
     */
    public static void releaseDB() {
        dbLock.unlock();
    }

    public static boolean isRunningLollipopOrLater() {
        return VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
    }

    public static HashSet<String> blacklist = null;

    public static void loadBlackList() {
        blacklist = (HashSet<String>) sharedPrefs.getStringSet("package_blacklist", null);
        if (blacklist == null) {
            blacklist = new HashSet<>();
        }
    }

    public static void saveBlackList() {
        SharedPreferences.Editor editor = sharedPrefs.edit();
        if (blacklist.isEmpty()) {
            editor.putStringSet("package_blacklist", null);
        } else {
            editor.putStringSet("package_blacklist", blacklist);
        }
        editor.apply();
    }

    public static void addToBlacklist(String packageName) {
        if (!blacklist.contains(packageName)) {
            blacklist.add(packageName);
            saveBlackList();
        }
    }

    public static synchronized void removeFromBlacklist(String packageName) {
        blacklist.remove(packageName);
        saveBlackList();
    }

}

@@ -1431,55 +1431,8 @@
 is;%0A
-        deviceService = createDeviceService();%0A
     
@@ -2212,24 +2212,71 @@
 on.class);%0A%0A
+        deviceService = createDeviceService();%0A
         GB.e
@@ -3224,13 +3224,13 @@
 file
+%22
 , ex
-%22
 );%0A 
@@ -3248,113 +3248,160 @@
     
-System.setProperty(%22GB_LOGFILES_DIR%22, %22/dev/null%22);%0A            %7D%0A        %7D else %7B%0A            try %7B%0A    
+removeFileLogger();%0A            %7D%0A        %7D else %7B%0A            removeFileLogger();%0A        %7D%0A    %7D%0A%0A    private void removeFileLogger() %7B%0A        try %7B%0A
     
@@ -3539,20 +3539,16 @@
         
-    
 root.det
@@ -3568,36 +3568,32 @@
 FILE%22);%0A        
-    
 %7D catch (Throwab
@@ -3617,31 +3617,31 @@
     
- 
-   System.out.println(
+Log.e(%22GBApplication%22, 
 %22Err
@@ -3677,62 +3677,14 @@
 der%22
-);%0A                ex.printStackTrace();%0A            %7D
+, ex);
 %0A   

Improve logging setup, should hopefully fix #107