package de.duffy.screencast.connect

import android.content.Context
import android.media.MediaRouter
import android.os.Handler
import android.os.Looper
import android.util.Log

/**
 * Spricht den Bildschirmuebertragungs-Stack an, den Android selbst mitbringt.
 *
 * Vielen Geraeten fehlt nur die Bedienkachel ("Smart View"), nicht die Technik
 * dahinter. Ueber MediaRouter kommt man an genau dieselben Routen wie die
 * System-Kachel - ganz ohne Sonderrechte. Klappt das, uebernimmt Android die
 * komplette Miracast-Uebertragung in voller Qualitaet.
 */
class SystemDisplayRoutes(context: Context) {

    data class Route(
        val id: String,
        val name: String,
        val status: String,
        val enabled: Boolean,
        val connected: Boolean,
        internal val info: MediaRouter.RouteInfo
    )

    private val router =
        context.getSystemService(Context.MEDIA_ROUTER_SERVICE) as MediaRouter
    private val handler = Handler(Looper.getMainLooper())
    private var callback: MediaRouter.Callback? = null

    var onRoutesChanged: ((List<Route>) -> Unit)? = null

    /** Startet die aktive Suche. Das System funkt dabei nach Miracast-Empfaengern. */
    fun startScan() {
        if (callback != null) return
        val cb = object : MediaRouter.SimpleCallback() {
            override fun onRouteAdded(r: MediaRouter, info: MediaRouter.RouteInfo) = publish()
            override fun onRouteRemoved(r: MediaRouter, info: MediaRouter.RouteInfo) = publish()
            override fun onRouteChanged(r: MediaRouter, info: MediaRouter.RouteInfo) = publish()
            override fun onRouteSelected(r: MediaRouter, type: Int, info: MediaRouter.RouteInfo) = publish()
            override fun onRouteUnselected(r: MediaRouter, type: Int, info: MediaRouter.RouteInfo) = publish()
        }
        callback = cb
        try {
            router.addCallback(
                MediaRouter.ROUTE_TYPE_LIVE_VIDEO, cb,
                MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
            )
        } catch (e: Exception) {
            Log.w(TAG, "Routensuche nicht moeglich", e)
        }
        publish()
    }

    fun stopScan() {
        callback?.let { try { router.removeCallback(it) } catch (_: Exception) {} }
        callback = null
    }

    /** Alle Bildschirm-Routen ausser dem eingebauten Display. */
    fun routes(): List<Route> {
        val result = ArrayList<Route>()
        val default = try { router.defaultRoute } catch (_: Exception) { null }
        val selected = try { router.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO) } catch (_: Exception) { null }
        for (i in 0 until router.routeCount) {
            val info = router.getRouteAt(i)
            if (info == default) continue
            if (info.supportedTypes and MediaRouter.ROUTE_TYPE_LIVE_VIDEO == 0) continue
            result.add(
                Route(
                    id = "route:$i:${info.name}",
                    name = info.name?.toString() ?: "Unbenannt",
                    status = info.status?.toString().orEmpty(),
                    enabled = info.isEnabled,
                    connected = info == selected,
                    info = info
                )
            )
        }
        return result
    }

    /** Verbindet den Systemstack mit dieser Route - danach spiegelt Android selbst. */
    fun select(route: Route): Boolean = try {
        router.selectRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, route.info)
        true
    } catch (e: Exception) {
        Log.w(TAG, "Route liess sich nicht waehlen", e)
        false
    }

    /** Zurueck auf den eingebauten Bildschirm. */
    fun disconnect() {
        try {
            router.selectRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, router.defaultRoute)
        } catch (_: Exception) {}
    }

    private fun publish() {
        handler.post { onRoutesChanged?.invoke(routes()) }
    }

    companion object { private const val TAG = "SystemDisplayRoutes" }
}
