package com.laseroptek.raman.repository

import com.laseroptek.raman.data.model.SerialResult
import com.laseroptek.raman.data.source.serial.SerialPort
import com.laseroptek.raman.data.source.serial.SerialPortInterface
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import javax.inject.Inject

@ViewModelScoped
class SerialPortRepository @Inject constructor(
    private var serialPort: SerialPort
) : SerialPortInterface {

    companion object {
        // Fake constructor 1: Create a SerialPort with a specific (fake) File Descriptor
        fun createWithFakeRepository(): SerialPortRepository {
            return SerialPortRepository()
        }
    }

    private constructor() : this(
        serialPort = SerialPort.createDummy()
    )

    override suspend fun open(path: String, speed: Int) : Flow<SerialResult<ByteArray>> = callbackFlow {
        serialPort.open(path, speed, object : SerialPort.DataCallback {
            override fun onData(data: ByteArray) {
                // 명령단위로 데이터를 끊어서 처리 (한번에 송수신 가능한 크기는 1024 byte (1K))
                if (data.contentEquals(byteArrayOf(0x0E))) {
                    trySend(SerialResult.Error(Exception("Port error")))
                } else {
                    trySend(SerialResult.Success(data))
                }
            }
        })
        awaitClose()
    }

    override fun write(data: ByteArray) : SerialResult<String> {
        SerialPort.ensureLibraryLoaded()
        try {
            serialPort.write(data)
            return SerialResult.Success("Write success")
        } catch (e: Exception) {
            return SerialResult.Error(e)
        }
    }

    override fun close() : SerialResult<String> {
        try {
            serialPort.close()
            return SerialResult.Success("Port close success")
        } catch (e: Exception) {
            return SerialResult.Error(e)
        }
    }

    override fun getFD() : Int {
        return serialPort.getFD()
    }
}