package net.minecraft.util;

import java.util.function.Supplier;
import java.util.function.Consumer;
import com.mojang.serialization.MapLike;
import com.mojang.serialization.RecordBuilder;
import java.util.stream.Stream;
import com.mojang.serialization.DynamicOps;
import net.minecraft.resources.ResourceLocation;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import java.util.function.Function;

public class Codecs {
    private static Function<Integer, DataResult<Integer>> checkRange(final int integer1, final int integer2) {
        return (Function<Integer, DataResult<Integer>>)(integer -> {
            if (integer >= integer1 && integer <= integer2) {
                return DataResult.success(integer);
            }
            else {
                return DataResult.error("Value " + integer + " outside of range [" + integer1 + ":" + integer2 + "]", integer);
            }
        });
    }
    
    public static Codec<Integer> intRange(final int integer1, final int integer2) {
        final Function<Integer, DataResult<Integer>> function3 = checkRange(integer1, integer2);
        return (Codec<Integer>)Codec.INT.flatXmap((Function)function3, (Function)function3);
    }
    
    private static Function<Double, DataResult<Double>> checkRange(final double double1, final double double2) {
        return (Function<Double, DataResult<Double>>)(double3 -> {
            if (double3 >= double1 && double3 <= double2) {
                return DataResult.success(double3);
            }
            else {
                return DataResult.error("Value " + double3 + " outside of range [" + double1 + ":" + double2 + "]", double3);
            }
        });
    }
    
    public static Codec<Double> doubleRange(final double double1, final double double2) {
        final Function<Double, DataResult<Double>> function5 = checkRange(double1, double2);
        return (Codec<Double>)Codec.DOUBLE.flatXmap((Function)function5, (Function)function5);
    }
    
    public static <T> MapCodec<Pair<ResourceKey<T>, T>> withName(final ResourceKey<Registry<T>> ug, final MapCodec<T> mapCodec) {
        return (MapCodec<Pair<ResourceKey<T>, T>>)Codec.mapPair(ResourceLocation.CODEC.xmap((Function)ResourceKey.<T>elementKey(ug), (Function)ResourceKey::location).fieldOf("name"), (MapCodec)mapCodec);
    }
    
    private static <A> MapCodec<A> mapResult(final MapCodec<A> mapCodec, final ResultFunction<A> a) {
        return new MapCodec<A>() {
            public <T> Stream<T> keys(final DynamicOps<T> dynamicOps) {
                return (Stream<T>)mapCodec.keys((DynamicOps)dynamicOps);
            }
            
            public <T> RecordBuilder<T> encode(final A object, final DynamicOps<T> dynamicOps, final RecordBuilder<T> recordBuilder) {
                return a.<T>coApply(dynamicOps, object, mapCodec.encode(object, (DynamicOps)dynamicOps, (RecordBuilder)recordBuilder));
            }
            
            public <T> DataResult<A> decode(final DynamicOps<T> dynamicOps, final MapLike<T> mapLike) {
                return a.<T>apply(dynamicOps, mapLike, mapCodec.decode((DynamicOps)dynamicOps, (MapLike)mapLike));
            }
            
            public String toString() {
                return mapCodec + "[mapResult " + a + "]";
            }
        };
    }
    
    public static <A> MapCodec<A> withDefault(final MapCodec<A> mapCodec, final Consumer<String> consumer, final Supplier<? extends A> supplier) {
        return Codecs.<A>mapResult(mapCodec, new ResultFunction<A>() {
            @Override
            public <T> DataResult<A> apply(final DynamicOps<T> dynamicOps, final MapLike<T> mapLike, final DataResult<A> dataResult) {
                return (DataResult<A>)DataResult.success(dataResult.resultOrPartial(consumer).orElseGet(supplier));
            }
            
            @Override
            public <T> RecordBuilder<T> coApply(final DynamicOps<T> dynamicOps, final A object, final RecordBuilder<T> recordBuilder) {
                return recordBuilder;
            }
            
            @Override
            public String toString() {
                return "WithDefault[" + supplier.get() + "]";
            }
        });
    }
    
    public static <A> MapCodec<A> setPartial(final MapCodec<A> mapCodec, final Supplier<A> supplier) {
        return Codecs.<A>mapResult(mapCodec, new ResultFunction<A>() {
            @Override
            public <T> DataResult<A> apply(final DynamicOps<T> dynamicOps, final MapLike<T> mapLike, final DataResult<A> dataResult) {
                return (DataResult<A>)dataResult.setPartial(supplier);
            }
            
            @Override
            public <T> RecordBuilder<T> coApply(final DynamicOps<T> dynamicOps, final A object, final RecordBuilder<T> recordBuilder) {
                return recordBuilder;
            }
            
            @Override
            public String toString() {
                return "SetPartial[" + supplier + "]";
            }
        });
    }
    
    interface ResultFunction<A> {
         <T> DataResult<A> apply(final DynamicOps<T> dynamicOps, final MapLike<T> mapLike, final DataResult<A> dataResult);
        
         <T> RecordBuilder<T> coApply(final DynamicOps<T> dynamicOps, final A object, final RecordBuilder<T> recordBuilder);
    }
}
