package com.grkj.iscs.util

import android.os.Handler
import android.os.Looper
import java.io.PrintWriter
import java.io.StringWriter
import java.util.concurrent.CountDownLatch

object Executor {

    private var io: Handler? = null
    private val main: Handler = Handler(Looper.getMainLooper())

    private val latch = CountDownLatch(1)

    init {
        val thread = Thread {
            Looper.prepare()
            io = Handler()
            latch.countDown()
            Looper.loop()
        }
        thread.isDaemon = true
        thread.start()
    }

    val mainHandler: Handler
        get() = main

    val ioHandler: Handler
        get() {
            if (io == null) {
                latch.await()
            }
            return io!!
        }

    fun runOnMain(run: Runnable) {
        main.post(run)
    }

    fun delayOnMain(delayMills: Long, run: Runnable) {
        main.postDelayed(run, delayMills)
    }

    fun repeatOnMain(run: () -> Boolean, intervalMills: Long, immediately: Boolean = true) {
        repeat(main, run, intervalMills, immediately)
    }

    fun runOnIO(run: Runnable) {
        val traces = Thread.currentThread().stackTrace
        ioHandler.post {
            runWithTraces(traces, run)
        }
    }

    fun runOnIO(run: Runnable, runnableTimeoutMillis: Long = 5000) {
        val traces = Thread.currentThread().stackTrace
        ioHandler.post {
            runWithTraces(traces, run, runnableTimeoutMillis)
        }
    }

    fun delayOnIO(run: Runnable, delayMills: Long, runnableTimeoutMillis: Long = 10_000) {
        val traces = Thread.currentThread().stackTrace
        ioHandler.postDelayed({
            runWithTraces(traces, run, runnableTimeoutMillis)
        }, delayMills)
    }

    fun delayOnIO(delayMills: Long, runnableTimeoutMillis: Long = 10_000, run: Runnable) {
        val traces = Thread.currentThread().stackTrace
        ioHandler.postDelayed({
            runWithTraces(traces, run, runnableTimeoutMillis)
        }, delayMills)
    }

    fun repeatOnIO(run: () -> Boolean, intervalMills: Long, immediately: Boolean = true, runnableTimeoutMillis: Long = 5_000) {
        val traces = Thread.currentThread().stackTrace
        repeat(ioHandler, {
            var blocked = true
            val thread = Thread.currentThread()
            delayOnMain(runnableTimeoutMillis) {
                // 如果线程被阻塞达到 1 秒，则打断唤醒
                if (blocked) {
                    printTimeoutTraces(traces)
                    thread.interrupt()
                }
            }
            val res = run()
            blocked = false
            return@repeat res
        }, intervalMills, immediately)
    }

    private fun runWithTraces(traces: Array<StackTraceElement>, run: Runnable, runnableTimeoutMillis: Long = 10_000) {
        var blocked = true
        val thread = Thread.currentThread()
        delayOnMain(runnableTimeoutMillis) {
            // 如果线程被阻塞达到 1 秒，则打断唤醒
            if (blocked) {
                printTimeoutTraces(traces)
                thread.interrupt()
            }
        }
        run.run()
        blocked = false
    }

    private fun printTimeoutTraces(traces: Array<StackTraceElement>) {
        val writer = StringWriter()
        val print = PrintWriter(writer)
        for (traceElement in traces) {
            print.println("\tat $traceElement")
        }
//        Logger.w("Executor", "执行器超时（10秒）：\n${writer}")
    }

    private fun repeat(
        handler: Handler,
        run: () -> Boolean,
        intervalMills: Long,
        immediately: Boolean = true
    ) {
        if (immediately) {
            if (run()) {
                handler.postDelayed({
                    repeat(handler, run, intervalMills, immediately)
                }, intervalMills)
            }
        } else {
            handler.postDelayed({
                if (run()) {
                    repeat(handler, run, intervalMills, immediately)
                }
            }, intervalMills)
        }
    }

}
