import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import com.russhwolf.settings.Settings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import mg.dot.feufaro.FileRepository
import mg.dot.feufaro.data.DrawerItem
import mg.dot.feufaro.data.GridTUOData
import mg.dot.feufaro.data.getCombinedList
import mg.dot.feufaro.solfa.TimeUnitObject
import mg.dot.feufaro.midi.FMediaPlayer
import mg.dot.feufaro.provideSettings
import mg.dot.feufaro.solfa.TUOEditState
import mg.dot.feufaro.solfa.getAllMarker
import mg.dot.feufaro.viewmodel.MidiMarkers
import java.io.File

class SharedScreenModel(private val fileRepository: FileRepository) : ScreenModel {
    private val _nextLabel = MutableStateFlow<String>("Next ...")
    val nextLabel: StateFlow<String> = _nextLabel.asStateFlow()

    private val _loadFile = MutableStateFlow<String>("Load ...")

    val loadFile: StateFlow<String> = _loadFile.asStateFlow()
    private val _measure = MutableStateFlow<String>("")
    val measure: StateFlow<String> = _measure.asStateFlow()
    private val _songTitle = MutableStateFlow<String>("")
    val songTitle: StateFlow<String> = _songTitle.asStateFlow()
    private val _stanza = MutableStateFlow<Int>(0)
    val stanza: StateFlow<Int> = _stanza.asStateFlow()
    private val _songKey = MutableStateFlow<String>("")
    val songKey: StateFlow<String> = _songKey.asStateFlow()
    private val _transposeTo = MutableStateFlow("")
    val transposeTo: StateFlow<String> = _transposeTo.asStateFlow()
    private val _transposeAsIf = MutableStateFlow("")
    val transposeAsIf: StateFlow<String> = _transposeAsIf.asStateFlow()
    private val _songAuthor = MutableStateFlow<String>("")
    val songAuthor: StateFlow<String> = _songAuthor.asStateFlow()
    private val _songComposer = MutableStateFlow<String>("")
    val songComposer: StateFlow<String> = _songComposer.asStateFlow()
    private val _songRhythm = MutableStateFlow<String>("")
    val songRhythm: StateFlow<String> = _songRhythm.asStateFlow()
    private val _nbStanzas = MutableStateFlow<Int>(0)
    val nbStanzas: StateFlow<Int> = _nbStanzas.asStateFlow()
    private var _tuoList =
        MutableStateFlow<List<TimeUnitObject>>(emptyList())
    val tuoList: StateFlow<List<TimeUnitObject>> = _tuoList.asStateFlow()
    private var _playlist =
        MutableStateFlow<List<String>>(emptyList())
    val playlist: StateFlow<List<String>> = _playlist.asStateFlow()
    private var _nextPlayed = MutableStateFlow(0)
    val nextPlayed: StateFlow<Int> = _nextPlayed.asStateFlow()
    private val tempTimeUnitObjectList = mutableListOf<TimeUnitObject>()
    var _hasMarker = MutableStateFlow<Boolean>(false)
    val hasMarker: StateFlow<Boolean> = _hasMarker.asStateFlow()

    private val _searchTitle = MutableStateFlow<String>("")
    val searchTitle: StateFlow<String> = _searchTitle.asStateFlow()

    private val _drawerItems = MutableStateFlow<List<DrawerItem>>(emptyList())
    val drawerItems: StateFlow<List<DrawerItem>> = _drawerItems.asStateFlow()

    private val prefs: Settings = provideSettings()

    private val _currentBpmFlow = MutableStateFlow(120f)
    val currentBpmFlow: StateFlow<Float> = _currentBpmFlow.asStateFlow()

    fun setBpmFlow(newBpm: Float) {
        _currentBpmFlow.value = newBpm
    }
    fun getBpmFlow(): StateFlow<Float> {
        return currentBpmFlow
    }
    val internalItems: StateFlow<List<DrawerItem>> = _drawerItems
        .map { list -> list.filter { it.path.startsWith("assets://") } }
        .stateIn(screenModelScope, SharingStarted.Lazily, emptyList())

