package com.laseroptek.raman.data.source.serial

import android.os.ParcelFileDescriptor
import timber.log.Timber
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton

@Singleton // Ensure the wrapper is a singleton if using Hilt
class SerialPort @Inject constructor() {
    private var mFD = -1
    private var mDataCallBack: DataCallback? = null
    private var preventLibraryLoad = false

    private constructor(preventLoading: Boolean) : this() {
        this.preventLibraryLoad = preventLoading
        if (preventLoading) {
            Timber.d("SerialPort (dummy) created with library loading PREVENTED.")
        }
    }

    init {
        Timber.d("SerialPort instance created. Library loading will be lazy.")
    }

    companion object {
        @Volatile
        private var isLibraryLoaded = false

        /**
         * Consolidated Thread-safe lazy loader.
         * Using @Synchronized on the companion method ensures the library
         * is only loaded once for the entire application process.
         */
        @Synchronized
        fun ensureLibraryLoaded(): Boolean {
            if (!isLibraryLoaded) {
                try {
                    System.loadLibrary("serial_port")
                    isLibraryLoaded = true
                    Timber.d("Native library 'serial_port' loaded successfully.")
                } catch (e: Exception) {
                    Timber.e(e, "Failed to load native library 'serial_port'")
                }
            }
            return isLibraryLoaded
        }

        fun createDummy(): SerialPort {
            return SerialPort(preventLoading = true)
        }
    }

    fun open(path: String, speed: Int, dataCallBack: DataCallback?) {
        // 1. Check if we should load the library
        if (!preventLibraryLoad) {
            if (!ensureLibraryLoaded()) {
                Timber.e("Cannot open port: Native library failed to load.")
                dataCallBack?.onData(byteArrayOf(0xE))
                return
            }
        } else {
            Timber.w("Port open called on dummy instance. Skipping native logic.")
            return
        }

        try {
            mDataCallBack = dataCallBack
            val file = File(path)
            if (!file.exists()) throw Exception("Device path $path does not exist")

            val pFD = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)
            mFD = pFD.detachFd()
            pFD.close()

            if (mFD == -1) throw Exception("Failed to get valid FD")

            // Call native open
            open(mFD, speed)
            Timber.d("Port open success (fd = %d)", mFD)

        } catch (e: Exception) {
            Timber.e(e, "Error opening port")
            dataCallBack?.onData(byteArrayOf(0xE))
        }
    }

    fun write(data: ByteArray) {
        if (preventLibraryLoad) {
            Timber.w("Write called on dummy. Data: ${data.contentToString()}")
            return
        }

        val fd = mFD
        if (fd != -1 && isLibraryLoaded) {
            write(fd, data)
        } else {
            throw Exception("Port error: FD is -1 or Library not loaded")
        }
    }

    fun close() {
        val fd = mFD
        if (fd != -1) {
            if (isLibraryLoaded) {
                close(fd)
            }
            mFD = -1
            Timber.d("Port closed.")
        }
    }

    fun getFD(): Int = mFD

    // --- Native Callbacks & Methods ---

    private fun onNativeData(data: ByteArray) {
        if (mFD != -1) mDataCallBack?.onData(data)
    }

    private external fun open(fd: Int, speed: Int)
    private external fun write(fd: Int, data: ByteArray)
    private external fun close(fd: Int)

    interface DataCallback {
        fun onData(data: ByteArray)
    }
}