package com.nisemoe.nise.service

import com.aayushatharva.brotli4j.encoder.Encoder
import com.aayushatharva.brotli4j.decoder.Decoder
import com.aayushatharva.brotli4j.Brotli4jLoader
import java.nio.ByteBuffer
import java.nio.ByteOrder

data class ReplayStruct(val version: Int, val replayData: ByteArray)

class CompressReplay {

    companion object {

        init {
            Brotli4jLoader.ensureAvailability()
        }

        private const val CURRENT_VERSION = 2

        private val brotliParameters: Encoder.Parameters = Encoder.Parameters()
            .setQuality(11)

        fun compressReplay(replay: String): ByteArray {
            return compressReplay(replay.toByteArray())
        }

        fun compressReplay(replay: ByteArray): ByteArray {
            val existingStruct = readReplayStruct(replay)
            return if (existingStruct != null && existingStruct.version <= CURRENT_VERSION) {
                replay
            } else {
                val compressedData = Encoder.compress(replay, brotliParameters)
                val newStruct = ReplayStruct(CURRENT_VERSION, compressedData)
                serializeReplayStruct(newStruct)
            }
        }

        fun decompressReplayToString(replay: ByteArray): String {
            return String(decompressReplay(replay), Charsets.UTF_8).trimEnd(',')
        }

        fun decompressReplay(replay: ByteArray): ByteArray {
            val replayStruct = readReplayStruct(replay)
            return if (replayStruct != null) {
                val decompressedResult = Decoder.decompress(replayStruct.replayData)
                if (decompressedResult != null && decompressedResult.decompressedData != null) {
                    decompressedResult.decompressedData
                } else {
                    replayStruct.replayData
                }
            } else {
                replay
            }
        }

        private const val MAGIC_NUMBER = 0x12345678 // a unique signature to identify our format

        private fun readReplayStruct(data: ByteArray): ReplayStruct? {
            if (data.size > 8) { // 4 bytes for the magic number + 4 bytes for the version
                val buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN)
                val magic = buffer.int
                if (magic == MAGIC_NUMBER) {
                    val version = buffer.int
                    val replayData = ByteArray(data.size - 8)
                    buffer.get(replayData)
                    return ReplayStruct(version, replayData)
                }
            }
            return null
        }

        private fun serializeReplayStruct(struct: ReplayStruct): ByteArray {
            val buffer = ByteBuffer.allocate(8 + struct.replayData.size).order(ByteOrder.BIG_ENDIAN)
            buffer.putInt(MAGIC_NUMBER)
            buffer.putInt(struct.version)
            buffer.put(struct.replayData)
            val byteArray = buffer.array()
            return byteArray
        }

    }

}
