package com.marvinelsen.cedict.internal

import com.marvinelsen.cedict.api.CedictDefinition
import com.marvinelsen.cedict.api.CedictEntry
import com.marvinelsen.cedict.api.CedictParser
import com.marvinelsen.chinese.transliteration.Syllable
import java.io.InputStream

internal class RegexCedictParser : CedictParser {
    companion object {
        private const val DEFINITION_SEPARATOR = '/'
        private const val GLOSS_SEPARATOR = ';'
        private const val COMMENT_MARKER = '#'

        // CC-CEDICT format: https://cc-cedict.org/wiki/format:syntax
        // Traditional Simplified [pin1 yin1] /gloss; gloss; .../gloss; gloss; .../
        // 皮實 皮实 [pi2 shi5] /(of things) durable/(of people) sturdy; tough/
        private val cedictLineRegex =
            """^(?<traditional>\S+) (?<simplified>\S+) \[(?<pinyinWithNumbers>.+)] /(?<definitions>.+)/$""".toRegex()
    }

    override fun parseCedict(inputStream: InputStream) =
        inputStream
            .bufferedReader()
            .lineSequence()
            .filterNot(::isComment)
            .map(::toCedictEntry)

    private fun isComment(line: String) = line[0] == COMMENT_MARKER

    @Suppress("DestructuringDeclarationWithTooManyEntries")
    internal fun toCedictEntry(line: String): CedictEntry {
        val matchResult = cedictLineRegex.matchEntire(line) ?: error("Line does not contain valid CEDICT syntax: $line")
        val (traditional, simplified, pinyinWithNumbers, definitions) = matchResult.destructured

        return CedictEntry(
            traditional = traditional,
            simplified = simplified,
            pinyinSyllables = toPinyinSyllables(pinyinWithNumbers),
            definitions = toCedictDefinitions(definitions),
        )
    }

    private fun toPinyinSyllables(pinyinWithNumbers: String) = pinyinWithNumbers
        .split(" ")
        .filter { Syllable.isValidPinyinWithToneNumberSyllable(it) }
        .map { Syllable.fromPinyinWithToneNumber(it) }

    private fun toCedictDefinitions(definitions: String) = definitions
        .split(DEFINITION_SEPARATOR)
        .map {
            CedictDefinition(
                glosses = it.split(GLOSS_SEPARATOR).map(String::trim)
            )
        }
}
