package com.marvinelsen.willow.domain

import kotlinx.serialization.json.Json
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.ResultSet

class SqliteDictionary(private val connection: Connection) : Dictionary {

    private val searchSimplifiedPreparedStatement: PreparedStatement by lazy {
        connection.prepareStatement(
            """
            SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions 
            FROM cedict 
            WHERE simplified GLOB ?
            ORDER BY character_count ASC
            """.trimIndent()
        )
    }

    private val searchTraditionalPreparedStatement: PreparedStatement by lazy {
        connection.prepareStatement(
            """
            SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions 
            FROM cedict 
            WHERE traditional GLOB ?
            ORDER BY character_count ASC
            """.trimIndent()
        )
    }

    private val findWordsContaining: PreparedStatement by lazy {
        connection.prepareStatement(
            """
            SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions 
            FROM cedict
            WHERE traditional LIKE ?
            ORDER BY character_count ASC
            """.trimIndent()
        )
    }

    override fun search(query: String, searchMode: SearchMode) = when (searchMode) {
        SearchMode.PINYIN -> TODO()
        SearchMode.SIMPLIFIED -> searchSimplified(query)
        SearchMode.TRADITIONAL -> searchTraditional(query)
        SearchMode.ENGLISH -> TODO()
    }

    override fun findWordsContaining(entry: DictionaryEntry): List<DictionaryEntry> {
        findWordsContaining.setString(1, "_%${entry.traditional}%")

        val resultSet: ResultSet = findWordsContaining.executeQuery()

        return resultSet.toListOfDictionaryEntries()
    }

    override fun findSentencesContaining(entry: DictionaryEntry): List<DictionaryEntry> {
        return emptyList()
    }

    override fun findCharacters(entry: DictionaryEntry): List<DictionaryEntry> {
        return emptyList()
    }

    private fun searchSimplified(query: String): List<DictionaryEntry> {
        searchSimplifiedPreparedStatement.setString(1, "$query*")

        val resultSet: ResultSet = searchSimplifiedPreparedStatement.executeQuery()

        return resultSet.toListOfDictionaryEntries()
    }

    private fun searchTraditional(query: String): List<DictionaryEntry> {
        searchTraditionalPreparedStatement.setString(1, "$query*")

        val resultSet: ResultSet = searchTraditionalPreparedStatement.executeQuery()

        return resultSet.toListOfDictionaryEntries()
    }
}

@Suppress("MagicNumber")
private fun ResultSet.toDictionaryEntry() = DictionaryEntry(
    traditional = this.getString(1),
    simplified = this.getString(2),
    pinyinWithToneMarks = this.getString(3),
    pinyinWithToneNumbers = this.getString(4),
    zhuyin = this.getString(5),
    definitions = Json.decodeFromString(this.getString(6))
)

private fun ResultSet.toListOfDictionaryEntries() = buildList {
    this@toListOfDictionaryEntries.use {
        while (it.next()) {
            add(it.toDictionaryEntry())
        }
    }
}
