package io.papermc.paper.configuration.type;

import io.papermc.paper.configuration.type.number.IntOr;
import java.lang.reflect.Type;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.objectmapping.meta.Required;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;

public record DespawnRange(
    @Required IntOr.Default horizontalLimit,
    @Required IntOr.Default verticalLimit,
    @Required boolean wasDefinedViaLongSyntax
) {

    public DespawnRange(final IntOr.Default generalLimit) {
        this(generalLimit, generalLimit, false);
    }

    public static final TypeSerializer<DespawnRange> SERIALIZER = new Serializer();

    static final class Serializer implements TypeSerializer<DespawnRange> {

        public static final String HORIZONTAL = "horizontal";
        public static final String VERTICAL = "vertical";

        @Override
        public DespawnRange deserialize(final Type type, final ConfigurationNode node) throws SerializationException {
            if (node.hasChild(HORIZONTAL) && node.hasChild(VERTICAL)) {
                return new DespawnRange(
                    node.node(HORIZONTAL).require(IntOr.Default.class),
                    node.node(VERTICAL).require(IntOr.Default.class),
                    true
                );
            } else if (node.hasChild(HORIZONTAL) || node.hasChild(VERTICAL)) {
                throw new SerializationException(node, DespawnRange.class, "Expected both horizontal and vertical despawn ranges to be defined");
            } else {
                return new DespawnRange(node.require(IntOr.Default.class));
            }
        }

        @Override
        public void serialize(final Type type, final @Nullable DespawnRange despawnRange, final ConfigurationNode node) throws SerializationException {
            if (despawnRange == null) {
                node.raw(null);
                return;
            }

            if (despawnRange.wasDefinedViaLongSyntax()) {
                node.node(HORIZONTAL).set(despawnRange.horizontalLimit());
                node.node(VERTICAL).set(despawnRange.verticalLimit());
            } else {
                node.set(despawnRange.verticalLimit());
            }
        }
    }
}
