package qiniu.predem.android.util;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import android.util.Log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Pattern;

/**
 * Created by Misty on 5/18/17.
 */

public final class LogUtils {
    /**
     * 打印级别为V,对应Log.v
     */
    public final static int V = 1;
    /**
     * 打印级别为W,对应Log.w
     */
    public final static int W = 2;
    /**
     * 打印级别为I,对应Log.i
     */
    public final static int I = 3;
    /**
     * 打印级别为D,对应Log.d
     */
    public final static int D = 4;
    /**
     * 打印级别为E,对应Log.e
     */
    public final static int E = 5;
    /**
     * 最高级别打印,强制性打印,LEVEL无法关闭。
     */
    private final static int P = Integer.MAX_VALUE;
    /**
     * 打印修饰符号
     */
    private static final String _L = "[";
    private static final String _R = "]";
    /**
     * 生成一个日期文件名格式的日式格式对象
     */
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
    /**
     * 控制打印级别 在level级别之上才可以被打印出来
     */
    public static int LEVEL = 1;
    /**
     * 是否同步输出到本地日志文件
     */
    private static boolean IS_SYNS = false;
    /**
     * 打印日志保存路径
     */
    private static String LOG_FILE_DIR = "";
    /**
     * 是否新建一个日志文件。
     */
    private static boolean IF_START_NEWLOG = true;
    /**
     * 保存创建的文件路径
     */
    private static String CURRENT_LOG_NAME = "";
    /**
     * 针对天数级别的。如果当天已经存在一个LOG了,而使用者需要新开一个LOG,那么将计数
     */
    private static int FILE_LOG_COUNT = 0;
    /**
     * 单个日志的最大的容量,如果一个日志太大了,打开会影响效率
     */
    private static int LOG_MAX_SIZE = 6 * 1024 * 1024;
    /**
     * 检测文件目的地址是否正确
     */
    private static Pattern pattern = Pattern.compile("(\\w+/)+");
    /**
     * Debug模式,在Debug模式下才打印日志
     */
    private static boolean DEBUG_MODE = true;

    /**
     * 动态关闭日志, 默认打开(关闭打印输出和文件输出)
     * <p>Title: closeLog0
     * <p>Description:
     */
    public static final void closeLog() {
        LogUtils.LEVEL = 10;
    }

    /**
     * 动态打开日志
     * <p>Title: openLog
     * <p>Description:
     */
    public static final void openLog() {
        LogUtils.LEVEL = 1;
    }

    /**
     * 设置是否同步记录信息或者异常到日志文件。
     */
    public static final void setSyns(boolean flag) {
        synchronized (LogUtils.class) {
            IS_SYNS = flag;
        }
    }

    /**
     * 开启一个新的LOG
     */
    public static final void startNewLog() {
        IF_START_NEWLOG = true;
    }

