package com.zhaohaoting.framework.view.picker.base.util;

import android.os.Debug;
import android.os.Environment;
import android.util.Log;

import com.zhaohaoting.framework.view.picker.base.AppConfig;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 将信息记录到控制台的LogCat，显示调用方法及所在的文件、行号，方便开发时调试查错。
 * 注意：在Debug状态下开启，在Release状态下关闭，敏感信息不宜打印，否则被非法之徒抓取贻害无穷。
 *
 * @author 李玉江[QQ:1023694760]
 * @since 2013/11/2
 */
public final class LogUtils {
    private static final int MIN_STACK_OFFSET = 3;// starts at this class after two native calls
    private static final int MAX_STACK_TRACE_SIZE = 131071; //128 KB - 1
    private static final int METHOD_COUNT = 2; // show method count in trace
    private static boolean isDebug = AppConfig.DEBUG_ENABLE;// 是否调试模式
    private static String debugTag = AppConfig.DEBUG_TAG;// LogCat的标记

    public static void setIsDebug(boolean isDebug) {
        LogUtils.isDebug = isDebug;
    }

    public static boolean isDebug() {
        return isDebug;
    }

    public static void setDebugTag(String debugTag) {
        LogUtils.debugTag = debugTag;
    }

    public static String getDebugTag() {
        return debugTag;
    }

    /**
     * Verbose.
     *
     * @param message the message
     */
    public static void verbose(String message) {
        verbose("", message);
    }

    /**
     * Verbose.
     *
     * @param object  the object
     * @param message the message
     */
    public static void verbose(Object object, String message) {
        verbose(object.getClass().getSimpleName(), message);
    }

    /**
     * 记录“verbose”级别的信息
     *
     * @param tag the tag
     * @param msg the msg
     */
    public static void verbose(String tag, String msg) {
        if (isDebug) {
            tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag;
            msg = msg + getTraceElement();
            Log.v(tag, msg);
        }
    }

    /**
     * Debug.
     *
     * @param message the message
     */
    public static void debug(String message) {
        debug("", message);
    }

    /**
     * Debug.
     *
     * @param object  the object
     * @param message the message
     */
    public static void debug(Object object, String message) {
        debug(object.getClass().getSimpleName(), message);
    }

    /**
     * 记录“debug”级别的信息
     *
     * @param tag the tag
     * @param msg the msg
     */
    public static void debug(String tag, String msg) {
        if (isDebug) {
            tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag;
            msg = msg + getTraceElement();
            Log.d(tag, msg);
        }
    }

    /**
     * Warn.
     *
     * @param e the e
     */
    public static void warn(Throwable e) {
        warn(toStackTraceString(e));
    }

    /**
     * Warn.
     *
     * @param message the message
     */
    public static void warn(String message) {
        warn("", message);
    }

    /**
     * Warn.
     *
     * @param object  the object
     * @param message the message
     */
    public static void warn(Object object, String message) {
        warn(object.getClass().getSimpleName(), message);
    }

    /**
     * Warn.
     *
     * @param object the object
     * @param e      the e
     */
    public static void warn(Object object, Throwable e) {
        warn(object.getClass().getSimpleName(), toStackTraceString(e));
    }

    /**
     * 记录“warn”级别的信息
     *
     * @param tag the tag
     * @param msg the msg
     */
    public static void warn(String tag, String msg) {
        if (isDebug) {
            tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag;
            msg = msg + getTraceElement();
            Log.w(tag, msg);
        }
    }

    /**
     * Error.
     *
     * @param e the e
     */
    public static void error(Throwable e) {
        error(toStackTraceString(e));
    }

    /**
     * Error.
     *
     * @param message the message
     */
    public static void error(String message) {
        error("", message);
    }

    /**
     * Error.
     *
     * @param object  the object
     * @param message the message
     */
    public static void error(Object object, String message) {
        error(object.getClass().getSimpleName(), message);
    }

