package database.table import content.Category import database.data.Soundbank import database.dbFunctions import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.tinylog.Logger import java.io.File import java.sql.Connection import java.util.function.Consumer class Table_Soundbank(connection: Connection) : dbFunctions("soundbank", connection, listOf("index", "Description", "TAG", "Category", "Language", "VoiceType", "Path")) { 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(cbOK: Consumer?, cbFail: Consumer?) { 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) } cbOK?.accept(Unit) } catch (e: Exception) { Logger.error("Error fetching soundbanks: ${e.message}" as Any) cbFail?.accept("Error fetching ${super.dbName} : ${e.message}") } } 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): 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() 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 } /** * Get all distinct airline code tags from soundbank * @return a list of distinct airline code tags sorted alphabetically */ fun Get_AirlineCode_Tags(): List { return List .filter { it.Category.equals(Category.Airline_Code.name,true) } .distinctBy { it.TAG } .map { it.TAG } .sorted() } /** * Get all distinct city tags from soundbank * @return a list of distinct city tags sorted alphabetically */ fun Get_City_Tags(): List { return List .filter { it.Category.equals(Category.City.name,true) } .distinctBy { it.TAG } .map { it.TAG } .sorted() } /** * Find City by TAG * @param tag the city tag to search for * @return a list of Soundbank entries matching the city tag */ fun Find_City_By_TAG(tag: String) : List { return List .filter {it.Category.equals(Category.City.name,true) } .filter { it.TAG.equals(tag, true) } .distinctBy { it.TAG } } /** * Find Airline Name by TAG * @param tag the airline code tag to search for * @return a list of Soundbank entries matching the airline code tag */ fun Find_AirlineName_By_TAG(tag: String) : List { return List .filter {it.Category.equals(Category.Airplane_Name.name,true) } .filter { it.TAG.equals(tag, true) } .distinctBy { it.TAG } } fun Get_Places(): List { return List .filter { it.Category.equals(Category.Places.name,true) } .distinctBy { it.TAG } .sortedBy { it.TAG } } fun Get_Shalat() : List { return List .filter { it.Category.equals(Category.Shalat.name,true) } .distinctBy { it.TAG } .sortedBy { it.TAG } } fun Get_Sequences() : List { return List .filter { it.Category.equals(Category.Sequence.name,true) } .distinctBy { it.TAG } .sortedBy { it.TAG } } fun Get_Reasons() : List { return List .filter { it.Category.equals(Category.Reason.name,true) } .distinctBy { it.TAG } .sortedBy { it.TAG } } fun Get_Gates(): List { return List .filter { it.Category.equals(Category.Gate.name,true) } .distinctBy { it.TAG } .sortedBy { it.TAG } } fun Get_Compensation(): List { return List .filter { it.Category.equals(Category.Compensation.name,true) } .distinctBy { it.TAG } .sortedBy { it.TAG } } fun Get_Greeting() : List { return List .filter { it.Category.equals(Category.Greeting.name,true) } .distinctBy { it.TAG } .sortedBy { it.TAG } } fun Get_Procedures(): List { return List .filter { it.Category.equals(Category.Procedure.name,true) } .distinctBy { it.TAG } .sortedBy { it.TAG } } class FileCheckResult{ var validfile = arrayListOf() var invalidfile = arrayListOf() } /** * Check Soundbank path files existence * @param cb callback with FileCheckResult containing valid and invalid files */ fun FileCheck(cb: Consumer){ val result = FileCheckResult() // file wav must at least 10 kb val validfilesize = 10 * 1024; // 10 KB for (sb in List){ if (sb.Path.isBlank()) { result.invalidfile.add(sb) continue } val file = File(sb.Path) if (file.isFile && file.length() >= validfilesize) { // file size should at leat 10 kb result.validfile.add(sb) } else { result.invalidfile.add(sb) } } cb.accept(result) } }