package ru.dbotthepony.mc.otm.data

import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import net.minecraft.nbt.NbtOps
import ru.dbotthepony.mc.otm.core.math.Decimal
import java.nio.ByteBuffer
import java.util.stream.Collector
import java.util.stream.Stream

object DecimalCodec : Codec<Decimal> {
	override fun <T : Any> encode(input: Decimal, ops: DynamicOps<T>, prefix: T): DataResult<T> {
		if (ops === NbtOps.INSTANCE) {
			return DataResult.success((ops as DynamicOps<T>).createByteList(ByteBuffer.wrap(input.toByteArray())))
		}

		return DataResult.success(ops.createString(input.toString()))
	}

	override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Decimal, T>> {
		return ops.getStringValue(input).flatMap {
			try {
				DataResult.success(Pair(Decimal(it), ops.empty()))
			} catch (err: NumberFormatException) {
				DataResult.error { "Not a valid number for converting into Decimal: $it" }
			}
		}.get().map(
			{
				DataResult.success(it)
			},
			{ e0 ->
				ops.getIntStream(input).flatMap {
					try {
						DataResult.success(Pair(Decimal.fromByteArray(
							it
								.collect(::ByteArrayList, { v, a -> v.add(a.toByte()) }, ByteArrayList::addAll)
								.toByteArray()
						), ops.empty()))
					} catch (err: NumberFormatException) {
						DataResult.error { "Failed to convert array of bytes into Decimal: $it" }
					}
				}.get().map(
					{
						DataResult.success(it)
					},
					{ e1 ->
						ops.getNumberValue(input).flatMap {
							DataResult.success(Pair(Decimal(it.toString()), ops.empty()))
						}.get().map(
							{
								DataResult.success(it)
							},
							{ e2 ->
								DataResult.error { "None of attempts at decoding Decimal were successful: ${e0.message()}; ${e1.message()}; ${e2.message()}" }
							}
						)
					}
				)
			}
		)
	}
}
