package database

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.mariadb.jdbc.Connection
import org.tinylog.Logger
import java.sql.DriverManager
import java.util.function.Consumer

/**
 * A class to manage a connection to a MariaDB database.
 *
 * @property address The address of the MariaDB server.
 * @property port The port number of the MariaDB server.
 * @property dbName The name of the database to connect to.
 * @property username The username for the database connection.
 * @property password The password for the database connection.
 */
@Suppress("unused")
class MariaDB(
    address: String = "localhost",
    port: Int = 3306,
    dbName: String = "aas",
    username: String = "admin",
    password: String = "admin"
) {
    private var connection: Connection? = null
    var connected: Boolean = false
    var SoundbankList: ArrayList<Soundbank> = ArrayList()
    var MessagebankList: ArrayList<Messagebank> = ArrayList()
    var LanguageLinkList: ArrayList<LanguageLink> = ArrayList()
    var SchedulebankList: ArrayList<ScheduleBank> = ArrayList()

    companion object {
        fun ValidDate(date: String): Boolean {
            // Check if the date is in the format DD/MM/YYYY
            val regex = Regex("""^\d{2}/\d{2}/\d{4}$""")
            return regex.matches(date)
        }

        fun ValidTime(time: String): Boolean {
            // Check if the time is in the format HH:MM:SS
            val regex = Regex("""^\d{2}:\d{2}:\d{2}$""")
            return regex.matches(time)
        }

        private val objectMapper = jacksonObjectMapper()

        /**
         * Convert SoundbankList, MessagebankList, LanguageLinkList, or SchedulebankList to a JSON String.
         * @param list The ArrayList to convert to a String.
         * @return A JSON String representation of the ArrayList.
         */
        fun <T> ArrayListtoString(list: ArrayList<T>): String {
            return try {
                objectMapper.writeValueAsString(list.toArray())
            } catch (e: Exception) {
                Logger.error("Error converting list to JSON: ${e.message}" as Any)
                "[]"
            }

        }
    }

    init {
        try {
            connection =
                DriverManager.getConnection("jdbc:mariadb://$address:$port/$dbName", username, password) as Connection
            Logger.info("Connected to MariaDB" as Any)
            connected = true

            runBlocking {
                withContext(Dispatchers.IO) {
                    Reload_Messagebank()
                    Reload_Soundbank()
                    Reload_LanguageLink()
                    Reload_Schedulebank()
                }
            }



            Logger.info { "Loading MariaDB completed" }
            Logger.info { "Soundbank count: ${SoundbankList.size}" }
            Logger.info { "Messagebank count: ${MessagebankList.size}" }
            Logger.info { "LanguageLink count: ${LanguageLinkList.size}" }
            Logger.info { "Schedulebank count: ${SchedulebankList.size}" }


        } catch (e: Exception) {
            Logger.error("Failed to connect to MariaDB: ${e.message}" as Any)
        }
    }

    /**
     * Closes the MariaDB connection.
     */
    fun close() {
        try {
            connection?.close()

            Logger.info("Connection to MariaDB closed" as Any)
        } catch (e: Exception) {
            Logger.error("Error closing MariaDB connection: ${e.message}" as Any)
        }
        connected = false
    }

    /**
     * Add Log to database
     * @param machine The machine name or identifier.
     * @param description The description of the log entry.
     */
    fun Add_Log(machine: String, description: String) {
        val currentDate = java.time.LocalDate.now()
        val currentTime = java.time.LocalTime.now().withNano(0) // remove nanoseconds for cleaner format
        val dateString = currentDate.format(java.time.format.DateTimeFormatter.ofPattern("dd/MM/yyyy"))
        val timeString = currentTime.format(java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss"))

        try {
            val statement =
                connection?.prepareStatement("INSERT INTO log (datenya, timenya, machine, description) VALUES (?, ?, ?, ?)")
            statement?.setString(1, dateString)
            statement?.setString(2, timeString)
            statement?.setString(3, machine)
            statement?.setString(4, description)
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Log added: [$dateString $timeString] [$machine] $description" as Any)
            } else {
                Logger.warn("No log entry added for: [$dateString $timeString] [$machine] $description" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error adding log entry: ${e.message}" as Any)
        }
    }

    /**
     * Exports the log table to an XLSX workbook for a specific date and optional filter.
     * @param logDate The date string in format "dd/MM/yyyy".
     * @param logFilter The filter string for the description or machine. If empty, exports all logs for the date.
     * @return An XSSFWorkbook containing the filtered log data, or null if an error occurred.
     */
    fun Export_Log_XLSX(logDate: String, logFilter: String): XSSFWorkbook? {
        try {
            val statement = connection?.prepareStatement(
                if (logFilter.isBlank()) {
                    "SELECT * FROM log WHERE datenya = ?"
                } else {
                    "SELECT * FROM log WHERE datenya = ? AND (description LIKE ? OR machine LIKE ?)"
                }
            )
            statement?.setString(1, logDate)
            if (!logFilter.isBlank()) {
                val filter = "%$logFilter%"
                statement?.setString(2, filter)
                statement?.setString(3, filter)
            }
            val resultSet = statement?.executeQuery()
            val workbook = XSSFWorkbook()
            val sheet = workbook.createSheet("Log")
            val headerRow = sheet.createRow(0)
            val headers = arrayOf("Index", "datenya", "timenya", "machine", "description")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.createCell(colIndex)
                cell.setCellValue(header)
            }
            var rowIndex = 1
            while (resultSet?.next() == true) {
                val row = sheet.createRow(rowIndex++)
                row.createCell(0).setCellValue(resultSet.getString("index"))
                row.createCell(1).setCellValue(resultSet.getString("datenya"))
                row.createCell(2).setCellValue(resultSet.getString("timenya"))
                row.createCell(3).setCellValue(resultSet.getString("machine"))
                row.createCell(4).setCellValue(resultSet.getString("description"))
            }
            for (i in headers.indices) {
                sheet.autoSizeColumn(i)
            }
            return workbook
        } catch (e: Exception) {
            Logger.error { "Error exporting Log, Msg: ${e.message}" }
        }
        return null
    }

    /**
     * Get All Log from database
     * @param consumer A Consumer that will receive the list of logs
     */
    fun GetLog(consumer: Consumer<ArrayList<Log>>) {
        val logList = ArrayList<Log>()
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM log")
            while (resultSet?.next() == true) {
                val log = Log(
                    resultSet.getLong("index").toULong(),
                    resultSet.getString("datenya"),
                    resultSet.getString("timenya"),
                    resultSet.getString("machine"),
                    resultSet.getString("description")
                )
                logList.add(log)
            }
        } catch (e: Exception) {
            Logger.error("Error fetching logs: ${e.message}" as Any)
        }
        consumer.accept(logList)
    }

    /**
     * Get Log from database by date
     * @param date The date to filter logs by (format: DD/MM/YYYY)
     * @param consumer A Consumer that will receive the list of logs for the specified date
     */
    fun GetLog(date: String, consumer: Consumer<ArrayList<Log>>) {
        val logList = ArrayList<Log>()
        if (ValidDate(date)) {
            try {
                val statement = connection?.prepareStatement("SELECT * FROM log WHERE datenya = ?")
                statement?.setString(1, date)
                val resultSet = statement?.executeQuery()
                while (resultSet?.next() == true) {
                    val log = Log(
                        resultSet.getLong("index").toULong(),
                        resultSet.getString("datenya"),
                        resultSet.getString("timenya"),
                        resultSet.getString("machine"),
                        resultSet.getString("description")
                    )
                    logList.add(log)
                }
            } catch (e: Exception) {
                Logger.error("Error fetching logs for date $date: ${e.message}" as Any)
            }
        }

        consumer.accept(logList)
    }

    fun GetLog(date: String, filter: String, consumer: Consumer<ArrayList<Log>>) {
        val logList = ArrayList<Log>()
        if (ValidDate(date)) {
            try {
                val statement =
                    connection?.prepareStatement("SELECT * FROM log WHERE datenya = ? AND description LIKE ?")
                statement?.setString(1, date)
                statement?.setString(2, "%$filter%")
                val resultSet = statement?.executeQuery()
                while (resultSet?.next() == true) {
                    val log = Log(
                        resultSet.getLong("index").toULong(),
                        resultSet.getString("datenya"),
                        resultSet.getString("timenya"),
                        resultSet.getString("machine"),
                        resultSet.getString("description")
                    )
                    logList.add(log)
                }
            } catch (e: Exception) {
                Logger.error("Error fetching logs for date $date with filter $filter: ${e.message}" as Any)
            }
        }

        consumer.accept(logList)
    }

    /**
     * Reloads the ScheduleBank list from the database.
     */
    fun Reload_Schedulebank() {
        SchedulebankList.clear()
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM schedulebank")
            while (resultSet?.next() == true) {
                val schedulebank = ScheduleBank(
                    resultSet.getLong("index").toUInt(),
                    resultSet.getString("Description"),
                    resultSet.getString("Day"),
                    resultSet.getString("Time"),
                    resultSet.getString("Soundpath"),
                    resultSet.getInt("Repeat").toUByte(),
                    resultSet.getBoolean("Enable"),
                    resultSet.getString("BroadcastZones"),
                    resultSet.getString("Language")
                )
                SchedulebankList.add(schedulebank)
            }
        } catch (e: Exception) {
            Logger.error("Error fetching schedulebanks: ${e.message}" as Any)
        }
    }

    /**
     * Adds a new schedulebank entry to the database.
     * @param schedulebank The ScheduleBank object to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_Schedulebank(schedulebank: ScheduleBank): Boolean {
        if (!ValidDate(schedulebank.Day)) {
            Logger.error("Error adding schedulebank entry: Invalid date format ${schedulebank.Day}" as Any)
            return false
        }
        if (!ValidTime(schedulebank.Time)) {
            Logger.error("Error adding schedulebank entry: Invalid time format ${schedulebank.Time}" as Any)
            return false
        }
        try {
            val statement =
                connection?.prepareStatement("INSERT INTO schedulebank (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
            statement?.setString(1, schedulebank.Description)
            statement?.setString(2, schedulebank.Day)
            statement?.setString(3, schedulebank.Time)
            statement?.setString(4, schedulebank.Soundpath)
            statement?.setInt(5, schedulebank.Repeat.toInt())
            statement?.setBoolean(6, schedulebank.Enable)
            statement?.setString(7, schedulebank.BroadcastZones)
            statement?.setString(8, schedulebank.Language)
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Schedulebank added: ${schedulebank.Description}" as Any)
                return true
            } else {
                Logger.warn("No schedulebank entry added for: ${schedulebank.Description}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error adding schedulebank entry: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Adds multiple schedulebank entries to the database using bulk insert.
     * @param schedulebankList ArrayList of ScheduleBank objects to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_Schedulebank(schedulebankList: ArrayList<ScheduleBank>): Boolean {
        if (schedulebankList.isNotEmpty()) {
            try {
                if (connection != null) {
                    connection!!.autoCommit = false
                    val sql =
                        "INSERT INTO schedulebank (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
                    val statement = connection!!.prepareStatement(sql)
                    for (sb in schedulebankList) {
                        if (!ValidDate(sb.Day) || !ValidTime(sb.Time)) {
                            Logger.error("Invalid date or time format for schedulebank: ${sb.Description}" as Any)
                            continue
                        }
                        statement.setString(1, sb.Description)
                        statement.setString(2, sb.Day)
                        statement.setString(3, sb.Time)
                        statement.setString(4, sb.Soundpath)
                        statement.setInt(5, sb.Repeat.toInt())
                        statement.setBoolean(6, sb.Enable)
                        statement.setString(7, sb.BroadcastZones)
                        statement.setString(8, sb.Language)
                        statement.addBatch()
                    }
                    statement.executeBatch()
                    connection!!.commit()
                    Logger.info("Bulk schedulebank insert successful: ${schedulebankList.size} entries" as Any)
                    connection!!.autoCommit = true
                    return true
                }
            } catch (e: Exception) {
                Logger.error("Error adding schedulebank entries: ${e.message}" as Any)
            }
        }
        return false
    }

    /**
     * Updates an existing schedulebank entry in the database by its index.
     * @param index The index of the schedulebank entry to update.
     * @param schedulebank The ScheduleBank object with updated values.
     * @return True if the update was successful, false otherwise.
     */
    fun Update_Schedulebank_by_index(index: UInt, schedulebank: ScheduleBank): Boolean {
        if (!ValidDate(schedulebank.Day)) {
            Logger.error("Error updating schedulebank entry: Invalid date format ${schedulebank.Day}" as Any)
            return false
        }
        if (!ValidTime(schedulebank.Time)) {
            Logger.error("Error updating schedulebank entry: Invalid time format ${schedulebank.Time}" as Any)
            return false
        }
        try {
            val statement =
                connection?.prepareStatement("UPDATE schedulebank SET Description = ?, Day = ?, Time = ?, Soundpath = ?, Repeat = ?, Enable = ?, BroadcastZones = ?, Language = ? WHERE `index` = ?")
            statement?.setString(1, schedulebank.Description)
            statement?.setString(2, schedulebank.Day)
            statement?.setString(3, schedulebank.Time)
            statement?.setString(4, schedulebank.Soundpath)
            statement?.setInt(5, schedulebank.Repeat.toInt())
            statement?.setBoolean(6, schedulebank.Enable)
            statement?.setString(7, schedulebank.BroadcastZones)
            statement?.setString(8, schedulebank.Language)
            statement?.setLong(9, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Schedulebank updated at index $index: ${schedulebank.Description}" as Any)
                return true
            } else {
                Logger.warn("No schedulebank entry updated at index $index for: ${schedulebank.Description}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error updating schedulebank entry at index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Deletes a schedulebank entry by its index.
     * @param index The index of the schedulebank entry to delete.
     * @return True if the deletion was successful, false otherwise.
     */
    fun Delete_Schedulebank_by_index(index: UInt): Boolean {
        try {
            val statement = connection?.prepareStatement("DELETE FROM schedulebank WHERE `index` = ?")
            statement?.setLong(1, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Deleted $rowsAffected row(s) from schedulebank with index $index" as Any)
                return true
            } else {
                Logger.warn("No rows deleted from schedulebank with index $index" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error deleting from schedulebank with index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Clears all entries from the schedulebank table in the database and the local list.
     */
    fun Clear_Schedulebank(): Boolean {
        try {
            val statement = connection?.createStatement()
            // use TRUNCATE to reset auto increment index
            statement?.executeUpdate("TRUNCATE TABLE schedulebank")
            Logger.info("Schedulebank table cleared" as Any)
            SchedulebankList.clear()
            return true
        } catch (e: Exception) {
            Logger.error("Error clearing schedulebank table: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Exports the schedulebank table to an XLSX workbook.
     * @return An XSSFWorkbook containing the schedulebank data, or null if an error occurred.
     */
    fun Export_Schedulebank_XLSX(): XSSFWorkbook? {
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM schedulebank")
            val workbook = XSSFWorkbook()
            val sheet = workbook.createSheet("Schedulebank")
            val headerRow = sheet.createRow(0)
            val headers = arrayOf(
                "Index",
                "Description",
                "Day",
                "Time",
                "Soundpath",
                "Repeat",
                "Enable",
                "BroadcastZones",
                "Language"
            )
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.createCell(colIndex)
                cell.setCellValue(header)
            }
            var rowIndex = 1
            while (resultSet?.next() == true) {
                val row = sheet.createRow(rowIndex++)
                row.createCell(0).setCellValue(resultSet.getString("index"))
                row.createCell(1).setCellValue(resultSet.getString("Description"))
                row.createCell(2).setCellValue(resultSet.getString("Day"))
                row.createCell(3).setCellValue(resultSet.getString("Time"))
                row.createCell(4).setCellValue(resultSet.getString("Soundpath"))
                row.createCell(5).setCellValue(resultSet.getString("Repeat"))
                row.createCell(6).setCellValue(resultSet.getString("Enable"))
                row.createCell(7).setCellValue(resultSet.getString("BroadcastZones"))
                row.createCell(8).setCellValue(resultSet.getString("Language"))
            }
            for (i in headers.indices) {
                sheet.autoSizeColumn(i)
            }
            return workbook
        } catch (e: Exception) {
            Logger.error { "Error exporting Schedulebank, Msg: ${e.message}" }
        }
        return null
    }

    /**
     * Imports schedulebank entries from an XLSX workbook.
     * @param workbook The XSSFWorkbook containing the schedulebank data.
     * @return True if the import was successful, false otherwise.
     */
    fun Import_Schedulebank_XLSX(workbook: XSSFWorkbook): Boolean {
        try {
            val sheet = workbook.getSheet("Schedulebank") ?: throw Exception("No sheet named 'Schedulebank' found")
            val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
            val headers = arrayOf(
                "Index",
                "Description",
                "Day",
                "Time",
                "Soundpath",
                "Repeat",
                "Enable",
                "BroadcastZones",
                "Language"
            )
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found")
                if (cell.stringCellValue != header) throw Exception("Header '$header' not found")
            }
            // clear existing schedulebank
            Clear_Schedulebank()
            // read each row and insert into database
            val _schedulebankList = ArrayList<ScheduleBank>()
            for (rowIndex in 1..sheet.lastRowNum) {
                val row = sheet.getRow(rowIndex) ?: continue
                val description = row.getCell(1)?.stringCellValue ?: continue
                val day = row.getCell(2)?.stringCellValue ?: continue
                val time = row.getCell(3)?.stringCellValue ?: continue
                val soundpath = row.getCell(4)?.stringCellValue ?: continue
                val repeat = row.getCell(5)?.stringCellValue?.toUByteOrNull() ?: continue
                val enable = row.getCell(6)?.stringCellValue?.toBooleanStrictOrNull() ?: continue
                val broadcastZones = row.getCell(7)?.stringCellValue ?: continue
                val language = row.getCell(8)?.stringCellValue ?: continue
                val schedulebank =
                    ScheduleBank(0u, description, day, time, soundpath, repeat, enable, broadcastZones, language)
                _schedulebankList.add(schedulebank)
            }
            return Add_Schedulebank(_schedulebankList)
        } catch (e: Exception) {
            Logger.error { "Error importing Schedulebank, Msg: ${e.message}" }
        }
        return false
    }

    /**
     * Reloads the language link list from the database.
     */
    fun Reload_LanguageLink() {
        LanguageLinkList.clear()
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM languagelink")
            while (resultSet?.next() == true) {
                val languageLink = LanguageLink(
                    resultSet.getLong("index").toUInt(),
                    resultSet.getString("TAG"),
                    resultSet.getString("Language")
                )
                LanguageLinkList.add(languageLink)
            }
        } catch (e: Exception) {
            Logger.error("Error fetching language links: ${e.message}" as Any)
        }
    }

    /**
     * Adds a new language link entry to the database.
     * @param languageLink The LanguageLink object to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_LanguageLink(languageLink: LanguageLink): Boolean {
        try {
            val statement = connection?.prepareStatement("INSERT INTO languagelink (TAG, Language) VALUES (?, ?)")
            statement?.setString(1, languageLink.TAG)
            statement?.setString(2, languageLink.Language)
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Language link added: ${languageLink.TAG} -> ${languageLink.Language}" as Any)
                return true
            } else {
                Logger.warn("No language link entry added for: ${languageLink.TAG} -> ${languageLink.Language}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error adding language link entry: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Adds multiple language link entries to the database using bulk insert.
     * @param languageLinkList ArrayList of LanguageLink objects to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_LanguageLink(languageLinkList: ArrayList<LanguageLink>): Boolean {
        if (languageLinkList.isNotEmpty()) {
            try {
                if (connection != null) {
                    connection!!.autoCommit = false
                    val sql = "INSERT INTO languagelink (TAG, Language) VALUES (?, ?)"
                    val statement = connection!!.prepareStatement(sql)
                    for (ll in languageLinkList) {
                        statement.setString(1, ll.TAG)
                        statement.setString(2, ll.Language)
                        statement.addBatch()
                    }
                    statement.executeBatch()
                    connection!!.commit()
                    Logger.info("Bulk languagelink insert successful: ${languageLinkList.size} entries" as Any)
                    connection!!.autoCommit = true
                    return true
                }
            } catch (e: Exception) {
                Logger.error("Error adding languagelink entries: ${e.message}" as Any)
            }
        }
        return false
    }


    /**
     * Updates an existing language link entry in the database by its index.
     * @param index The index of the language link entry to update.
     * @param languageLink The LanguageLink object with updated values.
     * @return True if the update was successful, false otherwise.
     */
    fun Update_LanguageLink_by_index(index: UInt, languageLink: LanguageLink): Boolean {
        try {
            val statement =
                connection?.prepareStatement("UPDATE languagelink SET TAG = ?, Language = ? WHERE `index` = ?")
            statement?.setString(1, languageLink.TAG)
            statement?.setString(2, languageLink.Language)
            statement?.setLong(3, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Language link updated at index $index: ${languageLink.TAG} -> ${languageLink.Language}" as Any)
                return true
            } else {
                Logger.warn("No language link entry updated at index $index for: ${languageLink.TAG} -> ${languageLink.Language}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error updating language link entry at index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Deletes a language link entry by its index.
     * @param index The index of the language link entry to delete.
     * @return True if the deletion was successful, false otherwise.
     */
    fun Delete_LanguageLink_by_index(index: UInt): Boolean {
        try {
            val statement = connection?.prepareStatement("DELETE FROM languagelink WHERE `index` = ?")
            statement?.setLong(1, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Deleted $rowsAffected row(s) from languagelink with index $index" as Any)
                return true
            } else {
                Logger.warn("No rows deleted from languagelink with index $index" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error deleting from languagelink with index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Clears all entries from the language link table in the database and the local list.
     */
    fun Clear_LanguageLink(): Boolean {
        try {
            val statement = connection?.createStatement()
            // use TRUNCATE to reset auto increment index
            statement?.executeUpdate("TRUNCATE TABLE languagelink")
            Logger.info("LanguageLink table cleared" as Any)
            LanguageLinkList.clear()
            return true
        } catch (e: Exception) {
            Logger.error("Error clearing LanguageLink table: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Exports the languagelink table to an XLSX workbook.
     * @return An XSSFWorkbook containing the languagelink data, or null if an error occurred.
     */
    fun Export_LanguageLink_XLSX(): XSSFWorkbook? {
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM languagelink")
            val workbook = XSSFWorkbook()
            val sheet = workbook.createSheet("LanguageLink")
            val headerRow = sheet.createRow(0)
            val headers = arrayOf("Index", "TAG", "Language")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.createCell(colIndex)
                cell.setCellValue(header)
            }
            var rowIndex = 1
            while (resultSet?.next() == true) {
                val row = sheet.createRow(rowIndex++)
                row.createCell(0).setCellValue(resultSet.getString("index"))
                row.createCell(1).setCellValue(resultSet.getString("TAG"))
                row.createCell(2).setCellValue(resultSet.getString("Language"))
            }
            for (i in headers.indices) {
                sheet.autoSizeColumn(i)
            }
            return workbook
        } catch (e: Exception) {
            Logger.error { "Error exporting LanguageLink, Msg: ${e.message}" }
        }
        return null
    }

    /**
     * Imports languagelink entries from an XLSX workbook.
     * @param workbook The XSSFWorkbook containing the languagelink data.
     * @return True if the import was successful, false otherwise.
     */
    fun Import_LanguageLink_XLSX(workbook: XSSFWorkbook): Boolean {
        try {
            val sheet = workbook.getSheet("LanguageLink") ?: throw Exception("No sheet named 'LanguageLink' found")
            val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
            val headers = arrayOf("Index", "TAG", "Language")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found")
                if (cell.stringCellValue != header) throw Exception("Header '$header' not found")
            }
            // clear existing languagelink
            Clear_LanguageLink()
            // read each row and insert into database
            val _languageLinkList = ArrayList<LanguageLink>()
            for (rowIndex in 1..sheet.lastRowNum) {
                val row = sheet.getRow(rowIndex) ?: continue
                val tag = row.getCell(1)?.stringCellValue ?: continue
                val language = row.getCell(2)?.stringCellValue ?: continue
                val languageLink = LanguageLink(0u, tag, language)
                _languageLinkList.add(languageLink)
            }
            return Add_LanguageLink(_languageLinkList)
        } catch (e: Exception) {
            Logger.error { "Error importing LanguageLink, Msg: ${e.message}" }
        }
        return false
    }

    /**
     * Reloads the soundbank list from the database.
     */
    fun Reload_Soundbank() {

        SoundbankList.clear()
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM soundbank")
            while (resultSet?.next() == true) {
                val soundbank = Soundbank(
                    resultSet.getLong("index").toUInt(),
                    resultSet.getString("Description"),
                    resultSet.getString("TAG"),
                    resultSet.getString("Category"),
                    resultSet.getString("Language"),
                    resultSet.getString("VoiceType"),
                    resultSet.getString("Path")
                )
                SoundbankList.add(soundbank)
            }
        } catch (e: Exception) {
            Logger.error("Error fetching soundbanks: ${e.message}" as Any)
        }
    }

    /**
     * Adds a new soundbank entry to the database.
     * @param soundbank The Soundbank object to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_Soundbank(soundbank: Soundbank): Boolean {
        try {
            val statement =
                connection?.prepareStatement("INSERT INTO soundbank (Description, TAG, Category, Language, VoiceType, Path) VALUES (?, ?, ?, ?, ?, ?)")
            statement?.setString(1, soundbank.Description)
            statement?.setString(2, soundbank.TAG)
            statement?.setString(3, soundbank.Category)
            statement?.setString(4, soundbank.Language)
            statement?.setString(5, soundbank.VoiceType)
            statement?.setString(6, soundbank.Path)
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Soundbank added: ${soundbank.Description}" as Any)
                return true
            } else {
                Logger.warn("No soundbank entry added for: ${soundbank.Description}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error adding soundbank entry: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Adds multiple soundbank entries to the database using bulk insert.
     * @param soundbank Vararg of Soundbank objects to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_Soundbank(soundbank: ArrayList<Soundbank>): Boolean {
        if (soundbank.isNotEmpty()) {
            // use mysql bulk insert
            try {
                if (connection != null) {
                    connection!!.autoCommit = false
                    val sql =
                        "INSERT INTO soundbank (Description, TAG, Category, Language, VoiceType, Path) VALUES (?, ?, ?, ?, ?, ?)"
                    val statement = connection!!.prepareStatement(sql)
                    for (sb in soundbank) {
                        statement.setString(1, sb.Description)
                        statement.setString(2, sb.TAG)
                        statement.setString(3, sb.Category)
                        statement.setString(4, sb.Language)
                        statement.setString(5, sb.VoiceType)
                        statement.setString(6, sb.Path)
                        statement.addBatch()
                    }
                    statement.executeBatch()
                    connection!!.commit()
                    Logger.info("Bulk soundbank insert successful: ${soundbank.size} entries" as Any)
                    connection!!.autoCommit = true
                    return true
                }
            } catch (e: Exception) {
                Logger.error("Error adding soundbank entries: ${e.message}" as Any)
            }
        }
        return false
    }

    /**
     * Updates an existing soundbank entry in the database by its index.
     * @param index The index of the soundbank entry to update.
     * @param soundbank The Soundbank object with updated values.
     * @return True if the update was successful, false otherwise.
     */
    fun Update_Soundbank_by_index(index: UInt, soundbank: Soundbank): Boolean {
        try {
            val statement =
                connection?.prepareStatement("UPDATE soundbank SET Description = ?, TAG = ?, Category = ?, Language = ?, VoiceType = ?, Path = ? WHERE `index` = ?")
            statement?.setString(1, soundbank.Description)
            statement?.setString(2, soundbank.TAG)
            statement?.setString(3, soundbank.Category)
            statement?.setString(4, soundbank.Language)
            statement?.setString(5, soundbank.VoiceType)
            statement?.setString(6, soundbank.Path)
            statement?.setLong(7, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Soundbank updated at index $index: ${soundbank.Description}" as Any)
                return true
            } else {
                Logger.warn("No soundbank entry updated at index $index for: ${soundbank.Description}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error updating soundbank entry at index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Retrieves a soundbank entry by its index.
     * @param index The index of the soundbank entry to retrieve.
     * @return The Soundbank object if found, null otherwise.
     */
    fun Get_Soundbank_by_index(index: UInt): Soundbank? {
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM soundbank WHERE `index` = $index")
            if (resultSet?.next() == true) {
                return Soundbank(
                    resultSet.getLong("index").toUInt(),
                    resultSet.getString("Description"),
                    resultSet.getString("TAG"),
                    resultSet.getString("Category"),
                    resultSet.getString("Language"),
                    resultSet.getString("VoiceType"),
                    resultSet.getString("Path")
                )
            }
        } catch (_: Exception) {
            Logger.error("Error finding soundbank with index $index" as Any)
        }
        return null
    }

    /**
     * Deletes a soundbank entry by its index.
     * @param index The index of the soundbank entry to delete.
     * @return True if the deletion was successful, false otherwise.
     */
    fun Delete_Soundbank_by_index(index: UInt): Boolean {
        try {
            val statement = connection?.prepareStatement("DELETE FROM soundbank WHERE `index` = ?")
            statement?.setLong(1, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Deleted $rowsAffected row(s) from soundbank with index $index" as Any)
                return true
            } else {
                Logger.warn("No rows deleted from soundbank with index $index" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error deleting from soundbank with index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Clears all entries from the soundbank table in the database and the local list.
     */
    fun Clear_Soundbank(): Boolean {
        try {
            val statement = connection?.createStatement()
            // use TRUNCATE to reset auto increment index
            statement?.executeUpdate("TRUNCATE TABLE soundbank")
            Logger.info("Soundbank table cleared" as Any)
            SoundbankList.clear()
            return true
        } catch (e: Exception) {
            Logger.error("Error clearing soundbank table: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Exports the soundbank table to an XLSX workbook.
     * @return An XSSFWorkbook containing the soundbank data, or null if an error occurred.
     */
    fun Export_Soundbank_XLSX(): XSSFWorkbook? {
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM soundbank")
            val workbook = XSSFWorkbook()
            val sheet = workbook.createSheet("Soundbank")
            val headerRow = sheet.createRow(0)
            val headers = arrayOf("Index", "Description", "TAG", "Category", "Language", "VoiceType", "Path")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.createCell(colIndex)
                cell.setCellValue(header)
            }
            var rowIndex = 1
            while (resultSet?.next() == true) {
                val row = sheet.createRow(rowIndex++)
                row.createCell(0).setCellValue(resultSet.getString("index"))
                row.createCell(1).setCellValue(resultSet.getString("Description"))
                row.createCell(2).setCellValue(resultSet.getString("TAG"))
                row.createCell(3).setCellValue(resultSet.getString("Category"))
                row.createCell(4).setCellValue(resultSet.getString("Language"))
                row.createCell(5).setCellValue(resultSet.getString("VoiceType"))
                row.createCell(6).setCellValue(resultSet.getString("Path"))
            }
            for (i in 0 until headers.size) {
                sheet.autoSizeColumn(i)
            }
            return workbook
        } catch (e: Exception) {
            Logger.error { "Error exporting Soundbank, Msg: ${e.message}" }
        }
        return null
    }

    fun Import_Soundbank_XLSX(workbook: XSSFWorkbook): Boolean {
        try {
            // check if there is sheet named "Soundbank"
            val sheet = workbook.getSheet("Soundbank") ?: throw Exception("No sheet named 'Soundbank' found")
            // check if the sheet contains header named "index", "Description", "TAG", "Category", "Language", "VoiceType", "Path"
            val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
            val headers = arrayOf("Index", "Description", "TAG", "Category", "Language", "VoiceType", "Path")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found")
                if (cell.stringCellValue != header) throw Exception("Header '$header' not found")
            }
            // clear existing soundbank
            Clear_Soundbank()
            // read each row and insert into database
            val _soundbankList = ArrayList<Soundbank>()
            for (rowIndex in 1..sheet.lastRowNum) {
                val row = sheet.getRow(rowIndex) ?: continue
                val description = row.getCell(1)?.stringCellValue ?: continue
                val tag = row.getCell(2)?.stringCellValue ?: continue
                val category = row.getCell(3)?.stringCellValue ?: continue
                val language = row.getCell(4)?.stringCellValue ?: continue
                val voiceType = row.getCell(5)?.stringCellValue ?: continue
                val path = row.getCell(6)?.stringCellValue ?: continue
                val soundbank = Soundbank(0u, description, tag, category, language, voiceType, path)
                _soundbankList.add(soundbank)
            }
            return Add_Soundbank(_soundbankList)
        } catch (e: Exception) {
            Logger.error { "Error importing Soundbank, Msg: ${e.message}" }
        }
        return false
    }

    /**
     * Reloads the messagebank list from the database.
     */
    fun Reload_Messagebank() {
        MessagebankList.clear()
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM messagebank")
            while (resultSet?.next() == true) {
                val messagebank = Messagebank(
                    resultSet.getLong("index").toUInt(),
                    resultSet.getString("Description"),
                    resultSet.getString("Language"),
                    resultSet.getInt("ANN_ID").toUInt(),
                    resultSet.getString("Voice_Type"),
                    resultSet.getString("Message_Detail"),
                    resultSet.getString("Message_TAGS")
                )

                MessagebankList.add(messagebank)
            }
        } catch (e: Exception) {
            Logger.error("Error fetching messagebanks: ${e.message}" as Any)
        }
    }

    /**
     * Adds a new messagebank entry to the database.
     * @param messagebank The Messagebank object to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_Messagebank(messagebank: Messagebank): Boolean {
        try {
            val statement =
                connection?.prepareStatement("INSERT INTO messagebank (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)")
            statement?.setString(1, messagebank.Description)
            statement?.setString(2, messagebank.Language)
            statement?.setInt(3, messagebank.ANN_ID.toInt())
            statement?.setString(4, messagebank.Voice_Type)
            statement?.setString(5, messagebank.Message_Detail)
            statement?.setString(6, messagebank.Message_TAGS)
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Messagebank added: ${messagebank.Description}" as Any)
                return true
            } else {
                Logger.warn("No messagebank entry added for: ${messagebank.Description}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error adding messagebank entry: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Adds multiple messagebank entries to the database using bulk insert.
     * @param messagebankList ArrayList of Messagebank objects to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_Messagebank(messagebankList: ArrayList<Messagebank>): Boolean {
        if (messagebankList.isNotEmpty()) {
            try {
                if (connection != null) {
                    connection!!.autoCommit = false
                    val sql =
                        "INSERT INTO messagebank (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)"
                    val statement = connection!!.prepareStatement(sql)
                    for (mb in messagebankList) {
                        statement.setString(1, mb.Description)
                        statement.setString(2, mb.Language)
                        statement.setInt(3, mb.ANN_ID.toInt())
                        statement.setString(4, mb.Voice_Type)
                        statement.setString(5, mb.Message_Detail)
                        statement.setString(6, mb.Message_TAGS)
                        statement.addBatch()
                    }
                    statement.executeBatch()
                    connection!!.commit()
                    Logger.info("Bulk messagebank insert successful: ${messagebankList.size} entries" as Any)
                    connection!!.autoCommit = true
                    return true
                }
            } catch (e: Exception) {
                Logger.error("Error adding messagebank entries: ${e.message}" as Any)
            }
        }
        return false
    }

    /**
     * Updates an existing messagebank entry in the database by its index.
     * @param index The index of the messagebank entry to update.
     * @param messagebank The Messagebank object with updated values.
     * @return True if the update was successful, false otherwise.
     */
    fun Update_Messagebank_by_index(index: UInt, messagebank: Messagebank): Boolean {
        try {
            val statement =
                connection?.prepareStatement("UPDATE messagebank SET Description = ?, Language = ?, ANN_ID = ?, Voice_Type = ?, Message_Detail = ?, Message_TAGS = ? WHERE `index` = ?")
            statement?.setString(1, messagebank.Description)
            statement?.setString(2, messagebank.Language)
            statement?.setInt(3, messagebank.ANN_ID.toInt())
            statement?.setString(4, messagebank.Voice_Type)
            statement?.setString(5, messagebank.Message_Detail)
            statement?.setString(6, messagebank.Message_TAGS)
            statement?.setLong(7, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Messagebank updated at index $index: ${messagebank.Description}" as Any)
                return true
            } else {
                Logger.warn("No messagebank entry updated at index $index for: ${messagebank.Description}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error updating messagebank entry at index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Deletes a messagebank entry by its index.
     * @param index The index of the messagebank entry to delete.
     * @return True if the deletion was successful, false otherwise.
     */
    fun Delete_Messagebank_by_index(index: UInt): Boolean {
        try {
            val statement = connection?.prepareStatement("DELETE FROM messagebank WHERE `index` = ?")
            statement?.setLong(1, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Deleted $rowsAffected row(s) from messagebank with index $index" as Any)
                return true
            } else {
                Logger.warn("No rows deleted from messagebank with index $index" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error deleting from messagebank with index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Exports the messagebank table to an XLSX workbook.
     * @return An XSSFWorkbook containing the messagebank data.
     */
    fun Export_Messagebank_XLSX(): XSSFWorkbook? {
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM messagebank")
            val workbook = XSSFWorkbook()
            val sheet = workbook.createSheet("Messagebank")
            val headerRow = sheet.createRow(0)
            val headers =
                arrayOf("Index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.createCell(colIndex)
                cell.setCellValue(header)
            }
            var rowIndex = 1
            while (resultSet?.next() == true) {
                val row = sheet.createRow(rowIndex++)
                row.createCell(0).setCellValue(resultSet.getString("index"))
                row.createCell(1).setCellValue(resultSet.getString("Description"))
                row.createCell(2).setCellValue(resultSet.getString("Language"))
                row.createCell(3).setCellValue(resultSet.getString("ANN_ID"))
                row.createCell(4).setCellValue(resultSet.getString("Voice_Type"))
                row.createCell(5).setCellValue(resultSet.getString("Message_Detail"))
                row.createCell(6).setCellValue(resultSet.getString("Message_TAGS"))
            }
            for (i in headers.indices) {
                sheet.autoSizeColumn(i)
            }
            return workbook
        } catch (e: Exception) {
            Logger.error { "Error exporting Messagebank, Msg: ${e.message}" }
        }
        return null
    }

    fun Import_Messagebank_XLSX(workbook: XSSFWorkbook): Boolean {
        try {
            // check if there is sheet named "Messagebank"
            val sheet = workbook.getSheet("Messagebank") ?: throw Exception("No sheet named 'Messagebank' found")
            // check if the sheet contains header named "Index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS"
            val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
            val headers =
                arrayOf("Index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found")
                if (cell.stringCellValue != header) throw Exception("Header '$header' not found")
            }
            // clear existing messagebank
            Clear_Messagebank()
            // read each row and insert into database
            val _messagebankList = ArrayList<Messagebank>()
            for (rowIndex in 1..sheet.lastRowNum) {
                val row = sheet.getRow(rowIndex) ?: continue
                val description = row.getCell(1)?.stringCellValue ?: continue
                val language = row.getCell(2)?.stringCellValue ?: continue
                val annId = row.getCell(3)?.stringCellValue?.toUIntOrNull() ?: continue
                val voiceType = row.getCell(4)?.stringCellValue ?: continue
                val messageDetail = row.getCell(5)?.stringCellValue ?: continue
                val messageTags = row.getCell(6)?.stringCellValue ?: continue
                val messagebank = Messagebank(0u, description, language, annId, voiceType, messageDetail, messageTags)
                _messagebankList.add(messagebank)
            }
            return Add_Messagebank(_messagebankList)
        } catch (e: Exception) {
            Logger.error { "Error importing Messagebank, Msg: ${e.message}" }
        }
        return false
    }


    /**
     * Clears all entries from the messagebank table in the database and the local list.
     */
    fun Clear_Messagebank(): Boolean {
        try {
            val statement = connection?.createStatement()
            // use TRUNCATE to reset auto increment index
            statement?.executeUpdate("TRUNCATE TABLE messagebank")
            Logger.info("Messagebank table cleared" as Any)
            MessagebankList.clear()
            return true
        } catch (e: Exception) {
            Logger.error("Error clearing messagebank table: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Reads all entries from the queue_table in the database.
     * @return A list of QueueTable entries.
     */
    fun Read_Queue_Table(): ArrayList<QueueTable> {
        val queueList = ArrayList<QueueTable>()
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM queue_table")
            while (resultSet?.next() == true) {
                val queueTable = QueueTable(
                    resultSet.getLong("index").toUInt(),
                    resultSet.getString("Date_Time"),
                    resultSet.getString("Source"),
                    resultSet.getString("Type"),
                    resultSet.getString("Message"),
                    resultSet.getString("SB_TAGS"),
                    resultSet.getString("BroadcastZones"),
                    resultSet.getInt("Repeat").toUInt(),
                    resultSet.getString("Language")
                )
                queueList.add(queueTable)
            }
        } catch (e: Exception) {
            Logger.error("Error fetching queue table: ${e.message}" as Any)
        }
        return queueList
    }

    /**
     * Deletes a queue_table entry by its index.
     * @param index The index of the queue_table entry to delete.
     * @return True if the deletion was successful, false otherwise.
     */
    fun Delete_Queue_Table_by_index(index: UInt): Boolean {
        try {
            val statement = connection?.prepareStatement("DELETE FROM queue_table WHERE `index` = ?")
            statement?.setLong(1, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Deleted $rowsAffected row(s) from queue_table with index $index" as Any)
                return true
            } else {
                Logger.warn("No rows deleted from queue_table with index $index" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error deleting from queue_table with index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Clears all entries from the queue_table in the database.
     */
    fun Clear_Queue_Table(): Boolean {
        try {
            val statement = connection?.createStatement()
            // use TRUNCATE to reset auto increment index
            statement?.executeUpdate("TRUNCATE TABLE queue_table")
            Logger.info("Queue table cleared" as Any)
            return true
        } catch (e: Exception) {
            Logger.error("Error clearing queue table: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Reads all entries from the queue_paging in the database.
     * @return A list of QueuePaging entries.
     */
    fun Read_Queue_Paging(): ArrayList<QueuePaging> {
        val queueList = ArrayList<QueuePaging>()
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM queue_paging")
            while (resultSet?.next() == true) {
                val queuePaging = QueuePaging(
                    resultSet.getLong("index").toUInt(),
                    resultSet.getString("Date_Time"),
                    resultSet.getString("Source"),
                    resultSet.getString("Type"),
                    resultSet.getString("Message"),
                    resultSet.getString("SB_TAGS"),
                )
                queueList.add(queuePaging)
            }
        } catch (e: Exception) {
            Logger.error("Error fetching queue paging: ${e.message}" as Any)
        }
        return queueList
    }

    /**
     * Deletes a queue_paging entry by its index.
     * @param index The index of the queue_paging entry to delete.
     * @return True if the deletion was successful, false otherwise.
     */
    fun Delete_Queue_Paging_by_index(index: UInt): Boolean {
        try {
            val statement = connection?.prepareStatement("DELETE FROM queue_paging WHERE `index` = ?")
            statement?.setLong(1, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Deleted $rowsAffected row(s) from queue_paging with index $index" as Any)
                return true
            } else {
                Logger.warn("No rows deleted from queue_paging with index $index" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error deleting from queue_paging with index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Clears all entries from the queue_paging in the database.
     */
    fun Clear_Queue_Paging(): Boolean {
        try {
            val statement = connection?.createStatement()
            // use TRUNCATE to reset auto increment index
            statement?.executeUpdate("TRUNCATE TABLE queue_paging")
            Logger.info("Queue paging table cleared" as Any)
            return true
        } catch (e: Exception) {
            Logger.error("Error clearing queue paging table: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Get All Broadcast Zones from database
     * @return A list of BroadcastZones entries.
     */
    fun GetBroadcastZones(): List<BroadcastZones> {
        val zonesList = ArrayList<BroadcastZones>()
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM broadcast_zones")
            while (resultSet?.next() == true) {
                val zone = BroadcastZones(
                    resultSet.getLong("index").toUInt(),
                    resultSet.getString("description"),
                    resultSet.getString("SoundChannel"),
                    resultSet.getString("Box"),
                    resultSet.getString("Relay")
                )
                zonesList.add(zone)
            }
        } catch (e: Exception) {
            Logger.error("Error fetching broadcast zones: ${e.message}" as Any)
        }
        return zonesList
    }

    /**
     * Adds a new broadcast_zones entry to the database.
     * @param broadcastZones The BroadcastZones object to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_BroadcastZones(broadcastZones: BroadcastZones): Boolean {
        try {
            val statement =
                connection?.prepareStatement("INSERT INTO broadcast_zones (description, SoundChannel, Box, Relay) VALUES (?, ?, ?, ?)")
            statement?.setString(1, broadcastZones.description)
            statement?.setString(2, broadcastZones.SoundChannel)
            statement?.setString(3, broadcastZones.Box)
            statement?.setString(4, broadcastZones.Relay)
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Broadcast zone added: ${broadcastZones.description}" as Any)
                return true
            } else {
                Logger.warn("No broadcast zone entry added for: ${broadcastZones.description}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error adding broadcast zone entry: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Adds multiple broadcast_zones entries to the database using bulk insert.
     * @param broadcastZonesList ArrayList of BroadcastZones objects to add.
     * @return True if the addition was successful, false otherwise.
     */
    fun Add_BroadcastZones(broadcastZonesList: ArrayList<BroadcastZones>): Boolean {
        if (broadcastZonesList.isNotEmpty()) {
            try {
                if (connection != null) {
                    connection!!.autoCommit = false
                    val sql =
                        "INSERT INTO broadcast_zones (description, SoundChannel, Box, Relay) VALUES (?, ?, ?, ?)"
                    val statement = connection!!.prepareStatement(sql)
                    for (bz in broadcastZonesList) {
                        statement.setString(1, bz.description)
                        statement.setString(2, bz.SoundChannel)
                        statement.setString(3, bz.Box)
                        statement.setString(4, bz.Relay)
                        statement.addBatch()
                    }
                    statement.executeBatch()
                    connection!!.commit()
                    Logger.info("Bulk broadcast_zones insert successful: ${broadcastZonesList.size} entries" as Any)
                    connection!!.autoCommit = true
                    return true
                }
            } catch (e: Exception) {
                Logger.error("Error adding broadcast_zones entries: ${e.message}" as Any)
            }
        }
        return false
    }

    /**
     * Updates an existing broadcast_zones entry in the database by its index.
     * @param index The index of the broadcast_zones entry to update.
     * @param broadcastZones The BroadcastZones object with updated values.
     * @return True if the update was successful, false otherwise.
     */
    fun Update_BroadcastZones_by_index(index: UInt, broadcastZones: BroadcastZones): Boolean {
        try {
            val statement =
                connection?.prepareStatement("UPDATE broadcast_zones SET description = ?, SoundChannel = ?, Box = ?, Relay = ? WHERE `index` = ?")
            statement?.setString(1, broadcastZones.description)
            statement?.setString(2, broadcastZones.SoundChannel)
            statement?.setString(3, broadcastZones.Box)
            statement?.setString(4, broadcastZones.Relay)
            statement?.setLong(5, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Broadcast zone updated at index $index: ${broadcastZones.description}" as Any)
                return true
            } else {
                Logger.warn("No broadcast zone entry updated at index $index for: ${broadcastZones.description}" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error updating broadcast zone entry at index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Deletes a broadcast_zones entry by its index.
     * @param index The index of the broadcast_zones entry to delete.
     * @return True if the deletion was successful, false otherwise.
     */
    fun Delete_BroadcastZones_by_index(index: UInt): Boolean {
        try {
            val statement = connection?.prepareStatement("DELETE FROM broadcast_zones WHERE `index` = ?")
            statement?.setLong(1, index.toLong())
            val rowsAffected = statement?.executeUpdate()
            if (rowsAffected != null && rowsAffected > 0) {
                Logger.info("Deleted $rowsAffected row(s) from broadcast_zones with index $index" as Any)
                return true
            } else {
                Logger.warn("No rows deleted from broadcast_zones with index $index" as Any)
            }
        } catch (e: Exception) {
            Logger.error("Error deleting from broadcast_zones with index $index: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Clears all entries from the broadcast_zones in the database.
     */
    fun Clear_BroadcastZones(): Boolean {
        try {
            val statement = connection?.createStatement()
            // use TRUNCATE to reset auto increment index
            statement?.executeUpdate("TRUNCATE TABLE broadcast_zones")
            Logger.info("Broadcast zones table cleared" as Any)
            return true
        } catch (e: Exception) {
            Logger.error("Error clearing broadcast zones table: ${e.message}" as Any)
        }
        return false
    }

    /**
     * Exports the broadcast_zones table to an XLSX workbook.
     * @return An XSSFWorkbook containing the broadcast_zones data, or null if an error occurred.
     */
    fun Export_BroadcastZones_XLSX(): XSSFWorkbook? {
        try {
            val statement = connection?.createStatement()
            val resultSet = statement?.executeQuery("SELECT * FROM broadcast_zones")
            val workbook = XSSFWorkbook()
            val sheet = workbook.createSheet("BroadcastZones")
            val headerRow = sheet.createRow(0)
            val headers = arrayOf("Index", "description", "SoundChannel", "Box", "Relay")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.createCell(colIndex)
                cell.setCellValue(header)
            }
            var rowIndex = 1
            while (resultSet?.next() == true) {
                val row = sheet.createRow(rowIndex++)
                row.createCell(0).setCellValue(resultSet.getString("index"))
                row.createCell(1).setCellValue(resultSet.getString("description"))
                row.createCell(2).setCellValue(resultSet.getString("SoundChannel"))
                row.createCell(3).setCellValue(resultSet.getString("Box"))
                row.createCell(4).setCellValue(resultSet.getString("Relay"))
            }
            for (i in headers.indices) {
                sheet.autoSizeColumn(i)
            }
            return workbook
        } catch (e: Exception) {
            Logger.error { "Error exporting BroadcastZones, Msg: ${e.message}" }
        }
        return null
    }

    /**
     * Imports broadcast_zones entries from an XLSX workbook.
     * @param workbook The XSSFWorkbook containing the broadcast_zones data.
     * @return True if the import was successful, false otherwise.
     */
    fun Import_BroadcastZones_XLSX(workbook: XSSFWorkbook): Boolean {
        try {
            val sheet = workbook.getSheet("BroadcastZones") ?: throw Exception("No sheet named 'BroadcastZones' found")
            val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
            val headers = arrayOf("Index", "description", "SoundChannel", "Box", "Relay")
            for ((colIndex, header) in headers.withIndex()) {
                val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found")
                if (cell.stringCellValue != header) throw Exception("Header '$header' not found")
            }
            // clear existing broadcast_zones
            Clear_BroadcastZones()
            // read each row and insert into database
            val _broadcastZonesList = ArrayList<BroadcastZones>()
            for (rowIndex in 1..sheet.lastRowNum) {
                val row = sheet.getRow(rowIndex) ?: continue
                val description = row.getCell(1)?.stringCellValue ?: continue
                val soundChannel = row.getCell(2)?.stringCellValue ?: continue
                val box = row.getCell(3)?.stringCellValue ?: continue
                val relay = row.getCell(4)?.stringCellValue ?: continue
                val broadcastZone = BroadcastZones(0u, description, soundChannel, box, relay)
                _broadcastZonesList.add(broadcastZone)
            }
            return Add_BroadcastZones(_broadcastZonesList)
        } catch (e: Exception) {
            Logger.error { "Error importing BroadcastZones, Msg: ${e.message}" }
        }
        return false
    }

}