package llc.arma.ble.domain.model

import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.encoding.encodeStructure

@Serializable
data class BleFilter(
    val sortField: Field = Field.Name,
    val sortOrder: Order = Order.Asc,
    val name: String = "",
    val mac: String = "",
    @Serializable(with = CFPRSerializer::class) val battery: ClosedFloatingPointRange<Float> = (0f)..(100f),
    @Serializable(with = CFPRSerializer::class) val rssi: ClosedFloatingPointRange<Float> = (-100f)..(-10f),
    val bleType: BleInfo.Type? = null
){

    enum class Field {
        Name, Mac, Distance, Dbm, Battery
    }

    enum class Order {
        Asc, Desc
    }

}

object CFPRSerializer : KSerializer<ClosedFloatingPointRange<Float>> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("kotlin.ranges.DoubleClosedFloatingPointRange") {
        element<Float>("start")
        element<Float>("endInclusive")
    }

    override fun serialize(encoder: Encoder, value: ClosedFloatingPointRange<Float>) {
        encoder.encodeStructure(descriptor) {
            encodeFloatElement(descriptor, 0, value.start)
            encodeFloatElement(descriptor, 1, value.endInclusive)
        }
    }

    override fun deserialize(decoder: Decoder): ClosedFloatingPointRange<Float> {
        return decoder.decodeStructure(descriptor) {
            var start: Float? = null
            var end: Float? = null
            while (true) {
                val index = decodeElementIndex(descriptor)
                if (index == CompositeDecoder.DECODE_DONE) break
                if (index == 0) start = decodeFloatElement(descriptor, index)
                else end = decodeFloatElement(descriptor, index)
            }
            if (start == null || end == null) throw SerializationException("...")
            start..end
        }
    }
}