package hanson.xyz.vpnhotspotmod.preference

import android.content.Context
import android.graphics.Typeface
import android.net.LinkProperties
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.util.AttributeSet
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import hanson.xyz.vpnhotspotmod.R
import hanson.xyz.vpnhotspotmod.net.monitor.FallbackUpstreamMonitor
import hanson.xyz.vpnhotspotmod.net.monitor.UpstreamMonitor
import hanson.xyz.vpnhotspotmod.util.allRoutes
import hanson.xyz.vpnhotspotmod.util.format
import hanson.xyz.vpnhotspotmod.util.parseNumericAddress
import kotlinx.coroutines.launch
import timber.log.Timber

class UpstreamsPreference(context: Context, attrs: AttributeSet) : Preference(context, attrs),
        DefaultLifecycleObserver {
    companion object {
        private val internetV4Address = parseNumericAddress("8.8.8.8")
        private val internetV6Address = parseNumericAddress("2001:4860:4860::8888")
    }

    private open inner class Monitor : UpstreamMonitor.Callback {
        protected var currentInterfaces = emptyMap<String, Boolean>()
        val charSequence get() = currentInterfaces.map { (ifname, internet) ->
            if (internet) SpannableStringBuilder(ifname).apply {
                setSpan(StyleSpan(Typeface.BOLD), 0, length, 0)
            } else ifname
        }.joinTo(SpannableStringBuilder()).ifEmpty { "∅" }

        override fun onAvailable(properties: LinkProperties?) {
            val result = mutableMapOf<String, Boolean>()
            for (route in properties?.allRoutes ?: emptyList()) {
                result.compute(route.`interface` ?: continue) { _, internet ->
                    internet == true || try {
                        route.matches(internetV4Address) || route.matches(internetV6Address)
                    } catch (e: RuntimeException) {
                        Timber.w(e)
                        false
                    }
                }
            }
            currentInterfaces = result
            onUpdate()
        }
    }

    private val primary = Monitor()
    private val fallback = Monitor()

    init {
        (context as LifecycleOwner).lifecycle.addObserver(this)
        onUpdate()
    }

    override fun onStart(owner: LifecycleOwner) {
        UpstreamMonitor.registerCallback(primary)
        FallbackUpstreamMonitor.registerCallback(fallback)
    }
    override fun onStop(owner: LifecycleOwner) {
        UpstreamMonitor.unregisterCallback(primary)
        FallbackUpstreamMonitor.unregisterCallback(fallback)
    }

    private fun onUpdate() {
        (context as LifecycleOwner).lifecycleScope.launch {
            summary = context.getText(R.string.settings_service_upstream_monitor_summary).format(
                context.resources.configuration.locales[0], primary.charSequence, fallback.charSequence)
        }
    }
}
