package xyz.cottongin.radio247.audio

import java.io.InputStream

class IcyParser(
    private val input: InputStream,
    private val metaint: Int?,
    private val onAudioData: (ByteArray, Int, Int) -> Unit,
    private val onMetadata: (IcyMetadata) -> Unit
) {
    private val buffer = ByteArray(8192)

    fun readAll() {
        if (metaint == null) {
            passthrough()
        } else {
            parseWithMetadata(metaint)
        }
    }

    private fun passthrough() {
        while (true) {
            val n = input.read(buffer)
            if (n <= 0) break
            onAudioData(buffer, 0, n)
        }
    }

    private fun parseWithMetadata(metaint: Int) {
        require(metaint > 0) { "metaint must be positive, got $metaint" }
        val audioBuf = ByteArray(metaint)
        while (true) {
            val audioRead = readFully(audioBuf, 0, metaint)
            if (audioRead <= 0) break
            if (audioRead < metaint) {
                onAudioData(audioBuf, 0, audioRead)
                break
            }
            onAudioData(audioBuf, 0, metaint)

            val lengthByte = input.read()
            if (lengthByte < 0) break

            val metadataSize = (lengthByte and 0xFF) * 16
            if (metadataSize == 0) continue

            val metaBytes = ByteArray(metadataSize)
            val metaRead = readFully(metaBytes, 0, metadataSize)
            if (metaRead < metadataSize) break

            val raw = String(metaBytes, Charsets.UTF_8).trimEnd { it == '\u0000' }
            val parsed = parseMetaString(raw)
            onMetadata(parsed)
        }
    }

    private fun readFully(buf: ByteArray, off: Int, len: Int): Int {
        var total = 0
        while (total < len) {
            val n = input.read(buf, off + total, len - total)
            if (n <= 0) return total
            total += n
        }
        return total
    }

    private fun parseMetaString(raw: String): IcyMetadata {
        val streamTitle = extractStreamTitle(raw)
        val streamUrl = extractStreamUrl(raw)
        if (streamTitle != null) {
            val separatorIndex = streamTitle.indexOf(" - ")
            if (separatorIndex >= 0) {
                return IcyMetadata(
                    raw = raw,
                    artist = streamTitle.substring(0, separatorIndex).trim(),
                    title = streamTitle.substring(separatorIndex + 3).trim(),
                    streamUrl = streamUrl
                )
            }
            return IcyMetadata(raw = raw, title = streamTitle.trim(), artist = null, streamUrl = streamUrl)
        }
        return IcyMetadata(raw = raw, title = null, artist = null, streamUrl = streamUrl)
    }

    private fun extractStreamUrl(meta: String): String? {
        val key = "StreamUrl='"
        val start = meta.indexOf(key)
        if (start < 0) return null
        val valueStart = start + key.length
        val end = meta.indexOf("';", valueStart)
        if (end < 0) return null
        return meta.substring(valueStart, end)
    }

    private fun extractStreamTitle(meta: String): String? {
        val key = "StreamTitle='"
        val start = meta.indexOf(key)
        if (start < 0) return null
        val valueStart = start + key.length
        val end = meta.indexOf("';", valueStart)
        if (end < 0) return null
        return meta.substring(valueStart, end)
    }
}
