package com.omixlab.lckcontrol.service

import android.hardware.HardwareBuffer
import android.os.ParcelFileDescriptor
import android.os.RemoteCallbackList
import android.util.Log
import com.omixlab.lckcontrol.shared.ILckStreamingCallback
import com.omixlab.lckcontrol.shared.ILckStreamingService
import com.omixlab.lckcontrol.streaming.StreamingManager
import com.omixlab.lckcontrol.streaming.StreamingState

/**
 * AIDL implementation for ILckStreamingService.
 * Bridges AIDL IPC calls to the StreamingManager.
 * Frame submission methods are one-way for non-blocking game render thread.
 */
class LckStreamingServiceImpl(
    private val streamingManager: StreamingManager,
) : ILckStreamingService.Stub() {

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

    private val callbacks = RemoteCallbackList<ILckStreamingCallback>()

    init {
        // Forward state changes to AIDL callbacks
        // Note: state observation requires coroutine scope — delegated to LckControlService
    }

    override fun registerTexturePool(
        buffers: Array<HardwareBuffer>,
        width: Int,
        height: Int,
        format: Int,
    ) {
        Log.d(TAG, "registerTexturePool: ${buffers.size} buffers, ${width}x$height")
        streamingManager.registerTexturePool(buffers, width, height, format)
    }

    override fun unregisterTexturePool() {
        Log.d(TAG, "unregisterTexturePool")
        streamingManager.unregisterTexturePool()
    }

    override fun submitVideoFrame(
        bufferIndex: Int,
        timestampNs: Long,
        gpuFence: ParcelFileDescriptor?,
    ) {
        val fenceFd = gpuFence?.detachFd() ?: -1
        streamingManager.submitVideoFrame(bufferIndex, timestampNs, fenceFd)
    }

    override fun submitAudioFrame(
        pcmData: ByteArray,
        timestampNs: Long,
        sampleRate: Int,
        channels: Int,
        bitsPerSample: Int,
    ) {
        streamingManager.submitAudioFrame(pcmData, timestampNs)
    }

    override fun isStreaming(): Boolean {
        return streamingManager.isStreaming()
    }

    override fun registerStreamingCallback(callback: ILckStreamingCallback) {
        callbacks.register(callback)
    }

    override fun unregisterStreamingCallback(callback: ILckStreamingCallback) {
        callbacks.unregister(callback)
    }

    // ── Broadcast helpers (called from LckControlService coroutine scope) ──

    fun broadcastStateChanged(state: StreamingState) {
        val stateStr = state.name
        val count = callbacks.beginBroadcast()
        try {
            for (i in 0 until count) {
                try {
                    callbacks.getBroadcastItem(i).onStreamingStateChanged(stateStr)
                } catch (_: Exception) {}
            }
        } finally {
            callbacks.finishBroadcast()
        }
    }

    fun broadcastStats(videoBitrate: Long, audioBitrate: Long, fps: Int, droppedFrames: Int) {
        val count = callbacks.beginBroadcast()
        try {
            for (i in 0 until count) {
                try {
                    callbacks.getBroadcastItem(i).onStreamingStats(
                        videoBitrate, audioBitrate, fps, droppedFrames,
                    )
                } catch (_: Exception) {}
            }
        } finally {
            callbacks.finishBroadcast()
        }
    }

    fun broadcastError(code: Int, message: String) {
        val count = callbacks.beginBroadcast()
        try {
            for (i in 0 until count) {
                try {
                    callbacks.getBroadcastItem(i).onStreamingError(code, message)
                } catch (_: Exception) {}
            }
        } finally {
            callbacks.finishBroadcast()
        }
    }

    fun broadcastBufferReleased(bufferIndex: Int) {
        val count = callbacks.beginBroadcast()
        try {
            for (i in 0 until count) {
                try {
                    callbacks.getBroadcastItem(i).onBufferReleased(bufferIndex)
                } catch (_: Exception) {}
            }
        } finally {
            callbacks.finishBroadcast()
        }
    }

    fun kill() {
        callbacks.kill()
    }
}
