package com.grkj.iscs.util.log

import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import android.text.TextUtils
import com.grkj.iscs.util.FileUtil
import com.orhanobut.logger.FormatStrategy
import com.orhanobut.logger.LogStrategy
import com.orhanobut.logger.Logger
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

class MyCsvFormatStrategy private constructor(builder: Builder) : FormatStrategy {
    private val date: Date
    private val dateFormat: SimpleDateFormat
    private val logStrategy: LogStrategy
    private val tag: String?

    init {
        checkNotNull(builder)
        date = builder.date!!
        dateFormat = builder.dateFormat!!
        logStrategy = builder.logStrategy!!
        tag = builder.tag
    }

    override fun log(priority: Int, onceOnlyTag: String?, message: String) {
        var message = message
        checkNotNull(message)
        val tag = formatTag(onceOnlyTag)
        date.time = System.currentTimeMillis()
        val builder = StringBuilder()

        // machine-readable date/time
//        builder.append(Long.toString(date.getTime()));

        // human-readable date/time
//        builder.append(SEPARATOR);
        builder.append(dateFormat.format(date))

        // level
        builder.append(SEPARATOR + SEPARATOR)
        builder.append(logLevel(priority))

        // tag
        builder.append(SEPARATOR + SEPARATOR)
        //        builder.append(tag);
        builder.append(onceOnlyTag)

        // message
        if (message.contains(NEW_LINE)) {
            // a new line would break the CSV format, so we replace it here
            message = message.replace(NEW_LINE.toRegex(), NEW_LINE_REPLACEMENT)
        }
        builder.append(" : ")
        builder.append(message)

        // new line
        builder.append(NEW_LINE)
        logStrategy.log(priority, tag, builder.toString())
    }

    private fun formatTag(tag: String?): String? {
        return if (!TextUtils.isEmpty(tag) && !TextUtils.equals(this.tag, tag)) {
            this.tag + "-" + tag
        } else this.tag
    }

    class Builder {
        var date: Date? = null
        var dateFormat: SimpleDateFormat? = null
        var logStrategy: LogStrategy? = null
        var tag: String? = "PRETTY_LOGGER"
        fun date(`val`: Date?): Builder {
            date = `val`
            return this
        }

        fun dateFormat(`val`: SimpleDateFormat?): Builder {
            dateFormat = `val`
            return this
        }

        fun logStrategy(`val`: LogStrategy?): Builder {
            logStrategy = `val`
            return this
        }

        fun tag(tag: String?): Builder {
            this.tag = tag
            return this
        }

        fun build(mContext: Context): MyCsvFormatStrategy {
            if (date == null) {
                date = Date()
            }
            if (dateFormat == null) {
                dateFormat = SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS", Locale.UK)
            }
            if (logStrategy == null) {
                val diskPath = FileUtil.getRootFolder(mContext)?.absolutePath
                val folder = diskPath + File.separatorChar + "logger"
                val ht = HandlerThread("AndroidFileLogger.$folder")
                ht.start()
                val handler: Handler = MyDiskLogStrategy.WriteHandler(ht.looper, folder, MAX_BYTES)
                logStrategy = MyDiskLogStrategy(handler)
            }
            return MyCsvFormatStrategy(this)
        }

        companion object {
            private const val MAX_BYTES = 500 * 1024 // 500K averages to a 4000 lines per file
        }
    }

    companion object {
        private val NEW_LINE = System.getProperty("line.separator")
        private const val NEW_LINE_REPLACEMENT = " <br> "
        private const val SEPARATOR = " "
        fun newBuilder(): Builder {
            return Builder()
        }

        fun logLevel(value: Int): String {
            return when (value) {
                Logger.VERBOSE -> "V"
                Logger.DEBUG -> "D"
                Logger.INFO -> "I"
                Logger.WARN -> "W"
                Logger.ERROR -> "E"
                Logger.ASSERT -> "A"
                else -> "UNKNOWN"
            }
        }

        fun <T> checkNotNull(obj: T?): T {
            if (obj == null) {
                throw NullPointerException()
            }
            return obj
        }
    }
}