    /**
     * Error.
     *
     * @param object the object
     * @param e      the e
     */
    public static void error(Object object, Throwable e) {
        error(object.getClass().getSimpleName(), toStackTraceString(e));
    }

    /**
     * 记录“error”级别的信息
     *
     * @param tag the tag
     * @param msg the msg
     */
    public static void error(String tag, String msg) {
        if (isDebug) {
            tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag;
            msg = msg + getTraceElement();
            Log.e(tag, msg);
        }
    }

    /**
     * 在某个方法中调用生成.trace文件。然后拿到电脑上用DDMS工具打开分析
     *
     * @see #stopMethodTracing()
     */
    public static void startMethodTracing() {
        if (isDebug) {
            Debug.startMethodTracing(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + debugTag + ".trace");
        }
    }

    /**
     * Stop method tracing.
     */
    public static void stopMethodTracing() {
        if (isDebug) {
            Debug.stopMethodTracing();
        }
    }

    /**
     * To stack trace string string.
     * <p>
     * 此方法参见：https://github.com/Ereza/CustomActivityOnCrash
     *
     * @param throwable the throwable
     * @return the string
     */
    public static String toStackTraceString(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        throwable.printStackTrace(pw);
        String stackTraceString = sw.toString();
        //Reduce data to 128KB so we don't get a TransactionTooLargeException when sending the intent.
        //The limit is 1MB on Android but some devices seem to have it lower.
        //See: http://developer.android.com/reference/android/os/TransactionTooLargeException.html
        //And: http://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception#comment46697371_12809171
        if (stackTraceString.length() > MAX_STACK_TRACE_SIZE) {
            String disclaimer = " [stack trace too large]";
            stackTraceString = stackTraceString.substring(0, MAX_STACK_TRACE_SIZE - disclaimer.length()) + disclaimer;
        }
        pw.close();
        return stackTraceString;
    }

    /**
     * 可显示调用方法所在的文件行号，在AndroidStudio的logcat处可点击定位。
     * 此方法参考：https://github.com/orhanobut/logger
     */
    private static String getTraceElement() {
        try {
            int methodCount = METHOD_COUNT;
            StackTraceElement[] trace = Thread.currentThread().getStackTrace();
            int stackOffset = _getStackOffset(trace);

            //corresponding method count with the current stack may exceeds the stack trace. Trims the count
            if (methodCount + stackOffset > trace.length) {
                methodCount = trace.length - stackOffset - 1;
            }

            String level = "    ";
            StringBuilder builder = new StringBuilder();
            for (int i = methodCount; i > 0; i--) {
                int stackIndex = i + stackOffset;
                if (stackIndex >= trace.length) {
                    continue;
                }
                builder.append("\n")
                        .append(level)
                        .append(_getSimpleClassName(trace[stackIndex].getClassName()))
                        .append(".")
                        .append(trace[stackIndex].getMethodName())
                        .append(" ")
                        .append("(")
                        .append(trace[stackIndex].getFileName())
                        .append(":")
                        .append(trace[stackIndex].getLineNumber())
                        .append(")");
                level += "    ";
            }
            return builder.toString();
        } catch (Exception e) {
            Log.w(debugTag, e);
            return "";
        }
    }

    /**
     * Determines the starting index of the stack trace, after method calls made by this class.
     *
     * @param trace the stack trace
     * @return the stack offset
     */
    private static int _getStackOffset(StackTraceElement[] trace) {
        for (int i = MIN_STACK_OFFSET; i < trace.length; i++) {
            StackTraceElement e = trace[i];
            String name = e.getClassName();
            if (!name.equals(LogUtils.class.getName())) {
                return --i;
            }
        }
        return -1;
    }

    private static String _getSimpleClassName(String name) {
        int lastIndex = name.lastIndexOf(".");
        return name.substring(lastIndex + 1);
    }

}