    val externalItems: StateFlow<List<DrawerItem>> = _drawerItems
        .map { list ->
            list.filter { !it.path.startsWith("assets://") }
                .sortedByDescending { item: DrawerItem ->
                try {
                    File(item.path).lastModified()
                } catch (e: Exception) {
                    0L
                }
            }
        }
        .stateIn(screenModelScope, SharingStarted.Lazily, emptyList())


    private val _measureInt = MutableStateFlow<Int>(4)
    val measureInt: StateFlow<Int> = _measureInt.asStateFlow()
    fun setMeasureInt(measure: Int) {
        _measureInt.value = measure
    }

    private val _silentDurationBefore = MutableStateFlow<Int>(0)
    val silentDurationBefore: StateFlow<Int> = _silentDurationBefore.asStateFlow()
    fun setSilentDurBefr(silent: Int) {
        _silentDurationBefore.value = silent
    }

    private val playlistFilename = "playlist.json"
    private val _playlistPaths = MutableStateFlow<List<String>>(emptyList())
    val playlistItems: StateFlow<List<DrawerItem>> = combine(_playlistPaths, _drawerItems) { paths, allItems ->
        paths.mapNotNull { path -> allItems.find { it.path == path } }
    }.stateIn(screenModelScope, SharingStarted.Lazily, emptyList())

    fun savePlaylistToDisk(playlist: List<String>) {
        screenModelScope.launch {
            try {
                val itemsJson = playlist.joinToString(
                    separator = "\",\"",
                    prefix = "[\"",
                    postfix = "\"]"
                ).replace("[\"\"]", "[]")

                val finalJson = "{\"playlist\": $itemsJson}"

                val data = finalJson.encodeToByteArray()

                fileRepository.saveLocalFile(playlistFilename, data)

                println("Playlist sauvegardée avec succès !")
            } catch (e: Exception) {
                println("Erreur lors de la sauvegarde : ${e.message}")
            }
        }
    }
    fun toggleFavorite(path: String) {
        val current = _playlistPaths.value.toMutableList()
        if (current.contains(path)) {
            current.remove(path)
        } else {
            current.add(path)
        }
        _playlistPaths.value = current
        savePlaylistToDisk(current)
    }

    fun moveUp(index: Int) {
        if (index > 0) {
            val current = _playlistPaths.value.toMutableList()
            val item = current.removeAt(index)
            current.add(index - 1, item)
            _playlistPaths.value = current
            savePlaylistToDisk(current)
        }
    }
    fun moveToTop(index: Int) {
        val current = _playlistPaths.value.toMutableList()
        val item = current.removeAt(index)
        current.add(0, item)
        _playlistPaths.value = current
        savePlaylistToDisk(current)
    }

    fun moveDown(index: Int) {
        val current = _playlistPaths.value.toMutableList()
        if (index < current.size - 1) {
            val item = current.removeAt(index)
            current.add(index + 1, item)
            _playlistPaths.value = current
            savePlaylistToDisk(current)
        }
    }

    fun isInPlaylist(path: String): Boolean {
        return _playlistPaths.value.contains(path)
    }
    init {
        loadItems()
    }

    fun loadItems() {
        screenModelScope.launch {
            _drawerItems.value = getCombinedList(fileRepository)
        }
        loadPlaylistFromDisk()
    }

