package database

import codes.Somecodes.Companion.ValiDateForLogHtml
import codes.Somecodes.Companion.toJsonString
import content.Category
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import max_channel
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.tinylog.Logger
import java.sql.Connection
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", "SqlSourceToSinkFlow")
class MariaDB(
    address: String = "localhost",
    port: Int = 3306,
    dbName: String = "aas",
    username: String = "admin",
    password: String = "admin"
) {
    var connected: Boolean = false


    lateinit var connection: Connection
    lateinit var soundDB: dbFunctions<Soundbank>
    lateinit var messageDB: dbFunctions<Messagebank>
    lateinit var languageDB: dbFunctions<LanguageLink>
    lateinit var scheduleDB: dbFunctions<ScheduleBank>
    lateinit var broadcastDB: dbFunctions<BroadcastZones>
    lateinit var queuetableDB: dbFunctions<QueueTable>
    lateinit var queuepagingDB: dbFunctions<QueuePaging>
    lateinit var soundchannelDB: dbFunctions<SoundChannel>
    lateinit var logDB: dbFunctions<Log>
    lateinit var userDB: dbFunctions<UserDB>

    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)
        }


        /**
         * 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 {
                toJsonString(list)
            } catch (e: Exception) {
                Logger.error("Error converting list to JSON: ${e.message}" as Any)
                "[]"
            }

        }
    }

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

            soundDB = object : dbFunctions<Soundbank>("soundbank", connection) {
                override fun Create() {
                    val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "Description VARCHAR(1024) NOT NULL," +  // Description of the soundbank
                            "TAG VARCHAR(45) NOT NULL," +            // TAG of the soundbank
                            "Category VARCHAR(45) NOT NULL," +       // Category of the soundbank
                            "Language VARCHAR(45) NOT NULL," +       // Language of the soundbank
                            "VoiceType VARCHAR(45) NOT NULL," +      // VoiceType of the soundbank
                            "Path VARCHAR(1024) NOT NULL" +           // Path to the sound file
                            ")"
                    super.Create(tabledefinition)
                }

                override fun Get() {
                    List.clear()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG")
                        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")
                            )
                            List.add(soundbank)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error fetching soundbanks: ${e.message}" as Any)
                    }
                }

                fun Get_By_Index(index: Int): Soundbank? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} 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 ${super.dbName} with index $index" as Any)
                    }
                    return null
                }

                override fun Add(data: Soundbank): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) VALUES (?, ?, ?, ?, ?, ?)")
                        statement?.setString(1, data.Description)
                        statement?.setString(2, data.TAG)
                        statement?.setString(3, data.Category)
                        statement?.setString(4, data.Language)
                        statement?.setString(5, data.VoiceType)
                        statement?.setString(6, data.Path)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Soundbank added: ${data.Description}" as Any)
                            return true
                        } else {
                            Logger.warn("No soundbank entry added for: ${data.Description}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error adding soundbank entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<Soundbank>): Boolean {
                    // use mysql bulk insert
                    try {
                        connection.autoCommit = false
                        val sql =
                            "INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) VALUES (?, ?, ?, ?, ?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (sb in data) {
                            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: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error adding soundbank entries: ${e.message}" as Any)
                    }
                    return false
                }

                override fun UpdateByIndex(index: Int, data: Soundbank): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, TAG = ?, Category = ?, Language = ?, VoiceType = ?, Path = ? WHERE `index` = ?")
                        statement?.setString(1, data.Description)
                        statement?.setString(2, data.TAG)
                        statement?.setString(3, data.Category)
                        statement?.setString(4, data.Language)
                        statement?.setString(5, data.VoiceType)
                        statement?.setString(6, data.Path)
                        statement?.setLong(7, index.toLong())
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Soundbank updated at index $index: ${data.Description}" as Any)
                            return true
                        } else {
                            Logger.warn("No soundbank entry updated at index $index for: ${data.Description}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error updating soundbank entry at index $index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"

                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG ")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by Description" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by Description: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_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()
                        // 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 AddAll(_soundbankList)
                    } catch (e: Exception) {
                        Logger.error { "Error importing Soundbank, Msg: ${e.message}" }
                    }
                    return false
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        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
                }
            }

            messageDB = object : dbFunctions<Messagebank>("messagebank", connection) {
                override fun Create() {
                    val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "Description VARCHAR(512) NOT NULL," +  // Description of the message
                            "Language VARCHAR(45) NOT NULL," +       // Language of the message
                            "ANN_ID INT NOT NULL," +                 // ANN ID of the message
                            "Voice_Type VARCHAR(45) NOT NULL," +     // Voice type of the message
                            "Message_Detail VARCHAR(1024) NOT NULL," +        // Full message text
                            "Message_TAGS VARCHAR(1024)" +                     // Comma-separated tags for the message
                            ")"
                    super.Create(tabledefinition)
                }

                override fun Get() {
                    List.clear()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        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")
                            )

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

                override fun Add(data: Messagebank): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)")
                        statement?.setString(1, data.Description)
                        statement?.setString(2, data.Language)
                        statement?.setInt(3, data.ANN_ID.toInt())
                        statement?.setString(4, data.Voice_Type)
                        statement?.setString(5, data.Message_Detail)
                        statement?.setString(6, data.Message_TAGS)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Messagebank added: ${data.Description}" as Any)
                            return true
                        } else {
                            Logger.warn("No messagebank entry added for: ${data.Description}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error adding messagebank entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<Messagebank>): Boolean {
                    try {
                        connection.autoCommit = false
                        val sql =
                            "INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (mb in data) {
                            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: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error adding messagebank entries: ${e.message}" as Any)
                    }
                    return false
                }

                override fun UpdateByIndex(index: Int, data: Messagebank): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, Language = ?, ANN_ID = ?, Voice_Type = ?, Message_Detail = ?, Message_TAGS = ? WHERE `index` = ?")
                        statement?.setString(1, data.Description)
                        statement?.setString(2, data.Language)
                        statement?.setInt(3, data.ANN_ID.toInt())
                        statement?.setString(4, data.Voice_Type)
                        statement?.setString(5, data.Message_Detail)
                        statement?.setString(6, data.Message_TAGS)
                        statement?.setLong(7, index.toLong())
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Messagebank updated at index $index: ${data.Description}" as Any)
                            return true
                        } else {
                            Logger.warn("No messagebank entry updated at index $index for: ${data.Description}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error updating messagebank entry at index $index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"
                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM ${super.dbName} ORDER BY ANN_ID ")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by Description" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by Description: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_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()
                        // 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 AddAll(_messagebankList)
                    } catch (e: Exception) {
                        Logger.error { "Error importing Messagebank, Msg: ${e.message}" }
                    }
                    return false
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        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
                }

            }

            languageDB = object : dbFunctions<LanguageLink>("languagelinking", connection) {
                override fun Create() {
                    val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "TAG VARCHAR(45) NOT NULL," +        // Language tag (e.g., EN, FR)
                            "Language VARCHAR(128) NOT NULL" +   // Full language name (e.g., English, French)
                            ")"

                    super.Create(tabledefinition)
                }

                override fun Get() {
                    List.clear()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        while (resultSet?.next() == true) {
                            val languageLink = LanguageLink(
                                resultSet.getLong("index").toUInt(),
                                resultSet.getString("TAG"),
                                resultSet.getString("Language")
                            )
                            List.add(languageLink)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any)
                    }
                }

                override fun Add(data: LanguageLink): Boolean {
                    try {
                        val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (TAG, Language) VALUES (?, ?)")
                        statement.setString(1, data.TAG)
                        statement.setString(2, data.Language)
                        val rowsAffected = statement.executeUpdate()
                        if (rowsAffected > 0) {
                            Logger.info("Language link added: ${data.TAG} -> ${data.Language}" as Any)
                            return true
                        } else {
                            Logger.warn("No language link entry added for: ${data.TAG} -> ${data.Language}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error adding language link entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<LanguageLink>): Boolean {
                    try {
                        connection.autoCommit = false
                        val sql = "INSERT INTO ${super.dbName} (TAG, Language) VALUES (?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (ll in List) {
                            statement.setString(1, ll.TAG)
                            statement.setString(2, ll.Language)
                            statement.addBatch()
                        }
                        statement.executeBatch()
                        connection.commit()
                        Logger.info("Bulk languagelinking insert successful: ${List.size} entries" as Any)
                        connection.autoCommit = true
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error adding languagelinking entries: ${e.message}" as Any)
                    }
                    return false
                }

                override fun UpdateByIndex(index: Int, data: LanguageLink): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("UPDATE ${super.dbName} SET TAG = ?, Language = ? WHERE `index` = ?")
                        statement?.setString(1, data.TAG)
                        statement?.setString(2, data.Language)
                        statement?.setLong(3, index.toLong())
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Language link updated at index $index: ${data.TAG} -> ${data.Language}" as Any)
                            return true
                        } else {
                            Logger.warn("No language link entry updated at index $index for: ${data.TAG} -> ${data.Language}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error updating language link entry at index $index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"
                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (TAG, Language) SELECT TAG, Language FROM ${super.dbName} ORDER BY TAG")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (TAG, Language) SELECT TAG, Language FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by TAG" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by TAG: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_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()
                        // 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 AddAll(_languageLinkList)
                    } catch (e: Exception) {
                        Logger.error { "Error importing LanguageLink, Msg: ${e.message}" }
                    }
                    return false
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        val workbook = XSSFWorkbook()
                        val sheet = workbook.createSheet("languagelinking")
                        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 languagelinking, Msg: ${e.message}" }
                    }
                    return null
                }

            }

            scheduleDB = object : dbFunctions<ScheduleBank>("schedulebank", connection) {
                override fun Create() {
                    val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "Description VARCHAR(128) NOT NULL," +  // Description of the schedule
                            "Day VARCHAR(255) NOT NULL," +            // Day in format DD/MM/YYYY
                            "Time VARCHAR(20) NOT NULL," +            // Time in format HH:MM:SS
                            "Soundpath VARCHAR(512) NOT NULL," +     // Path to the sound file
                            "`Repeat` TINYINT UNSIGNED NOT NULL," +    // Repeat type (0=Once, 1=Daily, 2=Weekly, 3=Monthly, 4=Yearly)
                            "Enable BOOLEAN NOT NULL," +             // Enable or disable the schedule
                            "BroadcastZones TEXT NOT NULL," +        // Comma-separated list of broadcast zones
                            "Language VARCHAR(45) NOT NULL" +        // Language code (e.g., EN, FR)
                            ")"
                    super.Create(tabledefinition)
                }

                override fun Get() {
                    List.clear()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        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")
                            )
                            List.add(schedulebank)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)
                    }
                }

                override fun Add(data: ScheduleBank): Boolean {
                    if (!ValidDate(data.Day)) {
                        Logger.error("Error adding schedulebank entry: Invalid date format ${data.Day}" as Any)
                        return false
                    }
                    if (!ValidTime(data.Time)) {
                        Logger.error("Error adding schedulebank entry: Invalid time format ${data.Time}" as Any)
                        return false
                    }
                    try {
                        val statement =
                            connection.prepareStatement("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
                        statement?.setString(1, data.Description)
                        statement?.setString(2, data.Day)
                        statement?.setString(3, data.Time)
                        statement?.setString(4, data.Soundpath)
                        statement?.setInt(5, data.Repeat.toInt())
                        statement?.setBoolean(6, data.Enable)
                        statement?.setString(7, data.BroadcastZones)
                        statement?.setString(8, data.Language)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Schedulebank added: ${data.Description}" as Any)
                            return true
                        } else {
                            Logger.warn("No schedulebank entry added for: ${data.Description}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error adding schedulebank entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<ScheduleBank>): Boolean {
                    try {
                        connection.autoCommit = false
                        val sql =
                            "INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (sb in data) {
                            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: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error adding schedulebank entries: ${e.message}" as Any)
                    }
                    return false
                }


                override fun UpdateByIndex(index: Int, data: ScheduleBank): Boolean {
                    if (!ValidDate(data.Day)) {
                        Logger.error("Error updating schedulebank entry: Invalid date format ${data.Day}" as Any)
                        return false
                    }
                    if (!ValidTime(data.Time)) {
                        Logger.error("Error updating schedulebank entry: Invalid time format ${data.Time}" as Any)
                        return false
                    }
                    try {
                        val statement =
                            connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, Day = ?, Time = ?, Soundpath = ?, Repeat = ?, Enable = ?, BroadcastZones = ?, Language = ? WHERE `index` = ?")
                        statement?.setString(1, data.Description)
                        statement?.setString(2, data.Day)
                        statement?.setString(3, data.Time)
                        statement?.setString(4, data.Soundpath)
                        statement?.setInt(5, data.Repeat.toInt())
                        statement?.setBoolean(6, data.Enable)
                        statement?.setString(7, data.BroadcastZones)
                        statement?.setString(8, data.Language)
                        statement?.setLong(9, index.toLong())
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Schedulebank updated at index $index: ${data.Description}" as Any)
                            return true
                        } else {
                            Logger.warn("No schedulebank entry updated at index $index for: ${data.Description}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error updating schedulebank entry at index $index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"
                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM ${super.dbName} ORDER BY Day , Time ")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by Day and Time" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by Day and Time: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_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()
                        // 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 scheduleDB.AddAll(_schedulebankList)
                    } catch (e: Exception) {
                        Logger.error { "Error importing Schedulebank, Msg: ${e.message}" }
                    }
                    return false
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        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
                }

            }

            broadcastDB = object : dbFunctions<BroadcastZones>("broadcastzones", connection) {
                override fun Create() {
                    val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "description VARCHAR(512) NOT NULL," +  // Description of the broadcast zone
                            "SoundChannel VARCHAR(45) NOT NULL," +   // Sound channel of the broadcast zone
                            "id VARCHAR(45) NOT NULL," +            // Box of the broadcast zone
                            "bp VARCHAR(45) NOT NULL" +           // Relay of the broadcast zone
                            ")"
                    super.Create(tabledefinition)
                }

                override fun Get() {
                    List.clear()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        while (resultSet?.next() == true) {
                            val zone = BroadcastZones(
                                resultSet.getLong("index").toUInt(),
                                resultSet.getString("description"),
                                resultSet.getString("SoundChannel"),
                                resultSet.getString("id"),
                                resultSet.getString("bp")
                            )
                            List.add(zone)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any)
                    }
                }

                override fun Add(data: BroadcastZones): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)")
                        statement?.setString(1, data.description)
                        statement?.setString(2, data.SoundChannel)
                        statement?.setString(3, data.id)
                        statement?.setString(4, data.bp)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Broadcast zone added: ${data.description}" as Any)
                            return true
                        } else {
                            Logger.warn("No broadcast zone entry added for: ${data.description}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error adding broadcast zone entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<BroadcastZones>): Boolean {
                    try {
                        connection.autoCommit = false
                        val sql =
                            "INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (bz in data) {
                            statement.setString(1, bz.description)
                            statement.setString(2, bz.SoundChannel)
                            statement.setString(3, bz.id)
                            statement.setString(4, bz.bp)
                            statement.addBatch()
                        }
                        statement.executeBatch()
                        connection.commit()
                        Logger.info("Bulk ${super.dbName} insert successful: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error adding ${super.dbName} entries: ${e.message}" as Any)
                    }
                    return false
                }

                override fun UpdateByIndex(index: Int, data: BroadcastZones): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("UPDATE ${super.dbName} SET description = ?, SoundChannel = ?, id = ?, bp = ? WHERE `index` = ?")
                        statement?.setString(1, data.description)
                        statement?.setString(2, data.SoundChannel)
                        statement?.setString(3, data.id)
                        statement?.setString(4, data.bp)
                        statement?.setLong(5, index.toLong())
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("Broadcast zone updated at index $index: ${data.description}" as Any)
                            return true
                        } else {
                            Logger.warn("No broadcast zone entry updated at index $index for: ${data.description}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error updating broadcast zone entry at index $index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"
                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM ${super.dbName} ORDER BY description ")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by description" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by description: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_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", "id", "bp")
                        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()
                        // 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 id = row.getCell(3)?.stringCellValue ?: continue
                            val bp = row.getCell(4)?.stringCellValue ?: continue
                            val broadcastZone = BroadcastZones(0u, description, soundChannel, id, bp)
                            _broadcastZonesList.add(broadcastZone)
                        }
                        return AddAll(_broadcastZonesList)
                    } catch (e: Exception) {
                        Logger.error { "Error importing BroadcastZones, Msg: ${e.message}" }
                    }
                    return false
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        val workbook = XSSFWorkbook()
                        val sheet = workbook.createSheet("BroadcastZones")
                        val headerRow = sheet.createRow(0)
                        val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp")
                        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("id"))
                            row.createCell(4).setCellValue(resultSet.getString("bp"))
                        }
                        for (i in headers.indices) {
                            sheet.autoSizeColumn(i)
                        }
                        return workbook
                    } catch (e: Exception) {
                        Logger.error { "Error exporting BroadcastZones, Msg: ${e.message}" }
                    }
                    return null
                }

            }

            queuetableDB = object : dbFunctions<QueueTable>("queue_table", connection) {
                override fun Create() {
                    val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "Date_Time VARCHAR(45) NOT NULL," +        // Date and time of the entry
                            "Source VARCHAR(45) NOT NULL," +           // Source of the entry
                            "Type VARCHAR(45) NOT NULL," +             // Type of the entry
                            "Message VARCHAR(1024) NOT NULL," +        // Message content
                            "SB_TAGS VARCHAR(1024)," +                 // Comma-separated soundbank tags
                            "BroadcastZones VARCHAR(1024) NOT NULL," + // Comma-separated broadcast zones
                            "`Repeat` INT NOT NULL," +                   // Number of repeats
                            "Language VARCHAR(100) NOT NULL" +          // Language of the message
                            ")"
                    super.Create(tabledefinition)
                }

                override fun Get() {
                    List.clear()
                    val queueList = ArrayList<QueueTable>()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        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)
                            List.add(queueTable)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any)
                    }
                }

                override fun Add(data: QueueTable): Boolean {
                    try {
                        val statement = connection.prepareStatement(
                            "INSERT INTO ${super.dbName} (`Date_Time`, `Source`, `Type`, `Message`, `SB_TAGS`, `BroadcastZones`, `Repeat`, `Language`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
                        )
                        statement?.setString(1, data.Date_Time)
                        statement?.setString(2, data.Source)
                        statement?.setString(3, data.Type)
                        statement?.setString(4, data.Message)
                        statement?.setString(5, data.SB_TAGS)
                        statement?.setString(6, data.BroadcastZones)
                        statement?.setInt(7, data.Repeat.toInt())
                        statement?.setString(8, data.Language)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("QueueTable added Source=${data.Source} Type=${data.Type} Message=${data.Message}, Languages=${data.Language} Variables=${data.SB_TAGS}, BroadcastZones=${data.BroadcastZones}" as Any)
                            return true
                        } else {
                            Logger.warn("No QueueTable entry added for: ${data.Message}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error adding QueueTable entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<QueueTable>): Boolean {
                    try {
                        connection.autoCommit = false
                        val sql =
                            "INSERT INTO ${super.dbName} (`Date_Time`, `Source`, `Type`, `Message`, `SB_TAGS`, `BroadcastZones`, `Repeat`, `Language`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (qt in data) {
                            statement.setString(1, qt.Date_Time)
                            statement.setString(2, qt.Source)
                            statement.setString(3, qt.Type)
                            statement.setString(4, qt.Message)
                            statement.setString(5, qt.SB_TAGS)
                            statement.setString(6, qt.BroadcastZones)
                            statement.setInt(7, qt.Repeat.toInt())
                            statement.setString(8, qt.Language)
                            statement.addBatch()
                        }
                        statement.executeBatch()
                        connection.commit()
                        Logger.info("Bulk QueueTable insert successful: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error adding QueueTable entries: ${e.message}" as Any)
                    }
                    return false
                }

                override fun UpdateByIndex(index: Int, data: QueueTable): Boolean {
                    throw Exception("Update not supported")
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"
                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM ${super.dbName} ORDER BY `index` ")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by index" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_XLSX(workbook: XSSFWorkbook): Boolean {
                    throw Exception("Import XLSX not supported for QueueTable")
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        val workbook = XSSFWorkbook()
                        val sheet = workbook.createSheet("QueueTable")
                        val headerRow = sheet.createRow(0)
                        val headers = arrayOf(
                            "Index",
                            "Date_Time",
                            "Source",
                            "Type",
                            "Message",
                            "SB_TAGS",
                            "BroadcastZones",
                            "Repeat",
                            "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("Date_Time"))
                            row.createCell(2).setCellValue(resultSet.getString("Source"))
                            row.createCell(3).setCellValue(resultSet.getString("Type"))
                            row.createCell(4).setCellValue(resultSet.getString("Message"))
                            row.createCell(5).setCellValue(resultSet.getString("SB_TAGS"))
                            row.createCell(6).setCellValue(resultSet.getString("BroadcastZones"))
                            row.createCell(7).setCellValue(resultSet.getString("Repeat"))
                            row.createCell(8).setCellValue(resultSet.getString("Language"))
                        }
                        for (i in headers.indices) {
                            sheet.autoSizeColumn(i)
                        }
                        return workbook
                    } catch (e: Exception) {
                        Logger.error { "Error exporting QueueTable, Msg: ${e.message}" }
                    }
                    return null
                }

            }

            queuepagingDB = object : dbFunctions<QueuePaging>("queue_paging", connection) {
                override fun Create() {
                    val tabledefinition ="CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "Date_Time VARCHAR(45) NOT NULL," +        // Date and time of the entry
                            "Source VARCHAR(45) NOT NULL," +           // Source of the entry
                            "Type VARCHAR(45) NOT NULL," +             // Type of the entry
                            "Message VARCHAR(1024) NOT NULL," +        // Message content
                            "BroadcastZones VARCHAR(1024)" +                  // Comma-separated soundbank tags
                            ")"
                    super.Create(tabledefinition)
                }

                override fun Get() {
                    List.clear()
                    val queueList = ArrayList<QueuePaging>()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        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("BroadcastZones"),
                            )
                            queueList.add(queuePaging)
                            List.add(queuePaging)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any)
                    }
                }

                override fun Add(data: QueuePaging): Boolean {
                    try {
                        val statement = connection.prepareStatement(
                            "INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) VALUES (?, ?, ?, ?, ?)"
                        )
                        statement?.setString(1, data.Date_Time)
                        statement?.setString(2, data.Source)
                        statement?.setString(3, data.Type)
                        statement?.setString(4, data.Message)

                        statement?.setString(5, data.BroadcastZones)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("QueuePaging added: ${data.Message}" as Any)
                            return true
                        } else {
                            Logger.warn("No QueuePaging entry added for: ${data.Message}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error adding QueuePaging entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<QueuePaging>): Boolean {
                    return try {
                        connection.autoCommit = false
                        val sql =
                            "INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) VALUES (?, ?, ?, ?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (qp in data) {
                            statement.setString(1, qp.Date_Time)
                            statement.setString(2, qp.Source)
                            statement.setString(3, qp.Type)
                            statement.setString(4, qp.Message)
                            statement.setString(5, qp.BroadcastZones)
                            statement.addBatch()
                        }
                        statement.executeBatch()
                        connection.commit()
                        Logger.info("Bulk QueuePaging insert successful: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        true
                    } catch (e: Exception) {
                        Logger.error("Error adding QueuePaging entries: ${e.message}" as Any)
                        false
                    }
                }

                override fun UpdateByIndex(index: Int, data: QueuePaging): Boolean {
                    throw Exception("Update not supported")
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"
                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (Date_Time, Source, Type, Message, BroadcastZones) SELECT Date_Time, Source, Type, Message, BroadcastZones FROM ${super.dbName}  ")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) SELECT Date_Time, Source, Type, Message, BroadcastZones FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by index" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_XLSX(workbook: XSSFWorkbook): Boolean {
                    throw Exception("Importing QueuePaging from XLSX is not supported")
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        val workbook = XSSFWorkbook()
                        val sheet = workbook.createSheet("QueuePaging")
                        val headerRow = sheet.createRow(0)
                        val headers = arrayOf("Index", "Date_Time", "Source", "Type", "Message", "BroadcastZones")
                        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("Date_Time"))
                            row.createCell(2).setCellValue(resultSet.getString("Source"))
                            row.createCell(3).setCellValue(resultSet.getString("Type"))
                            row.createCell(4).setCellValue(resultSet.getString("Message"))
                            row.createCell(5).setCellValue(resultSet.getString("BroadcastZones"))
                        }
                        for (i in headers.indices) {
                            sheet.autoSizeColumn(i)
                        }
                        return workbook
                    } catch (e: Exception) {
                        Logger.error { "Error exporting QueuePaging, Msg: ${e.message}" }
                    }
                    return null
                }

            }

            soundchannelDB = object : dbFunctions<SoundChannel>("soundchannel", connection) {
                override fun Create() {
                    val tableDefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "channel VARCHAR(45) NOT NULL," +  // Channel 01 to Channel 64
                            "ip VARCHAR(45) NOT NULL" +         // IP address or empty string
                            ")"

                    super.Create(tableDefinition)

                    // Check if table is empty, if so, populate with 64 channels
                    try {
                        val statement = connection.createStatement()
                        val countResult = statement?.executeQuery("SELECT COUNT(*) AS count FROM ${super.dbName}")
                        if (countResult?.next() == true) {
                            val count = countResult.getInt("count")
                            if (count < max_channel) {
                                Logger.info("SoundChannel table is empty, populating with default channels" as Any)
                                Clear()
                            }
                        }
                    } catch (e: Exception) {
                        Logger.error("Error creating SoundChannel table: ${e.message}" as Any)
                    }
                }

                override fun Get() {
                    List.clear()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} ORDER BY `index` ")
                        while (resultSet?.next() == true) {
                            val channel = SoundChannel(
                                resultSet.getLong("index").toUInt(),
                                resultSet.getString("channel"),
                                resultSet.getString("ip")
                            )
                            List.add(channel)
                        }

                    } catch (e: Exception) {
                        Logger.error("Error fetching sound channels: ${e.message}" as Any)
                    }
                }

                override fun Add(data: SoundChannel): Boolean {
                    try {
                        val statement = connection.prepareStatement("UPDATE ${super.dbName} SET ip = ? WHERE channel = ?")
                        statement?.setString(1, data.ip)
                        statement?.setString(2, data.channel)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("SoundChannel updated: ${data.channel} -> ${data.ip}" as Any)
                            return true
                        } else {
                            Logger.warn("No SoundChannel entry updated for: ${data.channel} -> ${data.ip}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error updating SoundChannel entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<SoundChannel>): Boolean {
                    return try {
                        connection.autoCommit = false
                        val sql = "UPDATE ${super.dbName} SET ip = ? WHERE channel = ?"
                        val statement = connection.prepareStatement(sql)
                        for (sc in data) {
                            statement.setString(1, sc.ip)
                            statement.setString(2, sc.channel)
                            statement.addBatch()
                        }
                        statement.executeBatch()
                        connection.commit()
                        Logger.info("Bulk SoundChannel update successful: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        true
                    } catch (e: Exception) {
                        Logger.error("Error updating SoundChannel entries: ${e.message}" as Any)
                        false
                    }
                }

                override fun UpdateByIndex(index: Int, data: SoundChannel): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("UPDATE ${super.dbName} SET channel = ?, ip = ? WHERE `index` = ?")
                        statement?.setString(1, data.channel)
                        statement?.setString(2, data.ip)
                        statement?.setLong(3, index.toLong())
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("SoundChannel updated at index $index: ${data.channel} -> ${data.ip}" as Any)
                            return true
                        } else {
                            Logger.warn("No SoundChannel entry updated at index $index for: ${data.channel} -> ${data.ip}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error updating SoundChannel entry at index $index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"
                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (channel, ip) SELECT channel, ip FROM ${super.dbName} ORDER BY `index` ")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (channel, ip) SELECT channel, ip FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by index" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Clear(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        // use TRUNCATE to reset auto increment index
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        Logger.info("${super.dbName} table cleared" as Any)
                        List.clear()
                        // create new rows from 1 to 64 with description "Channel 1" to "Channel 64" and empty ip
                        for (i in 1..max_channel) {
                            val channel = String.format("Channel %d", i)
                            val insertStatement =
                                connection.prepareStatement("INSERT INTO ${super.dbName} (channel, ip) VALUES (?, ?)")
                            insertStatement?.setString(1, channel)
                            insertStatement?.setString(2, "")
                            insertStatement?.executeUpdate()
                        }
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error clearing soundchannel table: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_XLSX(workbook: XSSFWorkbook): Boolean {
                    try {
                        val sheet =
                            workbook.getSheet("SoundChannel") ?: throw Exception("No sheet named 'SoundChannel' found")
                        val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
                        val headers = arrayOf("Index", "channel", "ip")
                        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 soundchannel
                        Clear()
                        // read each row and insert into database
                        val _soundChannelList = ArrayList<SoundChannel>()
                        for (rowIndex in 1..sheet.lastRowNum) {
                            val row = sheet.getRow(rowIndex) ?: continue
                            val channel = row.getCell(1)?.stringCellValue ?: continue
                            val ip = row.getCell(2)?.stringCellValue ?: continue
                            val soundChannel = SoundChannel(0u, channel, ip)
                            _soundChannelList.add(soundChannel)
                        }
                        // Bulk update IPs for channels
                        var success = true
                        for (sc in _soundChannelList) {
                            if (!soundchannelDB.Add(sc)) {
                                success = false
                            }
                        }
                        return success
                    } catch (e: Exception) {
                        Logger.error { "Error importing SoundChannel, Msg: ${e.message}" }
                    }
                    return false
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        val workbook = XSSFWorkbook()
                        val sheet = workbook.createSheet("SoundChannel")
                        val headerRow = sheet.createRow(0)
                        val headers = arrayOf("Index", "channel", "ip")
                        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("channel"))
                            row.createCell(2).setCellValue(resultSet.getString("ip"))
                        }
                        for (i in headers.indices) {
                            sheet.autoSizeColumn(i)
                        }
                        return workbook
                    } catch (e: Exception) {
                        Logger.error { "Error exporting SoundChannel, Msg: ${e.message}" }
                    }
                    return null
                }

                /**
                 * Delete entry by index, but only clear the IP field
                 * @param index The index of the entry to delete
                 * @return true if successful, false otherwise
                 */
                override fun DeleteByIndex(index: Int): Boolean {
                    try {
                        val statement = connection.prepareStatement("UPDATE ${super.dbName} SET ip = '' WHERE `index` = ?")
                        statement?.setLong(1, index.toLong())
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("${super.dbName} IP cleared for index $index" as Any)
                            return true
                        } else {
                            Logger.warn("No ${super.dbName} entry cleared for index $index" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error clearing ${super.dbName} entry for index $index: ${e.message}" as Any)
                    }
                    return false
                }

            }

            logDB = object : dbFunctions<Log>("logs", connection) {
                override fun Create() {
                    val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "datenya VARCHAR(20) NOT NULL," +  // format DD/MM/YYYY
                            "timenya VARCHAR(20) NOT NULL," +   // format HH:MM:SS
                            "machine VARCHAR(45) NOT NULL," +
                            "description TEXT NOT NULL" +
                            ")"

                    super.Create(tabledefinition)

                }

                override fun Get() {
                    List.clear()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        while (resultSet?.next() == true) {
                            val log = Log(
                                resultSet.getLong("index").toULong(),
                                resultSet.getString("datenya"),
                                resultSet.getString("timenya"),
                                resultSet.getString("machine"),
                                resultSet.getString("description")
                            )
                            List.add(log)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)
                    }
                }



                override fun Add(data: Log): Boolean {
                    try {
                        val statement =
                            connection.prepareStatement("INSERT INTO logs (datenya, timenya, machine, description) VALUES (?, ?, ?, ?)")
                        statement?.setString(1, data.datenya)
                        statement?.setString(2, data.timenya)
                        statement?.setString(3, data.machine)
                        statement?.setString(4, data.description)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            return true
                        } else {
                            Logger.warn{"Failed to add log entry : $data"}
                        }
                    } catch (e: Exception) {
                        Logger.error{"Error adding log entry: ${e.message}"}
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<Log>): Boolean {
                    return try {
                        connection.autoCommit = false
                        val sql = "INSERT INTO logs (datenya, timenya, machine, description) VALUES (?, ?, ?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (log in data) {
                            statement.setString(1, log.datenya)
                            statement.setString(2, log.timenya)
                            statement.setString(3, log.machine)
                            statement.setString(4, log.description)
                            statement.addBatch()
                        }
                        statement.executeBatch()
                        connection.commit()
                        Logger.info("Bulk log insert successful: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        true
                    } catch (e: Exception) {
                        Logger.error("Error adding log entries: ${e.message}" as Any)
                        false
                    }
                }

                override fun UpdateByIndex(index: Int, data: Log): Boolean {
                    throw Exception("Update not supported")
                }

                override fun Resort(): Boolean {
                    try {
                            val statement = connection.createStatement()
                            val tempdb_name = "temp_${super.dbName}"
                            // use a temporary table to reorder the index
                            statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                            statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                            statement?.executeUpdate(
                                "INSERT INTO $tempdb_name (datenya, timenya, machine, description) " +
                                "SELECT datenya, timenya, machine, description FROM ${super.dbName} " +
                                "ORDER BY datenya , timenya , machine "
                            )
                            statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                            statement?.executeUpdate(
                                "INSERT INTO ${super.dbName} (datenya, timenya, machine, description) " +
                                "SELECT datenya, timenya, machine, description FROM $tempdb_name"
                            )
                            statement?.executeUpdate("DROP TABLE $tempdb_name")
                            Logger.info("${super.dbName} table resorted by datenya, timenya, machine" as Any)
                            // reload the local list
                            Get()
                            return true
                        } catch (e: Exception) {
                            Logger.error("Error resorting ${super.dbName} table by datenya, timenya, machine: ${e.message}" as Any)
                        }
                        return false
                }

                override fun Import_XLSX(workbook: XSSFWorkbook): Boolean {
                    throw Exception("Importing Logs from XLSX is not supported")
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        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
                }



            }

            userDB = object : dbFunctions<UserDB>("newuser", connection){
                override fun Create() {
                    val tableDefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
                            "`index` INT AUTO_INCREMENT PRIMARY KEY," +
                            "username VARCHAR(100) NOT NULL," +
                            "password VARCHAR(100) NOT NULL," +
                            "location VARCHAR(100) NOT NULL," +
                            "airline_tags TEXT NOT NULL,"+  // Comma-separated soundbank tags
                            "city_tags TEXT NOT NULL,"+     // Comma-separated soundbank tags
                            "messagebank_ann_id TEXT NOT NULL,"+ // Comma-separated messagebank announcement index
                            "broadcastzones TEXT NOT NULL"+ // Comma-separated broadcast zones
                            ")"
                    super.Create(tableDefinition)
                }

                override fun Get() {
                    List.clear()
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        while (resultSet?.next() == true) {
                            val user = UserDB(
                                resultSet.getLong("index").toUInt(),
                                resultSet.getString("username"),
                                resultSet.getString("password"),
                                resultSet.getString("location"),
                                resultSet.getString("airline_tags"),
                                resultSet.getString("city_tags"),
                                resultSet.getString("messagebank_ann_id"),
                                resultSet.getString("broadcastzones")
                            )
                            List.add(user)
                        }

                    } catch (e: Exception) {
                        Logger.error("Error fetching users: ${e.message}" as Any)
                    }
                }

                override fun Add(data: UserDB): Boolean {
                    try {
                        val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) VALUES (?, ?, ?, ?,?, ?, ?)")
                        statement?.setString(1, data.username)
                        statement?.setString(2, data.password)
                        statement?.setString(3, data.location)
                        statement?.setString(4, data.airline_tags)
                        statement?.setString(5, data.city_tags)
                        statement?.setString(6, data.messagebank_ann_id)
                        statement?.setString(7, data.broadcastzones)
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("User added: ${data.username}" as Any)
                            return true
                        } else {
                            Logger.warn("No user entry added for: ${data.username}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error adding user entry: ${e.message}" as Any)
                    }
                    return false
                }

                override fun AddAll(data: ArrayList<UserDB>): Boolean {
                    return try {
                        connection.autoCommit = false
                        val sql = "INSERT INTO ${super.dbName} (username, password, location,airline_tags,city_tags, messagebank_ann_id, broadcastzones) VALUES (?, ?, ?,?, ?, ?, ?)"
                        val statement = connection.prepareStatement(sql)
                        for (user in data) {
                            statement.setString(1, user.username)
                            statement.setString(2, user.password)
                            statement.setString(3, user.location)
                            statement.setString(4, user.airline_tags)
                            statement.setString(5, user.city_tags)
                            statement.setString(6, user.messagebank_ann_id)
                            statement.setString(7, user.broadcastzones)
                            statement.addBatch()
                        }
                        statement.executeBatch()
                        connection.commit()
                        Logger.info("Bulk user insert successful: ${data.size} entries" as Any)
                        connection.autoCommit = true
                        true
                    } catch (e: Exception) {
                        Logger.error("Error adding user entries: ${e.message}" as Any)
                        false
                    }
                }

                override fun UpdateByIndex(index: Int, data: UserDB): Boolean {
                    try {
                        val statement = connection.prepareStatement("UPDATE ${super.dbName} SET username = ?, password = ?, location = ?, airline_tags = ?,city_tags=?, messagebank_ann_id = ?, broadcastzones = ?  WHERE `index` = ?")
                        statement?.setString(1, data.username)
                        statement?.setString(2, data.password)
                        statement?.setString(3, data.location)
                        statement?.setString(4, data.airline_tags)
                        statement?.setString(5, data.city_tags)
                        statement?.setString(6, data.messagebank_ann_id)
                        statement?.setString(7, data.broadcastzones)
                        statement?.setLong(8, index.toLong())
                        val rowsAffected = statement?.executeUpdate()
                        if (rowsAffected != null && rowsAffected > 0) {
                            Logger.info("User updated at index $index: ${data.username}" as Any)
                            return true
                        } else {
                            Logger.warn("No user entry updated at index $index for: ${data.username}" as Any)
                        }
                    } catch (e: Exception) {
                        Logger.error("Error updating user entry at index $index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Resort(): Boolean {
                    try {
                        val statement = connection.createStatement()
                        val tempdb_name = "temp_${super.dbName}"
                        // use a temporary table to reorder the index
                        statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
                        statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
                        statement?.executeUpdate("INSERT INTO $tempdb_name (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM ${super.dbName} ORDER BY username ")
                        statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
                        statement?.executeUpdate("INSERT INTO ${super.dbName} (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM $tempdb_name")
                        statement?.executeUpdate("DROP TABLE $tempdb_name")
                        Logger.info("${super.dbName} table resorted by index" as Any)
                        // reload the local list
                        Get()
                        return true
                    } catch (e: Exception) {
                        Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any)
                    }
                    return false
                }

                override fun Import_XLSX(workbook: XSSFWorkbook): Boolean {
                    try {
                        val sheet = workbook.getSheet("User") ?: throw Exception("No sheet named 'User' found")
                        val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
                        val headers = arrayOf("Index", "username", "password", "location", "airline_tags", "city_tags", "messagebank_ann_id", "broadcastzones")
                        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 users
                        Clear()
                        // read each row and insert into database
                        val _userList = ArrayList<UserDB>()
                        for (rowIndex in 1..sheet.lastRowNum) {
                            val row = sheet.getRow(rowIndex) ?: continue
                            val username = row.getCell(1)?.stringCellValue ?: continue
                            val password = row.getCell(2)?.stringCellValue ?: continue
                            val location = row.getCell(3)?.stringCellValue ?: continue
                            val airline_tags = row.getCell(4)?.stringCellValue ?: continue
                            val city_tags = row.getCell(5)?.stringCellValue ?: continue
                            val messagebank_ann_id = row.getCell(6)?.stringCellValue ?: continue
                            val broadcastzones = row.getCell(7)?.stringCellValue ?: continue
                            val user = UserDB(0u, username, password, location, airline_tags,city_tags, messagebank_ann_id, broadcastzones)
                            _userList.add(user)
                        }
                        return AddAll(_userList)
                    } catch (e: Exception) {
                        Logger.error { "Error importing User, Msg: ${e.message}" }
                    }
                    return false
                }

                override fun Export_XLSX(): XSSFWorkbook? {
                    try {
                        val statement = connection.createStatement()
                        val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
                        val workbook = XSSFWorkbook()
                        val sheet = workbook.createSheet("User")
                        val headerRow = sheet.createRow(0)
                        val headers = arrayOf("Index", "username", "password", "location", "airline_tags","city_tags", "messagebank_ann_id", "broadcastzones")
                        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("username"))
                            row.createCell(2).setCellValue(resultSet.getString("password"))
                            row.createCell(3).setCellValue(resultSet.getString("location"))
                            row.createCell(4).setCellValue(resultSet.getString("airline_tags"))
                            row.createCell(5).setCellValue(resultSet.getString("city_tags"))
                            row.createCell(6).setCellValue(resultSet.getString("messagebank_ann_id"))
                            row.createCell(7).setCellValue(resultSet.getString("broadcastzones"))
                        }
                        for (i in headers.indices) {
                            sheet.autoSizeColumn(i)
                        }
                        return workbook
                    } catch (e: Exception) {
                        Logger.error { "Error exporting User, Msg: ${e.message}" }
                    }
                    return null
                }

            }

            runBlocking {
                withContext(Dispatchers.IO) {
                    logDB.Create()
                    soundchannelDB.Create()
                    scheduleDB.Create()
                    broadcastDB.Create()
                    messageDB.Create()
                    soundDB.Create()
                    languageDB.Create()
                    queuetableDB.Create()
                    queuepagingDB.Create()
                    userDB.Create()

                    messageDB.Get()
                    soundDB.Get()
                    languageDB.Get()
                    scheduleDB.Get()
                    broadcastDB.Get()
                    soundchannelDB.Get()
                    userDB.Get()
                }
            }



            Logger.info { "Loading MariaDB completed" }
            Logger.info { "Soundbank count: ${soundDB.List.size}" }
            Logger.info { "Messagebank count: ${messageDB.List.size}" }
            Logger.info { "LanguageLink count: ${languageDB.List.size}" }
            Logger.info { "Schedulebank count: ${scheduleDB.List.size}" }
            Logger.info { "BroadcastZones count: ${broadcastDB.List.size}" }
            Logger.info { "SoundChannel count: ${soundchannelDB.List.size}" }
            Logger.info { "User count: ${userDB.List.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
    }


    /**
     * Adds a new log entry to the database with the current date and time.
     * @param machine The machine name or identifier.
     * @param description The log description.
     * @return true if the log was added successfully, false otherwise.
     */
    fun Add_Log(machine: String, description: String): Boolean {
        val current = java.time.LocalDateTime.now()
        val date = current.toLocalDate().toString() // format YYYY-MM-DD
        val time = current.toLocalTime().withNano(0).toString() // format HH:MM:SS
        val datenya = date.split("-").reversed().joinToString("/") // convert to DD/MM/YYYY
        val log = Log(0u, datenya, time, machine, description)
        return logDB.Add(log)
    }

    /**
     * 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 logs")
            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 table: ${e.message}" as Any)
        }
        consumer.accept(logList)
    }

    /**
     * Get Log from database by date for HTML usage
     * @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 GetLogForHtml(date: String, consumer: Consumer<ArrayList<Log>>) {
        val logList = ArrayList<Log>()
        //println("GetLogForHtml Date: $date" )
        if (ValiDateForLogHtml(date)) {
            try {
                // must convert from DD-MM-YYYY to DD/MM/YYYY, because in database we use DD/MM/YYYY
                val adjusteddate = date.replace("-", "/")
                val statement = connection.prepareStatement("SELECT * FROM logs WHERE datenya = ?")
                statement?.setString(1, adjusteddate)
                //println("GetLogForHtml Date: $adjusteddate" )
                // println("GetLogForHtml SQL: " +statement?.toString())
                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 table for date $date: ${e.message}" as Any)
            }
        }

        consumer.accept(logList)
    }

    /**
     * Get Log from database by date and filter for HTML usage
     * @param date The date to filter logs by (format: DD-MM-YYYY)
     * @param filter The filter string to search in description or machine
     * @param consumer A Consumer that will receive the list of logs for the specified date and filter
     */
    fun GetLogForHtml(date: String, filter: String, consumer: Consumer<ArrayList<Log>>) {
        val logList = ArrayList<Log>()
        //println("GetLogForHtml Date: $date Filter: $filter" )

        if (ValiDateForLogHtml(date)) {
            try {
                // must convert from DD-MM-YYYY to DD/MM/YYYY, because in database we use DD/MM/YYYY
                val adjusteddate = date.replace("-", "/")
                val statement =
                    connection.prepareStatement("SELECT * FROM logs WHERE datenya = ? AND description LIKE ?")
                statement?.setString(1, adjusteddate)
                statement?.setString(2, "%$filter%")
                //println("GetLogForHtml Date: $adjusteddate , Filter=$filter" )
                //println("GetLogForHtml SQL: " +statement?.toString())
                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)
    }

    /**
     * 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 logs WHERE datenya = ?"
                } else {
                    "SELECT * FROM logs WHERE datenya = ? AND (description LIKE ?)"
                }
            )
            statement?.setString(1, logDate)
            if (!logFilter.isBlank()) {
                val filter = "%$logFilter%"
                statement?.setString(2, 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
    }

    /**
     * Find all city soundbank by tag
     * @param tag The tags to search for
     * @return a list of Soundbank with Category City and matching tag
     */
    fun Find_Soundbank_City(tag: String) : List<Soundbank>{
        return soundDB.List
            .filter{ it.Category== Category.City.name }
            .filter { it.TAG.equals(tag,true)}
    }

    fun Find_Soundbank_AirplaneName(tag: String) : List<Soundbank>{
        return soundDB.List
            .filter{ it.Category== Category.Airplane_Name.name }
            .filter { it.TAG.equals(tag,true)}
    }

    fun Find_Soundbank_Places(tag: String) : List<Soundbank>{
        return soundDB.List
            .filter{ it.Category== Category.Places.name }
            .filter { it.TAG.equals(tag,true)}
    }

    fun Find_Soundbank_Shalat(tag: String) : List<Soundbank>{
        val lowerTag = tag.lowercase()
        return soundDB.List
            .filter{ it.Category== Category.Shalat.name }
            .filter { it.TAG.lowercase()==lowerTag}
    }

    fun Find_Soundbank_Sequence(tag: String) : List<Soundbank>{
        return soundDB.List
            .filter{ it.Category== Category.Sequence.name }
            .filter { it.TAG.equals(tag,true)}
    }

    fun Find_Soundbank_Reason(tag: String) : List<Soundbank>{
        return soundDB.List
            .filter{ it.Category== Category.Reason.name }
            .filter { it.TAG.equals(tag,true)}
    }

    fun Find_Soundbank_Procedure(tag: String) : List<Soundbank> {
        return soundDB.List
            .filter { it.Category == Category.Procedure.name }
            .filter { it.TAG.equals(tag,true) }
    }

    /**
     * Get all distinct airline code tags from soundbank
     * @return a list of distinct airline code tags sorted alphabetically
     */
    fun Get_AirlineCode_Tags(): List<String> {
        return soundDB.List
            .filter { it.Category == Category.Airline_Code.name }
            .map { it.TAG }
            .distinct()
            .sorted()
    }

    /**
     * Get all distinct city tags from soundbank
     * @return a list of distinct city tags sorted alphabetically
     */
    fun Get_City_Tags(): List<String> {
        return soundDB.List
            .filter { it.Category == Category.City.name }
            .map { it.TAG }
            .distinct()
            .sorted()
    }

    /**
     * Get all distinct message ID from messagebank
     * @return a list of distinct ANN_ID sorted numerically
     */
    fun Get_MessageID_List(): List<UInt> {
        return messageDB.List
            .distinctBy { it.ANN_ID }
            .map { it.ANN_ID }
            .sorted()
    }

    /**
     * Get all distinct broadcast zone descriptions from broadcastDB
     * @return a list of distinct broadcast zone descriptions sorted alphabetically
     */
    fun Get_BroadcastZone_List(): List<String> {
        return broadcastDB.List
            .distinctBy { it.description }
            .map { it.description }
            .sorted()
    }

    /**
     * Get all distinct sound channel from soundchannelDB
     * @return a list of distinct sound channel sorted alphabetically
     */
    fun Get_SoundChannel_List(): List<String> {
        return soundchannelDB.List
            .distinctBy { it.channel }
            .map { it.channel }
            .sorted()
    }

    /**
     * Check if a username already exists in the userDB (case-insensitive)
     */
    fun Username_exists(username: String): Boolean {
        return userDB.List.any { it.username.equals(username, ignoreCase = true) }
    }






}