package mg.dot.feufaro.midi

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mg.dot.feufaro.FileRepository
import javax.sound.midi.*

actual class MidiWriterKotlin actual constructor(private val fileRepository: FileRepository) {
    private val sequence = Sequence(Sequence.PPQ, 60)
    private val track    = sequence.createTrack()
    private var tick: Long = 0
    private var nextTick: MutableList<MidiPitch> = mutableListOf()
    private val noteOn   = ShortMessage()
    private val noteOff = ShortMessage()
    private val lastPitch : MutableList<Int> = mutableListOf()
    private val useChord : Boolean = true
    actual fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long) {
        val channel = (voiceNumber -1).coerceIn(0, 3)
        var finalNote = note

        if (voiceNumber == 3 || voiceNumber == 4) {
            finalNote -= 12
        }
        if (lastPitch.size > voiceNumber && lastPitch[voiceNumber] > 0) {
            val offMsg = ShortMessage()
            offMsg.setMessage(ShortMessage.NOTE_OFF, channel, lastPitch[voiceNumber], 0)
            track.add(MidiEvent(offMsg, tick))
        }
        var finalVelocity = velocity
        var midiNote = finalNote
        if (finalNote <= 0) {
            midiNote = 40
            finalVelocity = 0
        }
        val onMsg = ShortMessage()
        onMsg.setMessage(ShortMessage.NOTE_ON, channel, midiNote, finalVelocity)
        track.add(MidiEvent(onMsg, tick))

        while(lastPitch.size <= voiceNumber) {
            lastPitch.add(0)
        }
        lastPitch[voiceNumber] = midiNote
    }
    actual fun save(filePath: String) {
        val parseScope = CoroutineScope(Dispatchers.Default)
        parseScope.launch {
            val midiFileName = fileRepository.getFileName(filePath)
            val out = fileRepository.getOutputStream(midiFileName)
            println("write to : $midiFileName   ")
            MidiSystem.write(sequence, 1, out)
            out.close()
        }
    }
    actual fun addMetaMessage(type: Int, tick: Int, nbData: Int, metaByteString: String) {
        val byteArray = metaByteString.toByteArray()
        val metaMessage = MetaMessage(type, byteArray, nbData)
        track.add(MidiEvent(metaMessage, tick.toLong()))
    }
    actual fun process(pitches: List<MidiPitch>) {
        val lastTick = 0
        nextTick.clear()
        // addMetaMessage(0x59, 4, 2, 2,0)
        tick = 0
        pitches.forEach {
            if (it.metaType > 0) {
                addMetaMessage(it.metaType, it.tick, it.metaByteSize, it.metaBytes)
            } else if (it.pitch != "") {
                addNote(it.voiceNumber, it.pitch.toInt(), 100, it.tick.toLong())
            }
        }
    }
}