/*
 * Copyright (c) 2016, Apptentive, Inc. All Rights Reserved.
 * Please refer to the LICENSE file for the terms and conditions
 * under which redistribution and use of this file is permitted.
 */

package com.apptentive.android.sdk;

import android.os.Looper;
import android.util.Log;

import java.util.IllegalFormatException;

public class ApptentiveLog {
	private static final String TAG = "Apptentive";

	private static Level logLevel = Level.DEFAULT;

	public static void overrideLogLevel(Level level) {
		ApptentiveLog.logLevel = level;
	}

	private static void doLog(Level level, ApptentiveLogTag tag, Throwable throwable, String message, Object... args){
		if(canLog(level) && message != null){
			if(args.length > 0){
				try{
					message = String.format(message, args);
				}catch(IllegalFormatException e){
					message = "Error formatting log message [level="+level+"]: "+message;
					level = Level.ERROR;
				}
			}

			String extra = null;

			// add thread name if logging of the UI-thread
			if (Looper.getMainLooper() != null && Looper.getMainLooper().getThread() != Thread.currentThread()) {
				extra = '[' + Thread.currentThread().getName() + ']';
			}

			// custom tag
			if (tag != null) {
				if (extra == null) {
					extra = '[' + tag.toString() + ']';
				} else {
					extra += " [" + tag.toString() + ']';
				}
			}

			if (extra != null) {
				message = extra + " " + message;
			}

			android.util.Log.println(level.getLevel(), TAG, message);
			if(throwable != null){
				if(throwable.getMessage() != null){
					android.util.Log.println(level.getLevel(), TAG, throwable.getMessage());
				}
				while(throwable != null) {
					android.util.Log.println(level.getLevel(), TAG, android.util.Log.getStackTraceString(throwable));
					throwable = throwable.getCause();
				}
			}
		}
	}

	public static boolean canLog(Level level) {
		return logLevel.canLog(level);
	}

	public static void v(ApptentiveLogTag tag, String message, Object... args) {
		if (tag.enabled) {
			doLog(Level.VERBOSE, tag, null, message, args);
		}
	}

	public static void v(String message, Object... args){
		doLog(Level.VERBOSE, null, null, message, args);
	}
	public static void v(String message, Throwable throwable, Object... args){
		doLog(Level.VERBOSE, null, throwable, message, args);
	}

	public static void d(ApptentiveLogTag tag, String message, Object... args){
		if (tag.enabled) {
			doLog(Level.DEBUG, tag, null, message, args);
		}
	}

	public static void d(String message, Object... args){
		doLog(Level.DEBUG, null, null, message, args);
	}
	public static void d(String message, Throwable throwable, Object... args){
		doLog(Level.DEBUG, null, throwable, message, args);
	}

	public static void i(ApptentiveLogTag tag, String message, Object... args){
		if (tag.enabled) {
			doLog(Level.INFO, tag, null, message, args);
		}
	}
	public static void i(String message, Object... args){
		doLog(Level.INFO, null, null, message, args);
	}
	public static void i(String message, Throwable throwable, Object... args){
		doLog(Level.INFO, null, throwable, message, args);
	}

	public static void w(ApptentiveLogTag tag, String message, Object... args){
		if (tag.enabled) {
			doLog(Level.WARN, tag, null, message, args);
		}
	}
	public static void w(String message, Object... args){
		doLog(Level.WARN, null, null, message, args);
	}
	public static void w(String message, Throwable throwable, Object... args){
		doLog(Level.WARN, null, throwable, message, args);
	}

	public static void e(ApptentiveLogTag tag, String message, Object... args){
		if (tag.enabled) {
			doLog(Level.ERROR, tag, null, message, args);
		}
	}
	public static void e(String message, Object... args){
		doLog(Level.ERROR, null, null, message, args);
	}
	public static void e(String message, Throwable throwable, Object... args){
		doLog(Level.ERROR, null, throwable, message, args);
	}
	public static void e(Throwable throwable, String message, Object... args){
		doLog(Level.ERROR, null, throwable, message, args);
	}

	public static void a(String message, Object... args){
		doLog(Level.ASSERT, null, null, message, args);
	}
	public static void a(String message, Throwable throwable, Object... args){
		doLog(Level.ASSERT, null, throwable, message, args);
	}

	public enum Level {
		VERBOSE(Log.VERBOSE),
		DEBUG(Log.DEBUG),
		INFO(Log.INFO),
		WARN(Log.WARN),
		ERROR(Log.ERROR),
		ASSERT(Log.ASSERT),
		DEFAULT(Log.INFO);

		private int level;

		private Level(int level) {
			this.level = level;
		}

		public int getLevel() {
			return level;
		}

		public static Level parse(String level) {
			try {
				return Level.valueOf(level);
			} catch (IllegalArgumentException e) {
				android.util.Log.println(Log.WARN, TAG, "Error parsing unknown ApptentiveLog.Level: " + level);
			}
			return DEFAULT;
		}

		/**
		 * If this object is the current level, returns true if the Level passed in is of a sufficient level to be logged.
		 * @return true if "level" can be logged.
		 */
		public boolean canLog(Level level) {
			return level.getLevel() >= getLevel();
		}
	}
}

@@ -1891,24 +1891,430 @@
 level);%0A%09%7D%0A%0A
+%09public static void vv(ApptentiveLogTag tag, String message, Object... args) %7B%0A%09%09if (tag.enabled) %7B%0A%09%09%09doLog(Level.VERBOSE, tag, null, message, args);%0A%09%09%7D%0A%09%7D%0A%0A%09public static void vv(String message, Object... args)%7B%0A%09%09doLog(Level.VERBOSE, null, null, message, args);%0A%09%7D%0A%09public static void vv(String message, Throwable throwable, Object... args)%7B%0A%09%09doLog(Level.VERBOSE, null, throwable, message, args);%0A%09%7D%0A%0A
 %09public stat
@@ -4676,16 +4676,45 @@
 Level %7B%0A
+%09%09VERY_VERBOSE(Log.VERBOSE),%0A
 %09%09VERBOS

Add VERY_VERBOSE log level in preparation for SDK development only logging.