/*
 * Copyright (c) 2016. See AUTHORS file.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mbrlabs.mundus.utils;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.mbrlabs.mundus.core.registry.Registry;

import org.apache.commons.lang3.exception.ExceptionUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Log messages with different log levels. To save performance during runtime,
 * string concatination is only run if log level is present. Use for example:
 * Log.log(Log.INFO, TAG, "Msg with param {} and param {}", "param1", "param2");
 * {} will be replaced with parameter in left to right order.
 * 
 * @author Marcus Brummer, codenigma
 * @version 23-09-2015
 */
public class Log {

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

    public static final int TRACE = 5;
    public static final int DEBUG = 4;
    public static final int INFO = 3;
    public static final int WARN = 2;
    public static final int ERROR = 1;
    public static final int FATAL = 0;

    public static int LOG_LEVEL = TRACE;

    private static File logFile;
    private static PrintWriter logFileWriter;
    private static SimpleDateFormat msgDateFormat = new SimpleDateFormat("[HH:mm]");

    public static void init() {
        System.setErr(new ErrorStreamInterceptor(System.err));
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable throwable) {
                Log.exception(TAG, throwable);
                Log.fatal(TAG, "Uncaught exception occurred, error report will be saved");
                logFileWriter.flush();
            }
        });

        prepareLogFile();
    }

    public static void dispose() {
        info(TAG, "Disposing");
        logFileWriter.close();
    }

    public static FileHandle getLogFile() {
        return Gdx.files.absolute(logFile.getAbsolutePath());
    }

    private static void prepareLogFile() {
        File logDirectory = new File(Registry.LOGS_DIR);
        logDirectory.mkdir();

        SimpleDateFormat fileDateFormat = new SimpleDateFormat("yy-MM-dd");
        String fileName = fileDateFormat.format(new Date());
        fileName = "mundus " + fileName + ".log";

        try {
            logFile = new File(logDirectory, fileName);
            logFile.createNewFile();
            logFileWriter = new PrintWriter(new FileWriter(logFile, true));
        } catch (IOException e) {
            exception(TAG, e);
        }

        logFileWriter.println();
        info(TAG, "Logging activated. Log file [{}] created. ", fileName);
    }

    public static String getLogStoragePath() {
        return logFile.getParent();
    }

    // Log with tag
    public static void trace(String tag, String msg, Object... params) {
        if (LOG_LEVEL >= TRACE) {
            msg = completeMsg(msg, params);
            print("[Trace][" + tag + "] " + msg);
        }
    }

    public static void debug(String tag, String msg, Object... params) {
        if (LOG_LEVEL >= DEBUG) {
            msg = completeMsg(msg, params);
            print("[Debug][" + tag + "] " + msg);
        }
    }

    public static void info(String tag, String msg, Object... params) {
        if (LOG_LEVEL >= INFO) {
            msg = completeMsg(msg, params);
            print("[Info][" + tag + "] " + msg);
        }
    }

    public static void warn(String tag, String msg, Object... params) {
        if (LOG_LEVEL >= WARN) {
            msg = completeMsg(msg, params);
            print("[Warning][" + tag + "] " + msg);
        }
    }

    public static void error(String tag, String msg, Object... params) {
        if (LOG_LEVEL >= ERROR) {
            msg = completeMsg(msg, params);
            printErr("[Error][" + tag + "] " + msg);
        }
    }

    public static void fatal(String tag, String msg, Object... params) {
        if (LOG_LEVEL >= FATAL) {
            msg = completeMsg(msg, params);
            printErr("[Fatal][" + tag + "] " + msg);
        }
    }

    private static void print(String msg) {
        msg = getTimestamp() + msg;
        logFileWriter.println(msg);
        logFileWriter.flush();
        System.out.println(msg);
    }

    private static void printErr(String msg) {
        msg = getTimestamp() + msg;
        System.err.println(msg);
    }

    public static void exception(String tag, Throwable e) {
        String stack = ExceptionUtils.getStackTrace(e);
        fatal(tag, stack);
    }

    private static String getTimestamp() {
        return msgDateFormat.format(new Date());
    }

    private static class ErrorStreamInterceptor extends PrintStream {

        public ErrorStreamInterceptor(OutputStream out) {
            super(out, true);
        }

        @Override
        public void print(String s) {
            super.print(s);
            if (logFileWriter != null) {
                logFileWriter.println(s);
            }
        }
    }

    private static String completeMsg(String msg, Object... params) {
        for (Object p : params)
            msg = msg.replaceFirst("\\{\\}", String.valueOf(p));
        return msg;
    }

    /**
     * Log msg with tag To save memory values will be concat to strings only
     * when log level really is set Use {} in the msg as wild card
     * 
     * @param logLevel
     * @param msg
     * @param params
     */
    public static void log(int logLevel, String tag, String msg, Object... params) {
        switch (logLevel) {
        case TRACE:
            trace(tag, msg, params);
            break;
        case DEBUG:
            debug(tag, msg, params);
            break;
        case INFO:
            info(tag, msg, params);
            break;
        case WARN:
            warn(tag, msg, params);
            break;
        case ERROR:
            error(tag, msg, params);
            break;
        case FATAL:
            fatal(tag, msg, params);
            break;
        default:
            error(tag, "Log level " + logLevel + " is not supported!");
        }
    }

    public final static void printHeadLine(String title) {
        print("|                  > " + title + " <)");
    }

    public final static void printUpperSeperationLine() {
        print("↓‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾BEGIN‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾↓");
    }

    public final static void printLowerSeperationLine() {
        print("↑_______________________END_______________________↑");
    }
}

@@ -2776,16 +2776,113 @@
 S_DIR);%0A
+        System.out.println(%22INFO: Ensuring log directory(%22 + Registry.LOGS_DIR + %22) exists...%22);%0A
         
@@ -2899,16 +2899,17 @@
 ry.mkdir
+s
 ();%0A%0A   
@@ -3068,17 +3068,16 @@
  %22mundus
- 
 %22 + file

Fix of the https://github.com/mbrlabs/Mundus/issues/29 Also, removed space from the log file name.