package com.obdwidgets.obd

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.UUID

/**
 * ELM327 OBD2 Protokoll-Implementierung
 * Unterstützt die meisten gängigen OBD2-Adapter
 */
class ELM327Protocol(
    private val device: BluetoothDevice
) : OBDProtocol {
    
    private var socket: BluetoothSocket? = null
    private var inputStream: InputStream? = null
    private var outputStream: OutputStream? = null
    
    companion object {
        // Standard UUID für SPP (Serial Port Profile)
        private val SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
        
        // Timeouts
        private const val READ_TIMEOUT = 2000L
        private const val WRITE_TIMEOUT = 1000L
    }
    
    override suspend fun initialize(): Boolean = withContext(Dispatchers.IO) {
        try {
            // Socket erstellen
            socket = device.createRfcommSocketToServiceRecord(SPP_UUID)
            
            // Verbinden
            socket?.connect()
            
            inputStream = socket?.inputStream
            outputStream = socket?.outputStream
            
            // ELM327 initialisieren
            sendCommand("ATZ") // Reset
            Thread.sleep(1000)
            
            sendCommand("ATE0") // Echo off
            sendCommand("ATL0") // Linefeeds off
            sendCommand("ATS0") // Spaces off
            sendCommand("ATH0") // Headers off
            sendCommand("ATSP0") // Auto protocol
            
            // Verbindung testen
            val response = sendCommand("0100") // Mode 01, PID 00
            response != null && response.isNotEmpty()
            
        } catch (e: Exception) {
            disconnect()
            false
        }
    }
    
    override suspend fun readPid(pid: String): ByteArray? = withContext(Dispatchers.IO) {
        try {
            val response = sendCommand(pid)
            if (response == null || response.isEmpty()) {
                return@withContext null
            }
            
            // Response parsen (Format: "41 0D 3C" -> [0x3C])
            val parts = response.trim().split(" ")
            if (parts.size < 3) return@withContext null
            
            // Erste zwei Bytes sind Header (z.B. "41 0D"), danach kommen die Daten
            val dataBytes = parts.drop(2).mapNotNull { part ->
                try {
                    part.toInt(16).toByte()
                } catch (e: Exception) {
                    null
                }
            }
            
            if (dataBytes.isEmpty()) return@withContext null
            
            return@withContext dataBytes.toByteArray()
            
        } catch (e: Exception) {
            null
        }
    }
    
    private suspend fun sendCommand(command: String): String? = withContext(Dispatchers.IO) {
        try {
            val output = outputStream ?: return@withContext null
            
            // Command senden
            val cmd = "$command\r"
            output.write(cmd.toByteArray())
            output.flush()
            
            // Response lesen
            val input = inputStream ?: return@withContext null
            val buffer = StringBuilder()
            val startTime = System.currentTimeMillis()
            
            while (System.currentTimeMillis() - startTime < READ_TIMEOUT) {
                if (input.available() > 0) {
                    val byte = input.read()
                    if (byte == -1) break
                    
                    val char = byte.toChar()
                    if (char == '>') break // ELM327 Prompt
                    
                    buffer.append(char)
                } else {
                    Thread.sleep(10)
                }
            }
            
            val response = buffer.toString().trim()
            if (response.isEmpty()) return@withContext null
            
            return@withContext response
            
        } catch (e: IOException) {
            null
        }
    }
    
    override suspend fun disconnect() {
        withContext(Dispatchers.IO) {
            try {
                inputStream?.close()
                outputStream?.close()
                socket?.close()
            } catch (e: Exception) {
                // Ignore
            } finally {
                inputStream = null
                outputStream = null
                socket = null
            }
        }
    }
    
    override fun isConnected(): Boolean {
        return socket?.isConnected == true
    }
}
