package com.alibaba.sdk.android.oss.common;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.StatFs;
import android.provider.MediaStore;
import android.system.ErrnoException;

import com.alibaba.sdk.android.oss.ClientConfiguration;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by jingdan on 2017/8/11.
 */

public class OSSLogToFileUtils {

    private static final String LOG_DIR_NAME = "OSSLog";
    private static LogThreadPoolManager logService = LogThreadPoolManager.newInstance();
    /**
     * Context Object
     */
    private static Context sContext;
    /**
     * FileLogUtils instance
     */
    private static OSSLogToFileUtils instance;
    /**
     * file for log
     */
    private static File sLogFile;
    private static Uri sLogUri;
    /**
     * time format
     */
    private static SimpleDateFormat sLogSDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /**
     * default 5M
     */
    private static long LOG_MAX_SIZE = 5 * 1024 * 1024; //5mb
    private boolean useSdCard = true;

    private OSSLogToFileUtils() {
    }

    /**
     * init
     *
     * @param context
     */
    public static void init(Context context, ClientConfiguration cfg) {
        OSSLog.logDebug("init ...", false);
        if (cfg != null) {
            LOG_MAX_SIZE = cfg.getMaxLogSize();
        }
        if (null == sContext || null == instance || null == sLogFile || !sLogFile.exists()) {
            sContext = context.getApplicationContext();
            instance = getInstance();
            logService.addExecuteTask(new Runnable() {
                @Override
                public void run() {
                    sLogFile = instance.getLogFile();
                    if (sLogFile != null) {
                        OSSLog.logInfo("LogFilePath is: " + sLogFile.getPath(), false);
                        // 获取当前日志文件大小
                        long logFileSize = getLogFileSize(sLogFile);
                        // 若日志文件超出了预设大小，则重置日志文件
                        if (LOG_MAX_SIZE < logFileSize) {
                            OSSLog.logInfo("init reset log file", false);
                            instance.resetLogFile();
                        }
                    }
                }
            });
        } else {
            OSSLog.logDebug("LogToFileUtils has been init ...", false);
        }
    }

    public static OSSLogToFileUtils getInstance() {
        if (instance == null) {
            synchronized (OSSLogToFileUtils.class) {
                if (instance == null) {
                    instance = new OSSLogToFileUtils();
                }
            }
        }
        return instance;
    }

    public static void reset() {
        sContext = null;
        instance = null;
        sLogFile = null;
    }

    /**
     * file length
     *
     * @param file 文件
     * @return
     */
    public static long getLogFileSize(File file) {
        long size = 0;
        if (file != null && file.exists()) {
            size = file.length();
        }
        return size;
    }

    /**
     * log size
     *
     * @return
     */
    public static long getLocalLogFileSize() {
        return getLogFileSize(sLogFile);
    }

