package com.xyzshell.andinfo.libs

import android.Manifest
import android.content.Context
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CameraMetadata
import android.os.Build
import android.os.Handler
import android.os.Looper
import com.xyzshell.andinfo.R
import com.xyzshell.andinfo.libs.camera.models.CameraCapability
import com.xyzshell.andinfo.libs.camera.models.CameraFacing
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow

class CameraInfo(private val context: Context) {

    private val cameraManager: CameraManager? = context.getSystemService(
        CameraManager::class.java
    )

    val cameraIds: List<String>
        get() = cameraManager?.cameraIdList?.toList() ?: emptyList()

    fun getCameraCharacteristics(cameraId: String): CameraCharacteristics? {
        return cameraManager?.getCameraCharacteristics(cameraId)
    }

    fun cameraIdsFlow() = cameraManager?.let { manager ->
        callbackFlow {
            val onCameraIdUpdated = {
                trySend(manager.cameraIdList.toList())
            }

            val availabilityCallback = object : CameraManager.AvailabilityCallback() {
                override fun onCameraAvailable(cameraId: String) {
                    onCameraIdUpdated()
                }

                override fun onCameraUnavailable(cameraId: String) {
                    onCameraIdUpdated()
                }
            }

            manager.registerAvailabilityCallback(
                availabilityCallback, Handler(Looper.getMainLooper())
            )

            onCameraIdUpdated()

            awaitClose {
                manager.unregisterAvailabilityCallback(availabilityCallback)
            }
        }
    }

    fun getCameraFacing(characteristics: CameraCharacteristics): CameraFacing? {
        return characteristics.get(CameraCharacteristics.LENS_FACING)?.let {
            when (it) {
                CameraMetadata.LENS_FACING_BACK -> CameraFacing.BACK
                CameraMetadata.LENS_FACING_FRONT -> CameraFacing.FRONT
                CameraMetadata.LENS_FACING_EXTERNAL -> CameraFacing.EXTERNAL
                else -> null
            }
        }
    }

    fun getPixelArraySize(characteristics: CameraCharacteristics): String? {
        return characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)?.toString()
    }

    fun getSensorPhysicalSize(characteristics: CameraCharacteristics): String? {
        return characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)?.toString()
    }

    fun hasFlashUnit(characteristics: CameraCharacteristics): Boolean? {
        return characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)
    }

    fun getAvailableApertures(characteristics: CameraCharacteristics): FloatArray? {
        return characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)
    }

    fun getAvailableCapabilities(characteristics: CameraCharacteristics): List<CameraCapability> {
        val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
        return capabilities?.toList()?.mapNotNull { capability ->
            when (capability) {
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE -> CameraCapability.BACKWARD_COMPATIBLE
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR -> CameraCapability.MANUAL_SENSOR
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING -> CameraCapability.MANUAL_POST_PROCESSING
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW -> CameraCapability.RAW
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING -> CameraCapability.PRIVATE_REPROCESSING
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS -> CameraCapability.READ_SENSOR_SETTINGS
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE -> CameraCapability.BURST_CAPTURE
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING -> CameraCapability.YUV_REPROCESSING
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT -> CameraCapability.DEPTH_OUTPUT
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO -> CameraCapability.CONSTRAINED_HIGH_SPEED_VIDEO
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) CameraCapability.MOTION_TRACKING else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) CameraCapability.LOGICAL_MULTI_CAMERA else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) CameraCapability.MONOCHROME else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) CameraCapability.SECURE_IMAGE_DATA else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) CameraCapability.SYSTEM_CAMERA else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) CameraCapability.OFFLINE_PROCESSING else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) CameraCapability.ULTRA_HIGH_RESOLUTION_SENSOR else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) CameraCapability.REMOSAIC_REPROCESSING else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) CameraCapability.DYNAMIC_RANGE_TEN_BIT else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) CameraCapability.STREAM_USE_CASE else null
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) CameraCapability.COLOR_SPACE_PROFILES else null
                else -> null
            }
        } ?: emptyList()
    }
}
