package net.taehui.twilight import com.fasterxml.jackson.core.JacksonException import com.ibm.icu.text.CharsetDetector import net.taehui.twilight.awilight.Component import net.taehui.twilight.awilight.DefaultCompute import net.taehui.twilight.note.BaseNote import net.taehui.twilight.system.Configure import java.io.ByteArrayInputStream import java.util.* import kotlin.math.max abstract class BaseCompiler(val noteFileContents: ByteArray, val format: String) : Logger { val notes = mutableListOf<BaseNote>() val waitBPMMap = TreeMap<Double, Double>() val waitStopMap = TreeMap<Double, Double>() val bpmValues = TreeMap<Double, Double>() val positionStandNoteCountMap = TreeMap<Double, Int>() val waitMultiplierMap = TreeMap<Double, Double>(Comparator.reverseOrder()) val waitValues = TreeMap<Double, Double>() lateinit var valueComponent: Component var highestPosition = 0.0 private fun getWaitValue(waitPosition: Double): Double { return waitValues[waitPosition]!! } fun onCompiled(targetComputing: Computing) { if (bpmValues.isNotEmpty()) { val bpmWaitMap = mutableMapOf<Double, Double>() var lastBPM = targetComputing.levyingBPM var lastBPMPosition = 0.0 bpmValues.forEach { if (it.key <= highestPosition) { bpmWaitMap[lastBPM] = bpmWaitMap.getOrDefault( lastBPM, 0.0 ) + waitValues[it.key]!! - waitValues[lastBPMPosition]!! lastBPMPosition = it.key lastBPM = it.value } } bpmWaitMap[lastBPM] = bpmWaitMap.getOrDefault( lastBPM, 0.0 ) + waitValues[highestPosition]!! - waitValues[lastBPMPosition]!! val longestWait = bpmWaitMap.values.max() val longestBPMs = bpmWaitMap.entries .filter { it.value == longestWait } .map { it.key } if (longestBPMs.isNotEmpty()) { val validBPMs = longestBPMs.filter { it > 0.5 && it < 65536 } if (validBPMs.isNotEmpty()) { targetComputing.bpm = validBPMs.min() } else { val validBPMWaitValues: List<Map.Entry<Double, Double>> = bpmWaitMap.entries.filter { it.key > 0.5 && it.key < 65536 } if (validBPMWaitValues.isNotEmpty()) { val validLongestWait = validBPMWaitValues.maxOfOrNull { it.value } targetComputing.bpm = validBPMWaitValues.minBy { it.value == validLongestWait }.key } else { val bpm = longestBPMs.max() if (bpm % 100001 == 0.0) { targetComputing.bpm = bpm / 100001 } else { targetComputing.bpm = bpm } } } } } else { if (targetComputing.levyingBPM % 100001 == 0.0) { targetComputing.bpm = targetComputing.levyingBPM / 100001 } else { targetComputing.bpm = targetComputing.levyingBPM } } targetComputing.length = getWaitValue(highestPosition) if (positionStandNoteCountMap.isNotEmpty()) { val positionStandNoteCounts = mutableListOf<Pair<Double, Int>>() positionStandNoteCounts.addAll(positionStandNoteCountMap.toList()) var lowestPosition = 0 var lowestWait = getWaitValue(positionStandNoteCounts[lowestPosition].first) var lowestCount = positionStandNoteCounts[lowestPosition].second var highestInputCount = lowestCount var i = 1 while (i < positionStandNoteCounts.size) { val (waitPosition, value) = positionStandNoteCounts[i] if (getWaitValue(waitPosition) - lowestWait < 1000.0) { highestInputCount += value ++i } else { targetComputing.highestInputCount = max(highestInputCount, targetComputing.highestInputCount) highestInputCount -= lowestCount ++lowestPosition lowestWait = getWaitValue(positionStandNoteCounts[lowestPosition].first) lowestCount = positionStandNoteCounts[lowestPosition].second } } targetComputing.highestInputCount = max(highestInputCount, targetComputing.highestInputCount) } } fun onCompiled(defaultComputer: DefaultCompute) { defaultComputer.totalNotes = notes.stream() .map { if (it.hasStand()) if (it.longWait > 0.0) 2 else 1 else 0 } .reduce(0) { o1, t2 -> o1 + t2 } defaultComputer.waitBPMMap.putAll(waitBPMMap) waitStopMap.forEach { (wait, stop) -> val lastBPMs = defaultComputer.waitBPMMap.filter { it.key <= wait }.toList() val lastBPM = if (lastBPMs.isNotEmpty()) lastBPMs.last().second else defaultComputer.levyingBPM valueComponent.setBPM(lastBPM) defaultComputer.waitBPMMap[wait] = 0.0 defaultComputer.waitBPMMap[wait + valueComponent.millisMeter * stop] = lastBPM } var lastWait = Double.MAX_VALUE waitMultiplierMap.forEach { (wait, multiplier) -> val lastBPMs = defaultComputer.waitBPMMap.filter { it.key <= wait }.toList() defaultComputer.waitBPMMap[wait] = if (lastBPMs.isNotEmpty()) lastBPMs.last().second else defaultComputer.levyingBPM defaultComputer.waitBPMMap.keys.filter { wait <= it && it < lastWait }.forEach { defaultComputer.waitBPMMap[it] = defaultComputer.waitBPMMap[it]!! * multiplier } lastWait = wait } defaultComputer.length = maxOf((notes.filter { it.hasStand() }.maxOfOrNull { it.wait + it.longWait } ?: 0.0), defaultComputer.waitAudioNoteMap.stream().max(Comparator.naturalOrder()).orElse(0.0), defaultComputer.waitMediaNoteMap.stream().max(Comparator.naturalOrder()).orElse(0.0) ) notes.sort() notes.forEach { defaultComputer.notes.add(it) it.noteID = defaultComputer.notes.size - 1 } } abstract fun handleCompile(targetComputing: Computing) abstract fun handleCompile(defaultComputer: DefaultCompute) companion object { fun handleCompile(noteFileContents: ByteArray): Computing { val targetComputing = Computing() ByteArrayInputStream(noteFileContents).use { val formatComputer = CharsetDetector() formatComputer.setText(it) val formats = formatComputer.detectAll() val format = if (formats[0].confidence >= 87.5) formats[0].name else Configure.db.format var targetCompiler: BaseCompiler try { targetCompiler = BMSONCompiler(noteFileContents, format) targetCompiler.handleCompile(targetComputing) } catch (e: JacksonException) { targetCompiler = BMSCompiler(noteFileContents, format) targetCompiler.handleCompile(targetComputing) } targetCompiler.onCompiled(targetComputing) } return targetComputing } fun handleCompile(defaultComputer: DefaultCompute, noteFileContents: ByteArray) { ByteArrayInputStream(noteFileContents).use { val formatComputer = CharsetDetector() formatComputer.setText(it) val formats = formatComputer.detectAll() val format = if (formats[0].confidence > 50) formats[0].name else Configure.db.format var targetCompiler: BaseCompiler try { targetCompiler = BMSONCompiler(noteFileContents, format) targetCompiler.handleCompile(defaultComputer) } catch (e: Throwable) { targetCompiler = BMSCompiler(noteFileContents, format) targetCompiler.handleCompile(defaultComputer) } targetCompiler.onCompiled(defaultComputer as Computing) targetCompiler.onCompiled(defaultComputer) } } } }