    /**
     * 打印信息
     *
     * @param message
     */
    public static final void i(String message) {
        if (LEVEL <= I) {
            if (DEBUG_MODE) {
                Log.i(getTag(message), message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void i(Exception exp) {
        if (LEVEL <= I) {
            if (DEBUG_MODE) {
                Log.i(getTag(exp), getMessage(exp));
            }
            if (IS_SYNS) {
                LogFile.writeLog(exp);
            }
        }
    }

    /**
     * 打印信息
     *
     * @param message
     */
    public static final void i(String tag, String message) {
        if (LEVEL <= I) {
            if (DEBUG_MODE) {
                Log.i(tag, message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void e(String message) {
        if (LEVEL <= E) {
            if (DEBUG_MODE) {
                Log.e(getTag(message), message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void e(Exception exp) {
        if (LEVEL <= E) {
            if (DEBUG_MODE) {
                Log.e(getTag(exp), getMessage(exp));
            }
            if (IS_SYNS) {
                LogFile.writeLog(exp);
            }
        }
    }

    public static final void e(String tag, String message) {
        if (LEVEL <= E) {
            if (DEBUG_MODE) {
                Log.e(tag, message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void w(String message) {
        if (LEVEL <= W) {
            if (DEBUG_MODE) {
                Log.w(getTag(message), message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void w(Exception exp) {
        if (LEVEL <= W) {
            if (DEBUG_MODE) {
                Log.w(getTag(exp), getMessage(exp));
            }
            if (IS_SYNS) {
                LogFile.writeLog(exp);
            }
        }
    }

    public static final void w(String tag, String message) {
        if (LEVEL <= W) {
            if (DEBUG_MODE) {
                Log.w(tag, message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void v(String message) {
        if (LEVEL <= V) {
            if (DEBUG_MODE) {
                Log.v(getTag(message), message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void v(Exception exp) {
        if (LEVEL <= V) {
            if (DEBUG_MODE) {
                Log.v(getTag(exp), getMessage(exp));
            }
            if (IS_SYNS) {
                LogFile.writeLog(exp);
            }
        }
    }

    public static final void v(String tag, String message) {
        if (LEVEL <= V) {
            if (DEBUG_MODE) {
                Log.v(tag, message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void d(String message) {
        if (LEVEL <= D) {
            if (DEBUG_MODE) {
                Log.d(getTag(message), message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    public static final void d(Exception exp) {
        if (LEVEL <= D) {
            if (DEBUG_MODE) {
                Log.d(getTag(exp), getMessage(exp));
            }
            if (IS_SYNS) {
                LogFile.writeLog(exp);
            }
        }
    }

    public static final void d(String tag, String message) {
        if (LEVEL <= D) {
            if (DEBUG_MODE) {
                Log.d(tag, message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }


    /**
     * 强制打印信息
     *
     * @param message
     */
    public static final void print(String message) {
        if (LEVEL <= P) {
            if (DEBUG_MODE) {
                Log.e(getTag(message), message);
            }
            if (IS_SYNS) {
                LogFile.writeLog(message);
            }
        }
    }

    /**
     * 强制打印异常
     *
     * @param exp
     */
    public static final void print(Exception exp) {
        if (LEVEL <= P) {
            if (DEBUG_MODE) {
                Log.e(getTag(exp), getMessage(exp));
            }
            if (IS_SYNS) {
                LogFile.writeLog(exp);
            }
        }
    }

    /**
     * 获取一个Tag打印标签
     *
     * @param msg
     * @return
     * @since JDK 1.5
     */
    private static String getTag(String msg) {
        if (msg != null) {
            //since jdk 1.5
            if (Thread.currentThread().getStackTrace().length > 0) {
                String name = Thread.currentThread().getStackTrace()[0].getClassName();
                return _L + name.substring(name.lastIndexOf(".") + 1) + _R;
            }
        }
        return _L + "null" + _R;
    }

    /**
     * 跟据变量获取一个打印的标签。
     *
     * @param exp
     * @return
     */
    private static String getTag(Exception exp) {
        if (exp != null) {
            if (exp.getStackTrace().length > 0) {
                String name = exp.getStackTrace()[0].getClassName();
                return _L + name.substring(name.lastIndexOf(".") + 1) + _R;
            }
            return _L + "exception" + _R;
        }
        return _L + "null" + _R;
    }

    /**
     * 获取Exception的简便异常信息
     *
     * @param exp
     * @return
     */
    private static String getMessage(Exception exp) {
        StringBuilder sb = new StringBuilder();
        StackTraceElement[] element = exp.getStackTrace();
        int n = 0;
        sb.append("\n");
        sb.append(exp.toString());
        sb.append("\n");
        for (StackTraceElement e : element) {
            sb.append(e.getClassName());
            sb.append(".");
            sb.append(e.getMethodName());
            sb.append("[");
            sb.append(e.getLineNumber());
            sb.append("]");
            sb.append("\n");
            n++;
            if (n >= 2) break;
        }
        if (exp.getCause() != null) {
            sb.append("Caused by: ");
            sb.append(exp.getMessage());
        }
        return sb.toString();
    }

    /**
     * 自定义保存文件路径,如果是多重路径,请以xxx/xxx/xxx 形式的‘文件夹’
     *
     * @param path : 文件夹名称
     *             * [将自动以当前时间为文件名拼接成完整路径。请慎用]
     */
    public final static void setLogFilePath(String path) {
        String url = SDcardUtil.getPath() + File.separator + path;
        boolean flag = pattern.matcher(url).matches();
        if (flag) {
            LOG_FILE_DIR = url;
        } else {
            LogFile.print("the url is not match file`s dir");
        }
    }

    /**
     * 设置默认路径,以包名为格式的文件夹
     */
    public final static void setDefaultFilePath(Context context) {
        String pkName = context.getPackageName().replaceAll("\\.", "\\/");
        setLogFilePath(pkName);
    }

    /**
     * 获取时间字符串
     */
    private static String getCurrTimeDir() {
        return sdf.format(new Date());
    }

    /**
     * LOG定制类。
     * 输出LOG到日志。
     */
    private static class LogFile {
        /**
         * 内部强制性打印使用。区分print , 是为了解决无限循环打印exception
         */
        private static void print(String msg) {
            if (LEVEL <= P) {
                Log.e(getTag(msg), msg);
            }
        }

        /**
         * 打印信息
         *
         * @param message
         */
        public static synchronized void writeLog(String message) {
            File f = getFile();
            if (f != null) {
                try {
                    FileWriter fw = new FileWriter(f, true);
                    BufferedWriter bw = new BufferedWriter(fw);
                    bw.append("\n");
                    bw.append(message);
                    bw.append("\n");
                    bw.flush();
                    bw.close();
                    fw.close();
                } catch (IOException e) {
                    print("writeLog error, " + e.getMessage());
                }
            } else {
                print("writeLog error, due to the file dir is error");
            }
        }

        /**
         * 打印异常
         *
         * @param exp
         */
        public static synchronized void writeLog(Exception exp) {
            File f = getFile();
            if (f != null) {
                try {
                    FileWriter fw = new FileWriter(f, true);
                    PrintWriter pw = new PrintWriter(fw);
                    pw.append("\n");
                    exp.printStackTrace(pw);
                    pw.flush();
                    pw.close();
                    fw.close();
                } catch (IOException e) {
                    print("writeLog error, " + e.getMessage());
                }
            } else {
                print("writeLog error, due to the file dir is error");
            }
        }

        /**
         * 获取文件
         *
         * @return
         */
        private static final File getFile() {
            if ("".equals(LOG_FILE_DIR)) {
                return null;
            }
            synchronized (LogUtils.class) {
                if (!IF_START_NEWLOG) {
                    File currFile = new File(CURRENT_LOG_NAME);
                    if (currFile.length() >= LOG_MAX_SIZE) {
                        IF_START_NEWLOG = true;
                        return getFile();
                    }
                    return currFile;
                }
                File f = new File(LOG_FILE_DIR);
                if (!f.exists()) {
                    f.mkdirs();
                }
                File file = new File(f.getAbsolutePath() + File.separator + getCurrTimeDir() + ".log");
                if (!file.exists()) {
                    try {
                        file.createNewFile();
                        FILE_LOG_COUNT = 0;
                        IF_START_NEWLOG = false;
                        CURRENT_LOG_NAME = file.getAbsolutePath();
                    } catch (IOException e) {
                        print("createFile error , " + e.getMessage());
                    }
                } else {
                    //已经存在了
                    if (IF_START_NEWLOG) {
                        FILE_LOG_COUNT++;
                        return new File(f.getAbsolutePath() + File.separator + getCurrTimeDir() + "_" + FILE_LOG_COUNT + ".log");
                    }
                }
                return file;
            }
        }
    }

    /**
     * SD卡管理器
     */
    private static class SDcardUtil {
        /**
         * 获取Path
         */
        public static String getPath() {
            if (isMounted()) {
                return Environment.getExternalStorageDirectory().getPath();
            }
            LogFile.print("please check if sd card is not mounted");
            return "";
        }

        /**
         * 判断SD卡是否mounted
         */
        @SuppressLint("NewApi")
        public static boolean isMounted() {
            return Environment.isExternalStorageEmulated();
        }

    }
}