/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package info.zamojski.soft.towercollector.logging;

import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
import androidx.annotation.NonNull;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import info.zamojski.soft.towercollector.utils.ApkUtils;
import info.zamojski.soft.towercollector.utils.FileUtils;
import timber.log.Timber;

public class FileLoggingTree extends Timber.DebugTree {

    private static final String TAG = FileLoggingTree.class.getSimpleName();

    private static final String[] LEVELS = new String[]{"disabled", "undefined", "V", "D", "I", "W", "E"};
    private static final int DISABLED = 0;

    private final static SimpleDateFormat shortDateFormat = new SimpleDateFormat("MM-dd-HH:mm:ss.SSS", Locale.getDefault());

    private OutputStreamWriter osw;
    private boolean firstRun = true;
    private int priority = DISABLED;

    public final static FileLoggingTree INSTANCE = new FileLoggingTree();

    private FileLoggingTree() {
        // Avoid instantiation
    }

    public FileLoggingTree setPriority(int priority) {
        this.priority = priority;
        return this;
    }

    @SuppressLint("LogNotTimber")
    @Override
    protected void log(int priority, String tag, @NonNull String message, Throwable t) {
        if (firstRun) {
            synchronized (FileLoggingTree.class) {
                if (firstRun) {
                    initialize();
                    firstRun = false;
                    // write identification data once initialization finished
                    log(Log.DEBUG, ApkUtils.getDeviceName());
                    log(Log.DEBUG, ApkUtils.getApkVersionNameWithSuffix());
                    log(Log.DEBUG, "Android version %s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT);
                }
            }
        }
        // Skip if storage is unavailable or initialization failed
        if (osw == null) {
            return;
        }
        try {
            osw.write(String.format(Locale.ENGLISH, "%s %s/%s(% 5d): %s\r\n", shortDateFormat.format(new Date()), LEVELS[priority], tag, Process.myPid(), message));
            osw.flush();
        } catch (Exception ex) {
            Log.e(TAG, "Failed to write log file!", ex);
        }
    }

    @Override
    protected boolean isLoggable(String tag, int priority) {
        return (this.priority > 0 && this.priority <= priority);
    }

    @SuppressLint("LogNotTimber")
    private void initialize() {
        try {
            File logDir = new File(Environment.getExternalStorageDirectory().getPath(), "TowerCollector");
            if (!logDir.exists()) {
                logDir.mkdirs();
            }
            File logFile = new File(logDir, FileUtils.getCurrentDateFileName("log"));
            FileOutputStream fis = new FileOutputStream(logFile);
            osw = new OutputStreamWriter(fis);
        } catch (Exception ex) {
            Log.e(TAG, "Failed to open log file!", ex);
        }
    }
}

@@ -374,16 +374,17 @@
 rocess;%0A
+%0A
 import a
@@ -411,16 +411,17 @@
 onNull;%0A
+%0A
 import a
@@ -1037,16 +1037,81 @@
 LED = 0;
+%0A    private static final int FILE_EXISTENCE_CHECK_INTERVAL = 25;
 %0A%0A    pr
@@ -1226,24 +1226,50 @@
 efault());%0A%0A
+    private File logFile;%0A
     private 
@@ -1365,16 +1365,60 @@
 ISABLED;
+%0A    private int logCallsWithinInterval = 0;
 %0A%0A    pu
@@ -2486,32 +2486,262 @@
 %7D%0A        try %7B%0A
+            if (logCallsWithinInterval++ %3E= FILE_EXISTENCE_CHECK_INTERVAL) %7B%0A                logCallsWithinInterval = 0;%0A                if (!logFile.exists()) %7B%0A                    reinitialize();%0A                %7D%0A            %7D%0A
             osw.
@@ -2991,33 +2991,32 @@
 ite log file
-!
 %22, ex);%0A
         %7D%0A  
@@ -2995,32 +2995,60 @@
 log file%22, ex);%0A
+            reinitialize();%0A
         %7D%0A    %7D%0A
@@ -3477,21 +3477,16 @@
         
-File 
 logFile 
@@ -3739,17 +3739,16 @@
 log file
-!
 %22, ex);%0A
@@ -3752,21 +3752,341 @@
 );%0A        %7D
+%0A    %7D%0A%0A    @SuppressLint(%22LogNotTimber%22)%0A    private void reinitialize() %7B%0A        try %7B%0A            Log.i(TAG, %22Log file deleted, reinitializing%22);%0A            osw.close();%0A        %7D catch (Exception ex) %7B%0A            Log.e(TAG, %22Failed to close log file%22, ex);%0A        %7D%0A        firstRun = true;%0A        initialize();
 %0A    %7D%0A%7D%0A

Fix log file not recreated after deletion until app killed.