    private fun loadPlaylistFromDisk() {
        screenModelScope.launch {
            try {
                val publicFolder = fileRepository.getAppPublicFolder()
                val playlistFilePath = "$publicFolder/$playlistFilename"

                val jsonString = fileRepository.readFileContent(playlistFilePath)

                if (jsonString.isNotBlank()) {
                    val paths = jsonString
                        .replace("{\"playlist\":", "")
                        .replace("}", "")
                        .replace("[", "")
                        .replace("]", "")
                        .replace("\"", "")
                        .split(",")
                        .map { it.trim() }
                        .filter { it.isNotBlank() }

                    _playlistPaths.value = paths
                    println("Playlist chargée : ${paths.size} chants")
                }
            } catch (e: Exception) {
                println("Erreur lors du chargement de la playlist : ${e.message}")
            }
        }
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    val filteredSongs: StateFlow<List<DrawerItem>> = searchTitle
        .mapLatest { currentTitle ->
            val searchTxt = if (currentTitle == "") "" else currentTitle.trim()
            return@mapLatest if(searchTxt.isEmpty()) {
                emptyList()
            } else {
                drawerItems.value.filter { item ->
                    item.contentTitle.contains(searchTxt, ignoreCase = true)
                }
            }
        }.stateIn (
            scope = CoroutineScope(Dispatchers.Default),
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )
    private val _fileContent = mutableStateOf<String?>(null)
    val fileContent: State<String?> = _fileContent

    private val _isQRCodeVisible = mutableStateOf(false)
    val isQRCodeVisible: State<Boolean> = _isQRCodeVisible

    private val _sourceModeState = mutableStateOf(false)
    val sourceModeState: State<Boolean> = _sourceModeState

    data class HairPinEditState(
        val isPending: Boolean = false,
        val gridIndex: Int = 0
    )
    private val _isPendingHairPinOnEdit = MutableStateFlow(HairPinEditState())
    val isPendingHairPinOnEdit: StateFlow<HairPinEditState> = _isPendingHairPinOnEdit.asStateFlow()
    fun setPendingHairPin(isPending: Boolean, gridIdx: Int = 0) {
        _isPendingHairPinOnEdit.value = HairPinEditState(isPending, gridIdx)
    }

    private val _modeEditor = MutableStateFlow(false)
    val modeEditor: StateFlow<Boolean> = _modeEditor

    fun toggleEditorMode(enabled: Boolean) {
        _modeEditor.value = enabled
    }

    private val _createMode = MutableStateFlow(false)
    val createMode: StateFlow<Boolean> = _createMode

    fun toggleCreateMode(state: Boolean) {
        _createMode.value = state
    }

    private val _harmonyView = MutableStateFlow(false)
    val harmonyView: StateFlow<Boolean> = _harmonyView
    fun toggleHarmonyMode(enabled: Boolean) {
        _harmonyView.value = enabled
    }

    private val _isFullScreen = MutableStateFlow(false)
    val isFullScreen = _isFullScreen.asStateFlow()

    fun toggleFullScreen(enabled: Boolean) {
        _isFullScreen.value = enabled
    }
    val transpositionInterval = MutableStateFlow(0)

    fun setTranspositionInterval(interval: Int) {
        transpositionInterval.value = interval
    }

    private val _synchronizedSYllables = MutableStateFlow<List<String>>(emptyList())
    val synchronizedSyllables: StateFlow<List<String>> = _synchronizedSYllables.asStateFlow()

    fun updateSyllablesFromList(measures: List<List<TimeUnitObject>>, stanzaNumber: Int) {
        val REGEX_CLEAN_PREFIX = Regex("(\\d+\\.)+")

//        println("\n--- DEBUG updateSyllablesFromList (Strophe $stanzaNumber) ---")

        var absoluteIndex = 0
        _synchronizedSYllables.value = measures.flatten().map { tuo ->
            val rawSyllablesList = tuo.getSingleSyllable(stanzaNumber)

            val processedSyllable = rawSyllablesList.joinToString(" ") { syllable ->
                val cleaned = syllable.replace(REGEX_CLEAN_PREFIX, "").trim()
                cleaned
            }

//            println("TUO #$absoluteIndex | Template: ${tuo.pTemplate.template} | Raw: $rawSyllablesList | Final: [$processedSyllable]")

            absoluteIndex++
            processedSyllable
        }

//        println("--- FIN DEBUG (Total TUOs: $absoluteIndex) ---\n")
    }
    private val _activeFilePath = mutableStateOf("")
    val activeFilePath: State<String> = _activeFilePath

    fun setFileContent(content: String?, path: String) {
        _fileContent.value = content
        _activeFilePath.value = path
    }
    fun toggleQRCodeVisibility() {
        _isQRCodeVisible.value = !_isQRCodeVisible.value
    }
    fun toggleSourceMode() {
        _sourceModeState.value = !_sourceModeState.value
    }
    val qrCodeContent: State<String?>
        get() = mutableStateOf(
            _activeFilePath.value.takeIf { it.isNotBlank() }?.let { path ->
                val fileName = path.substringAfterLast("/").substringAfterLast("\\")
                "feufaro://song?file=${fileName}"
            }
        )

    fun updateSearchTxt(searchValue: String) {
        _searchTitle.value = searchValue
    }
    private val _showMidiCtrl = MutableStateFlow<Boolean>(false)
    val showMidiCtrl: StateFlow<Boolean> = _showMidiCtrl.asStateFlow()
    fun setMidiCtrl(value: Boolean) {
        _showMidiCtrl.value = value
    }

    private val _expandedFAB = MutableStateFlow<Boolean>(false)
    val expandedFAB: StateFlow<Boolean> = _expandedFAB.asStateFlow()

    fun setExpandedFAB(value: Boolean) {
        _expandedFAB.value = value
    }

    private val _showSearchMenu = MutableStateFlow<Boolean>(false)
    val showSearchMenu: StateFlow<Boolean> = _showSearchMenu.asStateFlow()
    fun showSearchMenu(value: Boolean) {
        _showSearchMenu.value = value
    }

    private var _mediaPlayer by mutableStateOf<FMediaPlayer?>(null)
    val mediaPlayer: FMediaPlayer? get() = _mediaPlayer

    private val _isPlay = MutableStateFlow(false)
    val isPlay = _isPlay.asStateFlow()

    private val _isPos = MutableStateFlow(true)
    val isPos = _isPos.asStateFlow()

    private val _isDragging = MutableStateFlow(true)
    val isDragging = _isDragging.asStateFlow()

    private val _currentPos = MutableStateFlow(0f)
    val currentPos = _currentPos.asStateFlow()
    private val _duration = MutableStateFlow(0f)
    val duration = _duration.asStateFlow()

    private val _volumeLevel = MutableStateFlow(0.8f)
    val volumeLevel = _volumeLevel.asStateFlow()

    private val _isPlayMid = MutableStateFlow(false)
    val isPlayMid = _isPlayMid.asStateFlow()

    private val _canUpdPositionFromPartition = MutableStateFlow(false)
    val canUpdPositionFromPartition = _canUpdPositionFromPartition.asStateFlow()

    private val _dcDone = MutableStateFlow(false)
    val dcDone = _dcDone.asStateFlow()

    private val _dsDone = MutableStateFlow(false)
    val dsDone = _dsDone.asStateFlow()

    private val _tuoTimestamps = MutableStateFlow<List<Long>>(emptyList())
    val tuoTimestamps: StateFlow<List<Long>> = _tuoTimestamps

    fun updateTimestamps(list: List<Long>) {
        _tuoTimestamps.value = list
    }

    fun seekToTimestamp(micros: Long) {
        _mediaPlayer?.seekTo(micros / 1000)
    }

    private val _midiMarkersList = MutableStateFlow<List<MidiMarkers>>(emptyList())
    val midiMarkersList: StateFlow<List<MidiMarkers>> = _midiMarkersList
    private val _activeIndex = MutableStateFlow(-1)
    val activeIndex: StateFlow<Int> = _activeIndex.asStateFlow()

    val currentGridData: GridTUOData
        get() = GridTUOData(
            tuoList = _tuoList.value,
            measure = _measure.value,
            stanza = _stanza.value
        )
    fun updateActiveIndex(currentPosMs: Long) {
        val currentPosMicros = currentPosMs * 1000
        val index = _tuoTimestamps.value.indexOfLast { it <= currentPosMicros }
        if (_activeIndex.value != index) {
            _activeIndex.value = index.coerceAtLeast(0)
        }
    }
    fun updateActiveIndexByIndex(index: Int) {
        val clamped = index.coerceAtLeast(0)
        if (_activeIndex.value != clamped) {
            _activeIndex.value = clamped
        }
    }
    private val _gridCount = MutableStateFlow(0)
    val gridCount: StateFlow<Int> = _gridCount.asStateFlow()

    fun addGridCount(nbGrid: Int) {
        _gridCount.value += nbGrid
    }
    fun descGridCount(nbGrid: Int) {
        if(_gridCount.value > 0) {
            _gridCount.value -= nbGrid
        }
    }
    fun resetGridCount() {
        _gridCount.value = 0
    }
    fun seekToGrid(gridIndex: Int) {
        _mediaPlayer?.seekToGrid(gridIndex)
    }

    fun getFullMarkers(): List<MidiMarkers> {
        return _midiMarkersList.value
    }

    fun setDcDone(state: Boolean) {
        _dcDone.value = state
    }
    fun setDsDone(state: Boolean) {
        _dsDone.value = state
    }
    fun getDcDone(): Boolean {
        return _dcDone.value
    }
    fun getDsDone(): Boolean {
        return _dsDone.value
    }

    fun getTotalGridCount(): Int {
        return _tuoList.value.drop(1).size - 1
    }

    fun parseMarkers(rawText: String): MutableList<String> {
        val tokens = mutableListOf<String>()
        if (rawText.isBlank()) return tokens

        var remaining = rawText.replace(",", " ")
            .replace(Regex("""\b(DC)\b""", RegexOption.IGNORE_CASE), "D.C.")
            .replace(Regex("""\b(DS)\b""", RegexOption.IGNORE_CASE), "D.S.")
            .replace(Regex("""(D\.?C\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.C.Fin")
            .replace(Regex("""(D\.?S\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.S.Fin")
            .replace(Regex("""(D\.?C\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.C.alCoda")
            .replace(Regex("""(D\.?S\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.S.alCoda")

        val allStaticMarkers = getAllMarker()

        val tempoRegex = Regex("""^(♩\s*=\s*\d+)""", RegexOption.IGNORE_CASE)
        val modulaRegex = Regex("""^(D(?:ô|o)?\s*dia\s*(?:C|Db|D|Eb|E|F|Gb|G|Ab|A|Bb|B))""", RegexOption.IGNORE_CASE)

        //  Lexer
        while (remaining.isNotEmpty()) {
            remaining = remaining.trimStart()
            if (remaining.isEmpty()) break

            val tempoMatch = tempoRegex.find(remaining)
            if (tempoMatch != null) {
                tokens.add(tempoMatch.value)
                remaining = remaining.substring(tempoMatch.value.length)
                continue
            }

            val modulaMatch = modulaRegex.find(remaining)
            if (modulaMatch != null) {
                tokens.add(modulaMatch.value)
                remaining = remaining.substring(modulaMatch.value.length)
                continue
            }

            val matchedMarker = allStaticMarkers.find { remaining.startsWith(it, ignoreCase = true) }
            if (matchedMarker != null) {
                tokens.add(matchedMarker)
                remaining = remaining.substring(matchedMarker.length)
            } else {
                val unknownChunk = remaining.takeWhile { !it.isWhitespace() }
                tokens.add(unknownChunk)
                remaining = remaining.substring(unknownChunk.length)
            }
        }

//        println("Sortie: ${tokens.joinToString(" | ")}")
        return tokens
    }

    fun updateAndFinalizeMidiData(rawList: List<MidiMarkers>) {
//        val timestamps = _tuoTimestamps.value
        val tuos = _tuoList.value.drop(1)

        val hasDCDS = rawList.any { midiMarker ->
            midiMarker.marker.any { text ->
                text.contains(Regex("""D\.?C\.?|D\.?S\.?""", RegexOption.IGNORE_CASE))
            }
        }
        val finalizedList = rawList.map { marker ->
            val originalMarker = marker.marker
            var currentMarker = marker

            var newGridIndex = marker.gridIndex ?: 0
            val newMarkerList = mutableListOf<String>()

            originalMarker.forEach { markerText ->
                val index = marker.gridIndex ?: 0 // On utilise l'index passé par l'UI
                val isNearEnd = index >= (tuos.size - 2)
//                println("MARKERS:> $markerText\n")

                val isDC = markerText.trim().matches(Regex("""D\.?C\.?"""))
                val isDS = markerText.trim().matches(Regex("""D\.?S\.?"""))
                val isFarany = markerText.trim().matches(Regex("""^(fine|fin|farany|end)$""", RegexOption.IGNORE_CASE))
                val isDSFin = markerText.trim().contains(Regex("""D\.?S\.?""", RegexOption.IGNORE_CASE)) && markerText.trim().contains(Regex("""(fine|fin|farany|end)""", RegexOption.IGNORE_CASE))
                val isRit = Regex("""rit\.?|ritard\.?|ritenuto\.?|ritardando""", RegexOption.IGNORE_CASE)
                val isRall = Regex("""rall\.?|rallent\.?|rallentando""", RegexOption.IGNORE_CASE)

                var searchIndex = index + 1
                var lastValidIndex = index

                while (searchIndex < tuos.size && tuos.getOrNull(searchIndex)?.tuNotes?.getOrNull(1)?.toString() == "―") {
                    lastValidIndex = searchIndex
                    searchIndex++
                }

                val targetIndex = if (lastValidIndex > index) lastValidIndex else index

                when {
                    isDSFin -> {
                        newGridIndex = targetIndex
                        newMarkerList.add("${markerText.trim()}")
                    }
                    isFarany -> {
                        newGridIndex = if(hasDCDS) {
                            targetIndex
                        } else {
                            index
                        }
                        newMarkerList.add("Farany")
                    }
                    isDC -> {
                        newGridIndex = targetIndex
                        newMarkerList.add("${markerText.trim()}")
                    }
                    isDS -> {
                        newGridIndex = targetIndex
                        newMarkerList.add("${markerText.trim()}")
                    }
                    isRit.containsMatchIn(markerText) -> {
                        currentMarker = currentMarker.copy(lastCallerMarker = targetIndex)
                        newMarkerList.add("Ritenuto")
                    }
                    isRall.containsMatchIn(markerText) -> {
                        currentMarker = currentMarker.copy(lastCallerMarker = targetIndex)
                        newMarkerList.add("Rallentando")
                    }
                    else -> {
                        newMarkerList.add(markerText)
                    }
                }
            }
            currentMarker.copy(
                gridIndex = newGridIndex,
                marker = newMarkerList
            )
        }

        _midiMarkersList.value = finalizedList
        println("Markers finalisés et mis à jour : ${finalizedList.size} ${finalizedList.joinToString(", ")}")
    }

    private val _tuoEditState = MutableStateFlow<TUOEditState?>(null)
    val tuoEditState: StateFlow<TUOEditState?> = _tuoEditState.asStateFlow()

    fun openTUOEditor(editState: TUOEditState) {
        _tuoEditState.value = editState
        println("nMrk = [${editState.marker}] template [${editState.templateFragment}]")
    }

    fun closeTUOEditor() {
        _tuoEditState.value = null
    }

    fun updateTUOEdit(new: TUOEditState) {
        _tuoEditState.value = new
    }

    fun updateFullTUOList(newList: List<TimeUnitObject>) {
        _tuoList.value = newList
    }
    fun loadNewSong(newMidiFile: String) {
        _mediaPlayer?.stop()
        _mediaPlayer?.release()
        _mediaPlayer = null
        _isPos.value = true
        _isPlay.value = false
        _currentPos.value = 0f
        _dcDone.value = false
        _dsDone.value = false
        _sourceModeState.value = false
        try {
            val midiFileName = fileRepository.getFileName(newMidiFile)
            println("Opening xx129 $midiFileName")
            _mediaPlayer = FMediaPlayer(filename = midiFileName, sharedScreenModel = this, onFinished = {
//            _isPos.value = true
//            _isPlay.value = false
                _currentPos.value = 0f
                seekToGrid(0)
                _mediaPlayer?.stop()
                setDcDone(false)
                setDsDone(false)
                println("fin de lecture du Midi $newMidiFile")
                _mediaPlayer?.syncNavigationMonitor(this)
            })
//            synchro
            _mediaPlayer?.requestSync(this)
            //finalizeMarkers()
            _mediaPlayer?.syncNavigationMonitor(this)
        } catch(e: Exception) {
            println("Erreur d'ouverture de mediaPlayer : ${e.message} ")
        }
        println("New media Player crée $newMidiFile")
    }

    fun togglePlayPause() {
        _canUpdPositionFromPartition.value=true
        _mediaPlayer?.let { player ->
            if (_isPlay.value) {
                _isPlay.value = false
                _isPos.value = true
                player.pause()
            } else {
                _isPlay.value = true
                _isPos.value = false
                player.play()
                player.setVolume(_volumeLevel.value)
                if(currentPos.value == 0f) {
                    player.seekTo(0)
                }
            }
//            println("128: Status de isPlay ${_isPlay.value} \nisPos ${_isPos.value} \ncurrentPos ${_currentPos.value} \n volume ${_volumeLevel.value}")
        }
    }
    fun stopMidi() {
        _mediaPlayer?.let { player ->
            _isPlay.value = false
            _isPos.value = true
            player.pause()
        }
    }
    fun seekTo(pos: Float) {
        _currentPos.value = pos
        _mediaPlayer?.let { player ->
            player.seekTo(pos.toLong())
        }
    }

    fun setDragging(dragState: Boolean) {
        _isDragging.value = dragState
    }
    fun setVolume(level: Float) {
        _volumeLevel.value = level
        _mediaPlayer?.let { player ->
            player.setVolume(level) }
    }
    fun updateProgress(){
        _mediaPlayer?.let { player->
            if (_isPlay.value) {
                val p = player.getCurrentPosition().toFloat()
                val d = player.getDuration().toFloat()

                if (p >= 0) _currentPos.value = p
                if ((d > 0) && _duration.value != d) _duration.value = d
            }
        }
    }
    var currentNoteIndex by mutableStateOf(0f)

    fun updatePositionFromPartition(index: Int, totalRow: Int) {
        if(_canUpdPositionFromPartition.value) {     /*upd pos part only if midi on playing*/
            val duration = _duration.value
            if(totalRow > 0) {
                currentNoteIndex = index.toFloat()
                val newPos = (currentNoteIndex / totalRow.toFloat()) * duration
                seekTo(newPos)
                println("Shared:196 currentNoteIndex $currentNoteIndex, Index $index et curret = ${_currentPos.value}")
            }
        }
    }

    fun appendData(otherData: String) {
        _nextLabel.value += otherData
    }

    fun reset() {
        // Close other menus
        _showMidiCtrl.value = false
        _expandedFAB.value = false
        _showSearchMenu.value = false
        _sourceModeState.value = false
        _harmonyView.value = false
        setTranspositionInterval(0)
        setSilentDurBefr(0)
        _midiMarkersList.value = emptyList()
        _tuoTimestamps.value   = emptyList()
        _searchTitle.value = ""
        _isPendingHairPinOnEdit.value = HairPinEditState(false, 0)
        tempTimeUnitObjectList.clear()
        resetGridCount()
    }

    fun lastTUO(): TimeUnitObject? {
        return tempTimeUnitObjectList.lastOrNull()
    }

    fun addTUO(newTUO: TimeUnitObject) {
        tempTimeUnitObjectList.add(newTUO)
    }

    fun doneTUOList() {
        _tuoList.value = tempTimeUnitObjectList.toList()
    }

    fun setMeasure(theMeasure: String) {
        _measure.value = theMeasure
    }

    fun setSongTitle(theTitle: String) {
        _songTitle.value = theTitle
    }

    fun setStanza(theStanza: Int) {
        try {
            _stanza.value = theStanza
            _mediaPlayer?.syncNavigationMonitor(this)
        } catch (e: NumberFormatException) {
            _stanza.value = 0
        }
    }

    fun setSongKey(theSongKey: String) {
        _songKey.value = theSongKey
    }

    fun setTransposeTo(key: String) {
        _transposeTo.value = key
    }

    fun setTransposeAsIf(key: String) {
        _transposeAsIf.value = key
    }

    fun setSongAuthor(theSongAuthor: String) {
        _songAuthor.value = theSongAuthor
    }

    fun setSongComposer(theSongComposer: String) {
        _songComposer.value = theSongComposer
    }

    fun setSongRhythm(theSongRhythm: String) {
        _songRhythm.value = theSongRhythm
    }

    fun setNbStanzas(nbStanzas: Int) {
        _nbStanzas.value = nbStanzas
    }

    fun setPlaylist(thePlaylist: List<String>) {
        _playlist.value = thePlaylist
    }

    fun setHasMarker(theHasMarker: Boolean) {
        _hasMarker.value = theHasMarker

    }
    data class HairPinData(
        val symbol: Char,
        val startGrid: Int,
        val endGrid: Int,
    )

    fun getHairPins(): List<HairPinData> {
        val allMarkers = _midiMarkersList.value

        fun isStartToken(s: String): Boolean {
            val m = s.trim()
            return m == "<" ||
                    m == ">" ||
                    m.contains("cres", ignoreCase = true) ||
                    m.contains("dim", ignoreCase = true)
        }
        val starts = allMarkers
            .filter { markerObj ->
                markerObj.marker.any { isStartToken(it) }
            }
            .sortedBy { it.gridIndex }
        val ends = allMarkers
            .filter { markerObjt ->
                markerObjt.marker.any { it.trim().endsWith("=") }
            }
            .sortedBy { it.gridIndex }
            .toMutableList()

//        println("HairPins starts: ${starts.size} | ends: ${ends.size}")
        return starts.mapNotNull { start ->
            val startGrid = start.gridIndex ?: return@mapNotNull null
            val startString = start.marker.firstOrNull { isStartToken(it) } ?: return@mapNotNull null
            val symbol = when {
                startString.contains("<") || startString.contains("cres", ignoreCase = true) -> '<'
                startString.contains(">") || startString.contains("dim", ignoreCase = true) -> '>'
                else -> return@mapNotNull null
            }

            val matchingEnd = ends.firstOrNull { (it.gridIndex ?: 0) > startGrid }
            if (matchingEnd != null) ends.remove(matchingEnd)
            val endGrid = matchingEnd?.gridIndex ?: (startGrid + 4)
//            println("HairPin '$symbol' : $startGrid → $endGrid")
            HairPinData(symbol, startGrid, endGrid)
        }
    }

    fun playNext() {
        val nextIndex = (_nextPlayed.value + 1) % _playlist.value.size
        _nextPlayed.value = nextIndex
        setStanza(1)
    }

    fun currentPlayed(): String {
        val playlistIndex = _nextPlayed.value
        return _playlist.value[playlistIndex]
    }

    fun getVoiceInstrument(): Int {
        val defaultProgram = 0
        return prefs.getInt("voice_instrument", defaultProgram)
    }
    fun saveVoiceInstrument(program: Int) {
        prefs.putInt("voice_instrument", program)
        _mediaPlayer?.changeInstru(program)
    }
}