package com.dafay.demo.aidl.proxy

import android.app.Application
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.RemoteException
import com.dafay.demo.aidl.TestInterface
import com.dafay.demo.aidl.callback.ReceiveMessageCallback
import com.dafay.demo.aidl.callback.ServiceConnectCallback
import com.dafay.demo.aidl.inter.ReceiveListener
import com.dafay.demo.lib.base.utils.ApplicationUtils
import com.dafay.demo.lib.base.utils.debug
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

private const val SERVICE_PKG_NAME = "com.dafay.demo.aidl.server"
private const val SERVICE_CLASS_NAME = "com.dafay.demo.aidl.service.TestService"

/**
 * 问题
 * 1. Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
 * 使用 bindService(Intent service, int flags, Executor executor,ServiceConnection conn)，回调在子线程
 * 2. 如果并发两个绑定 bindToService 函数调用，第一个函数的回调会不会被替换掉导致第一个函数无法返回呢？
 */
abstract class BaseServiceConnect {
    private var application: Application = ApplicationUtils.getApp()
    private var intent: Intent = Intent().apply { component = ComponentName(SERVICE_PKG_NAME, SERVICE_CLASS_NAME) }

    @Volatile
    protected var remoteServiceProxy: TestInterface? = null
    protected var receiveListeners = CopyOnWriteArrayList<ReceiveListener>()
    protected val mainHandler = Handler(Looper.getMainLooper())

    private val receiveMessageCallback: ReceiveMessageCallback = object : ReceiveMessageCallback.Stub() {
        @Throws(RemoteException::class)
        override fun onReceive(payload: String) {
            debug("onReceive:$payload")
            mainHandler.post {
                receiveListeners.forEach { it.onReceive(payload) }
            }
        }
    }
    private val serviceConnectCallback: ServiceConnectCallback = object : ServiceConnectCallback.Stub() {
        @Throws(RemoteException::class)
        override fun onConnectReply(message: String) {
        }
    }

    fun bindToService(): Boolean {
        if (remoteServiceProxy != null && remoteServiceProxy!!.asBinder().isBinderAlive) {
            return true
        }
        val countDownLatch = CountDownLatch(1)
        val serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName, service: IBinder) {
                debug("bindToService onServiceConnected")
                remoteServiceProxy = TestInterface.Stub.asInterface(service)
                remoteServiceProxy!!.registerServiceCallback(serviceConnectCallback)
                remoteServiceProxy!!.registerReceiveMessageCallback(receiveMessageCallback)
                countDownLatch.countDown()
            }

            override fun onServiceDisconnected(name: ComponentName) {
                debug("bindToService onServiceDisconnected")
                release()
                countDownLatch.countDown()
            }
        }
        val result = application.bindService(
            intent,
            Context.BIND_AUTO_CREATE,
            Executors.newSingleThreadExecutor(), // 使回调在子线程
            serviceConnection
        )
        if (!result) {
            countDownLatch.countDown()
        }
        debug("countDownLatch start")
        countDownLatch.await(10 * 1000, TimeUnit.SECONDS)
        debug("countDownLatch end")
        return remoteServiceProxy != null
    }

    protected fun isServiceConnected(): Boolean {
        return bindToService()
    }

    private fun release() {
        remoteServiceProxy = null
    }
}