    private long readSDCardSpace() {
        long sdCardSize = 0;
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            File sdcardDir = Environment.getExternalStorageDirectory();
            try {
                StatFs sf = new StatFs(sdcardDir.getPath());
                long blockSize = sf.getBlockSize();
                long availCount = 0;
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    availCount = sf.getAvailableBlocksLong();
                } else {
                    availCount = sf.getAvailableBlocks();
                }
                sdCardSize = availCount * blockSize;
            } catch (Exception e) {
                sdCardSize = 0;
            }
        }
        OSSLog.logDebug("sd卡存储空间:" + String.valueOf(sdCardSize) + "kb", false);
        return sdCardSize;
    }

    private long readSystemSpace() {
        File root = Environment.getDataDirectory();
        long systemSpaceSize = 0;
        try {
            StatFs sf = new StatFs(root.getPath());
            long blockSize = sf.getBlockSize();
            long availCount = 0;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
                availCount = sf.getAvailableBlocksLong();
            } else {
                availCount = sf.getAvailableBlocks();
            }
            systemSpaceSize = availCount * blockSize / 1024;
        } catch (Exception e) {
            systemSpaceSize = 0;
        }
        OSSLog.logDebug("内部存储空间:" + String.valueOf(systemSpaceSize) + "kb", false);
        return systemSpaceSize;
    }

    public void setUseSdCard(boolean useSdCard) {
        this.useSdCard = useSdCard;
    }

    /**
     * set log file
     */
    public void resetLogFile() {
        OSSLog.logDebug("Reset Log File ... ", false);

        // 创建log.csv，若存在则删除
        if (!sLogFile.getParentFile().exists()) {
            OSSLog.logDebug("Reset Log make File dir ... ", false);
            sLogFile.getParentFile().mkdir();
        }
        File logFile = new File(sLogFile.getParent() + "/logs.csv");
        if (logFile.exists()) {
            logFile.delete();
        }
        // 新建日志文件
        createNewFile(logFile);
    }

    public void deleteLogFile() {
        // 创建log.csv，若存在则删除
        File logFile = new File(sLogFile.getParent() + "/logs.csv");
        if (logFile.exists()) {
            OSSLog.logDebug("delete Log File ... ", false);
            logFile.delete();
        }
    }

    public void deleteLogFileDir() {
        // 创建log.csv，若存在则删除
        deleteLogFile();
        File dir = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + LOG_DIR_NAME);
        if (dir.exists()) {
            OSSLog.logDebug("delete Log FileDir ... ", false);
            dir.delete();
        }
    }

    /**
     * get log file
     *
     * @return APP日志文件
     */
    private File getLogFile() {
        File file = null;
        File logFile = null;
        boolean canStorage;
        // 判断是否有SD卡或者外部存储器
        try {
            if (useSdCard && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                // 有SD卡则使用SD - PS:没SD卡但是有外部存储器，会使用外部存储器
                // SD\OSSLog\logs.csv
                canStorage = readSDCardSpace() > LOG_MAX_SIZE / 1024;
                file = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + LOG_DIR_NAME);
            } else {
                // 没有SD卡或者外部存储器，使用内部存储器
                // \data\data\包名\files\OSSLog\logs.csv
                canStorage = readSystemSpace() > LOG_MAX_SIZE / 1024;
                file = new File(sContext.getFilesDir().getPath() + File.separator + LOG_DIR_NAME);
            }
        } catch (Exception e) {
            canStorage = false;
        }
        // 若目录不存在则创建目录
        if (canStorage) {
            if (!file.exists()) {
                file.mkdirs();
            }
            logFile = new File(file.getPath() + "/logs.csv");
            if (!logFile.exists()) {
                createNewFile(logFile);
            }
        }
        return logFile;
    }

    private Uri getLogUri() {
        Uri uri = null;
        ContentResolver contentResolver = sContext.getContentResolver();

        uri = queryLogUri();
        if (uri == null) {
            ContentValues values = new ContentValues();
            values.put(MediaStore.Files.FileColumns.DISPLAY_NAME, "logs.csv");
            values.put(MediaStore.Files.FileColumns.MIME_TYPE, "file/csv");
            values.put(MediaStore.Files.FileColumns.TITLE, "logs.csv");
            values.put(MediaStore.Files.FileColumns.RELATIVE_PATH, "Documents/" + LOG_DIR_NAME);

            uri = contentResolver.insert(MediaStore.Files.getContentUri("external"), values);

            try {
                contentResolver.openFileDescriptor(uri, "w");
            } catch (Exception e) {
                return null;
            }
        }

        return uri;
    }

    private Uri queryLogUri() {
        Uri uri = null;

        ContentResolver contentResolver = sContext.getContentResolver();
        Uri external = MediaStore.Files.getContentUri("external");
        String selection =MediaStore.Files.FileColumns.RELATIVE_PATH+" like ? AND "
                + MediaStore.Files.FileColumns.DISPLAY_NAME + "=?";
        String[] args = new String[]{"Documents/" + LOG_DIR_NAME + "%", "logs.csv"};
        String[] projection = new String[]{MediaStore.Files.FileColumns._ID};
        Cursor cursor = contentResolver.query(external, projection, selection, args, null);

        if (cursor != null && cursor.moveToFirst()) {
            uri = ContentUris.withAppendedId(external, cursor.getLong(0));
            cursor.close();
        }
        return uri;
    }

    public void createNewFile(File logFile) {
        try {
            logFile.createNewFile();
        } catch (Exception e) {
            OSSLog.logError("Create log file failure !!! " + e.toString(), false);
        }
    }


    private String getFunctionInfo(StackTraceElement[] ste) {
        String msg = null;
        if (ste == null) {
            msg = "[" + sLogSDF.format(new java.util.Date()) + "]";
        }
        return msg;
    }

    public synchronized void write(Object str) {
        if (OSSLog.isEnableLog()) {
            // 判断是否初始化或者初始化是否成功
            if (null == sContext || null == instance || null == sLogFile) {
                return;
            }
            if (!sLogFile.exists()) {
                resetLogFile();
            }
            WriteCall writeCall = new WriteCall(str);
            logService.addExecuteTask(writeCall);
        }
    }

    private static class WriteCall implements Runnable {

        private Object mStr;

        public WriteCall(Object mStr) {
            this.mStr = mStr;
        }

        @Override
        public void run() {
            if (sLogFile != null) {
                long logFileSize = getInstance().getLogFileSize(sLogFile);
                // 若日志文件超出了预设大小，则重置日志文件
                if (logFileSize > LOG_MAX_SIZE) {
                    getInstance().resetLogFile();
                }
                //输出流操作 输出日志信息至本地存储空间内
                PrintWriter pw;
                try {
                    pw = new PrintWriter(new FileWriter(sLogFile, true), true);
                    if (pw != null) {
                        if (mStr instanceof Throwable) {
                            //写入异常信息
                            printEx(pw);
                        } else {
                            //写入普通log
                            pw.println(getInstance().getFunctionInfo(null) + " - " + mStr.toString());
                        }
                        pw.println("------>end of log");
                        pw.println();
                        pw.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        private PrintWriter printEx(PrintWriter pw) {
            pw.println("crash_time：" + sLogSDF.format(new Date()));
            ((Throwable) mStr).printStackTrace(pw);
            return pw;
        }
    }
}
